Playframework: How can I disable session / cookies on specific action? - java

For a specific controller action I want to turn off cookies. I've tried to remove the cookie Map, but that doesn't seem to work. I need to completely remove all response headers except my own.
Any ideas?

I found this solution when Googling this problem. It does the same thinng you try, removing cookies map, but it is done in an method which is annotated with #Finally.
I believe cookies map is filled after render() but before any #Finally annotated class.
Credits to Alex Jarvis on Google Groups, code is copied for reference:
/**
* Removes cookies from all responses.
*
* This is because cookies are not required in stateless webservice and
* we don't want to send any unnecessary information to the client.
*
* #author Alex Jarvis
*/
public class NoCookieFilter extends Controller {
/** An empty cookie map to replace any cookies in the response. */
private static final Map<String, Http.Cookie> cookies = new HashMap<String, Http.Cookie>(0);
/**
* When the configuration property 'cookies.enabled' equals false,
* this Finally filter will replace the cookies in the response with an empty Map.
*/
#Finally
protected static void removeCookies() {
boolean cookiesEnabled = Boolean.parseBoolean(Play.configuration.getProperty("cookies.enabled"));
if (!cookiesEnabled) {
response.cookies = cookies;
}
}
}
Usage: For any controller you want to be "cookieless" annotate it with
#With(NoCookieFilter.class)
(Tested in Play 1.2.5)

I managed to wrap the response with a wrapper with response.current.set(new CookieLessResponseWrapper(response.current())). Works ok for me.
Here is the code for the Response wrapper if anyone is interested.
package helpers;
import play.mvc.Http.Response;
public class CookieLessResponseWrapper extends Response {
private Response wrappedResponse;
public CookieLessResponseWrapper(Response response) {
this.wrappedResponse = response;
}
#Override
public void accessControl(String allowOrigin, boolean allowCredentials) {
wrappedResponse.accessControl(allowOrigin, allowCredentials);
}
#Override
public void accessControl(String allowOrigin, String allowMethods,
boolean allowCredentials) {
wrappedResponse.accessControl(allowOrigin, allowMethods, allowCredentials);
}
#Override
public void accessControl(String allowOrigin) {
wrappedResponse.accessControl(allowOrigin);
}
#Override
public void cacheFor(String etag, String duration, long lastModified) {
wrappedResponse.cacheFor(etag, duration, lastModified);
}
#Override
public void cacheFor(String duration) {
wrappedResponse.cacheFor(duration);
}
#Override
public String getHeader(String name) {
return wrappedResponse.getHeader(name);
}
#Override
public void print(Object o) {
wrappedResponse.print(o);
}
#Override
public void removeCookie(String name) {
wrappedResponse.removeCookie(name);
}
#Override
public void reset() {
wrappedResponse.reset();
}
#Override
public void setContentTypeIfNotSet(String contentType) {
wrappedResponse.setContentTypeIfNotSet(contentType);
}
#Override
public void setCookie(String name, String value, String domain,
String path, Integer maxAge, boolean secure, boolean httpOnly) {
}
#Override
public void setCookie(String name, String value, String domain,
String path, Integer maxAge, boolean secure) {
}
#Override
public void setCookie(String name, String value, String duration) {
}
#Override
public void setCookie(String name, String value) {
}
#Override
public void setHeader(String name, String value) {
wrappedResponse.setHeader(name, value);
}
}

You should be able to clear all headers, in your action, with:
Http.Response.current().reset();

Have in mind that the Session is a Cookie. I don't believe that you can remove it.

How about use discarding the whole session for each request as a workaround?
Discarding the whole session
Ok("Bye").withNewSession
https://www.playframework.com/documentation/2.3.x/ScalaSessionFlash#Discarding-the-whole-session

Related

Expected BEGIN_ARRAY but was BEGIN_OBJECT in Retrofit

I am new to android programming and can anyone help me or point out why its giving me this error
I want to fetch some data from the server such as under the Hardware json and get the names and status, but when i call api its shows me this.
Change the line
public void onResponse(Call<List<ObjectList>> call, Response<List<ObjectList>> response) {
to
public void onResponse(Call<List<ObjectList>> call, Response<ObjectList> response) {
As per your code, you are expecting response as List. But Actual response is object. So, you need to generate model class based on your response and set in code for output.
Your Model should be like :
public class Application {
ArrayList<Object> hardware = new ArrayList<Object>();
Header HeaderObject;
ArrayList<Object> software = new ArrayList<Object>();
// Getter Methods
public Header getHeader() {
return HeaderObject;
}
// Setter Methods
public void setHeader( Header headerObject ) {
this.HeaderObject = headerObject;
}
}
public class Header {
Stamp StampObject;
private String frame_id;
private float seq;
// Getter Methods
public Stamp getStamp() {
return StampObject;
}
public String getFrame_id() {
return frame_id;
}
public float getSeq() {
return seq;
}
// Setter Methods
public void setStamp( Stamp stampObject ) {
this.StampObject = stampObject;
}
public void setFrame_id( String frame_id ) {
this.frame_id = frame_id;
}
public void setSeq( float seq ) {
this.seq = seq;
}
}
public class Stamp {
private float secs;
private float nsecs;
// Getter Methods
public float getSecs() {
return secs;
}
public float getNsecs() {
return nsecs;
}
// Setter Methods
public void setSecs( float secs ) {
this.secs = secs;
}
public void setNsecs( float nsecs ) {
this.nsecs = nsecs;
}
}
Then change below line :
public void onResponse(Call<List<ObjectList>> call, Response<Application> response) {
Change this:
#GET("system_monitor")
Call<List<ObjectList>> getHardware();
to
#GET("system_monitor")
Call<ObjectList> getHardware();
Your response return an object instead of array.
Instead of
#GET("system_monitor")
Call<List<ObjectList>> getHardware();
use
#GET("system_monitor")
Call<ObjectList> getHardware();
And then use it like below:
Call<ObjectList> call = webRequestAPI.getHardware();
call.enqueue(new Callback<ObjectList>() {
#Override
public void onResponse(Call<ObjectList> call, Response<ObjectList> response) {
if (!response.isSuccessful()) {
textViewHardwareName.setText("Code: " + response.code());
return;
}
ObjectList system_monitor = response.body();
...
}
#Override
public void onFailure(Call<ObjectList> call, Throwable t) {
textViewHardwareName.setText(t.getMessage());
}
});
The best thing for your scenario hardware and Software are as objects , which have two property
1.Name 2. Object status.
So I recommend you to create a class name as System and put there these two variables so finally your class looks like :
Class System
{
String object_name;
boolean object_status;
}
and your getter setter .
And update your model class like this
#SerializedName("hardware")
#Expose
public List<System> hardware;
#SerializedName("software")
#Expose
public List<System> software;
and change your retrofit response holder as.
public void onResponse(Call<List<ObjectList>> call, Response<ObjectList>
response) {

Parallel url get calls using Java spring RestTemplate

I have a list of urls and I want to hit them all in parallel and combine the result into a final Java object using Java spring RestTemplate. I'm able to achieve it buy accessing the urls in sequence, but due to performance concerns, I want to achieve them same in parallel. Looking forward to hearing your suggestions
You can use threads to perform parallel jobs.
First, make a result data class to handle the responses of your URLs
public class URLResult {
public String url;
public String response;
public Date responseTime;
// Add fields whatever you need
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
public Date getResponseTime() {
return responseTime;
}
public void setResponseTime(Date responseTime) {
this.responseTime = responseTime;
}
}
Then use it in your threads :
public List<URLResult> list = new ArrayList<>();
public synchronized void addToList(URLResult result) {
list.add(result);
}
public void hitUrl(String url) {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
URLResult result = new URLResult();
//here, connect to your url, get the result then set your URLResult fields
addToList(result);
}
});
thread.start();
}
At the end of the process, you will have a "list" of your results.

GWTP Strange Error Messages (That are inaccurate)

So I really need some helpful hints with an issue I am having with my GWTP application, perhaps one of you have ran into this problem before. So I am using GWTP 1.1 with:
com.gwtplatform.mvp.Mvp (Not w/ entry point)
com.gwtplatform.dispatch.Dispatch with
com.google.gwt.uibinder.UiBinder
The issue I am having is that when ever I have a client sided error (can be almost anywhere in my client side, not sure where the boundary is) I receive a very cryptic message to do with GIN rather than a message that will help me resolve the issue. Seems like something to do with GWTP Proxying, here is the typical message that I get: http://pastebin.com/YgxPbkru the real issue isn't what the error message is presenting to me. The OpenIcidentPresenter extends IncidentPresenter which is a Presenter I have made to manage lifecycles of an Incident (in this case) this extends another presenter that I have made called a RequestPresenter, this is a Presenter that allows the user to Request PresenterWidget classes into it (into any given slot for that presenters lifecycle), this extends another presenter called RichPresenter, which just has things that almost all my Presenters require like page load indication and certain page locking etc, etc. Here are these classes:
OpenIncidentPresenter
public class OpenIncidentPresenter extends IncidentPresenter<OpenIncidentPresenter.MyView,
OpenIncidentPresenter.MyProxy> implements ViewUiHandlers, HasRequestedWidgets, NewIncidentHandler,
ChangeSectionHandler, ConfigureHandler {
public interface MyView extends View, HasUiHandlers<ViewUiHandlers> {
... Snip ...
}
#ProxyCodeSplit
#NameToken(NameTokens.open)
//#UseGatekeeper(LoginGatekeeper.class)
public interface MyProxy extends ProxyPlace<OpenIncidentPresenter> {
}
private static Logger logger = Logger.getLogger(OpenIncidentPresenter.class.getName());
Process process;
SectionTuple currentSection = new SectionTuple();
Map<Integer, SectionTuple> sections = new HashMap<Integer, SectionTuple>();
List<AccordionSection> sectionWigets = new ArrayList<AccordionSection>();
List<Activity> cachedActivities = new ArrayList<Activity>();
List<Authority> cachedAuthorities = new ArrayList<Authority>();
List<Severity> cachedSeverities = new ArrayList<Severity>();
List<Location> cachedLocations = new ArrayList<Location>();
List<Site> cachedSites = new ArrayList<Site>();
List<Area> cachedAreas = new ArrayList<Area>();
Severity currentSeverity;
boolean configured = false;
boolean changedConsequences = false;
final ImageResources imageResources;
RegisteredRequestWidget<ActionBarPresenterWidget> actionBarReg;
RegisteredRequestWidget<ProgressBarPresenterWidget> progressBarReg;
#Inject
public OpenIncidentPresenter(final EventBus eventBus, final MyView view,
final MyProxy proxy, final DispatchAsync dispatch,
final PlaceManager placeManager, ImageResources imageResources) {
super(eventBus, view, proxy, dispatch, placeManager);
this.imageResources = imageResources;
getView().setUiHandlers(this);
logger.log(Level.INFO, "Constructed OpenIncidentPresenter");
}
#Override
protected void revealInParent() {
RevealContentEvent.fire(this, ApplicationPresenter.SLOT_MIDDLE, this);
}
... Snip ...
}
IncidentPresenter
public abstract class IncidentPresenter<T extends View,
H extends Proxy<?>> extends RequestPresenter<T, H> implements HasIncident {
Logger logger = Logger.getLogger(IncidentPresenter.class.getName());
public static final String INCIDENT_COOKIE = "incidentId";
public interface LoadCallback {
void onFinished();
}
protected Incident incident;
protected DispatchAsync dispatch;
private boolean loaded = false;
public IncidentPresenter(EventBus eventBus, T view, H proxy,
DispatchAsync dispatch, PlaceManager placeManager) {
super(eventBus, view, proxy, placeManager, true);
this.dispatch = dispatch;
}
public abstract void onFailureToLoadIncident(Integer incidentId);
public abstract void onLoadedIncident(Incident incident);
#Override
public void loadIncident(final Integer id) {
loadIncident(id, null);
}
#Override
public void loadIncident(final Integer id, final LoadCallback callback) {
.. Snip ..
}
/**
* Process the incident dependencies
*/
protected void loadDependencies(final Incident incident, final LoadCallback callback) {
.. Snip ..
}
#Override
public Incident getIncident() {
return incident;
}
#Override
public void setIncident(Incident incident) {
this.incident = incident;
if(hasIncident()) {
String incidentCookie = Cookies.getCookie("incidentId");
if(incidentCookie == null || !incidentCookie.equals(String.valueOf(
incident.getId()))) {
// Set the cookie to the new incident id
Cookies.setCookie("incidentId", String.valueOf(incident.getId()));
logger.log(Level.INFO, "Set incident " + String.valueOf(incident.getId())
+ " to the cookie session");
}
} else {
Cookies.removeCookie("incidentId");
logger.log(Level.INFO, "Set incident to null incident object, cleared " +
"the cookie session");
}
}
#Override
public boolean isIncidentLoaded() {
return hasIncident() && loaded;
}
#Override
public boolean hasIncident() {
return IncidentUtils.isValid(incident);
}
#Override
public void resetIncident() {
setIncident(null);
loaded = false;
}
#Override
public DispatchAsync getDispatch() {
return dispatch;
}
public String getIncidentCookie() {
return Cookies.getCookie(INCIDENT_COOKIE);
}
}
RequestPresenter
public abstract class RequestPresenter<T extends View, H extends Proxy<?>>
extends RichPresenter<T, H> implements HasRequestedWidgets {
RequestWidgetManager requestManager = new RequestWidgetManager(this);
public RequestPresenter(EventBus eventBus, T view, H proxy,
PlaceManager placeManager) {
this(eventBus, view, proxy, placeManager, false);
}
public RequestPresenter(EventBus eventBus, T view, H proxy,
PlaceManager placeManager, boolean leaveConfirmation) {
super(eventBus, view, proxy, placeManager, leaveConfirmation);
}
#Override
public void prepareFromRequest(PlaceRequest request) {
prepareFromRequest(request, null);
}
/**
* Alternative to {#link RequestPresenter#prepareFromRequest(PlaceRequest)} that
* will allow you to register the {#link FinalCallback} in case you have dependency
* on the request widgets.
* #param request
* #param callback
*/
public void prepareFromRequest(PlaceRequest request, FinalCallback callback) {
super.prepareFromRequest(request);
executeAfterRequesting(callback);
requestWidgets();
}
#Override
protected void onBind() {
super.onBind();
registerRequestWidgets();
}
#Override
protected void onUnbind() {
super.onUnbind();
unregisterRequestWidgets();
}
#Override
protected void onHide() {
super.onHide();
dismissWidgets();
}
#Override
protected void onReveal() {
super.onReveal();
requestWidgets();
}
private void requestWidgets() {
requestManager.requestAll();
onRequestWidgets();
}
private void dismissWidgets() {
requestManager.dismissAll();
onDismissWidgets();
}
public void unregisterRequestWidgets() {
requestManager.unregisterAllWidgets();
}
#Override
public abstract void registerRequestWidgets();
public void onRequestWidgets() {
// Do nothing by default
}
public void onDismissWidgets() {
// Do nothing by default
}
protected RequestWidgetManager getRequestManager() {
return requestManager;
}
/**
* This will execute the callback method when the final request is made on loading.<br>
* This must be set before super.onReveal or super.prepareFromRequest are called.
* Or use {#link RequestPresenter#prepareFromRequest(PlaceRequest, FinalCallback)} to
* set the final callback.
* #param callback
*/
public void executeAfterRequesting(FinalCallback callback) {
requestManager.setFinalCallback(callback);
}
public <P extends PresenterWidget<?>> RequestedWidget<P> getRequestedWidget(
RegisteredRequestWidget<P> registry) {
return requestManager.get(registry);
}
public <P extends PresenterWidget<?>> RegisteredRequestWidget<P> registerRequestWidget(
HasHandlers handler, Class<P> clazz, Object slot, boolean clearSlot) {
return registerRequestWidget(handler, clazz, slot, clearSlot, null);
}
public <P extends PresenterWidget<?>> RegisteredRequestWidget<P> registerRequestWidget(
HasHandlers handler, Class<P> clazz, Object slot, boolean clearSlot,
RequestWidgetEvent.Callback<P> callback) {
return requestManager.registerWidget(handler, clazz, slot, clearSlot, callback);
}
}
RichPresenter
public abstract class RichPresenter<T extends View,
H extends Proxy<?>> extends Presenter<T, H> {
protected final PlaceManager placeManager;
private boolean leaveConfirmation;
private String defaultLeaveMessage = "Any unsaved work will be lost when " +
"leaving this page, are you sure you would like to leave?";
public RichPresenter(EventBus eventBus, T view, H proxy,
PlaceManager placeManager) {
this(eventBus, view, proxy, placeManager, false);
}
public RichPresenter(EventBus eventBus, T view, H proxy,
PlaceManager placeManager, boolean leaveConfirmation) {
super(eventBus, view, proxy);
this.placeManager = placeManager;
this.leaveConfirmation = leaveConfirmation;
}
/**
* Setup component control handlers for the UI
*/
protected void setupHandlers(final T view) {
// Do nothing by default
}
#Override
public void prepareFromRequest(PlaceRequest request) {
super.prepareFromRequest(request);
// Start Load Indicator
LoadingIndicatorEvent.fire(this, true);
// Attempt to set leave confirmation
setLeaveConfirmation(leaveConfirmation);
}
#Override
protected void onBind() {
super.onBind();
setupHandlers(getView());
}
#Override
protected void onUnbind() {
super.onUnbind();
// Remove all the event handlers
for(HandlerRegistration reg : handlerRegistrations) {
reg.removeHandler();
}
handlerRegistrations.clear();
}
#Override
protected void onReveal() {
super.onReveal();
// Stop Load Indicator
LoadingIndicatorEvent.fire(this, true);
// Attempt to set leave confirmation
setLeaveConfirmation(leaveConfirmation);
}
#Override
protected void onReset() {
super.onReset();
// Stop Load Indicator
LoadingIndicatorEvent.fire(this, false);
}
/**
* Set the page leave confirmation.
* #param leaveConfirmation
*/
public void setLeaveConfirmation(boolean leaveConfirmation) {
this.leaveConfirmation = leaveConfirmation;
if(leaveConfirmation && !BrowserUtils.isIEBrowser()) {
placeManager.setOnLeaveConfirmation(defaultLeaveMessage);
} else {
placeManager.setOnLeaveConfirmation(null);
}
}
public boolean isConfirmOnLeave() {
return leaveConfirmation;
}
public String getDefaultLeaveMessage() {
return defaultLeaveMessage;
}
public void setDefaultLeaveMessage(String message) {
this.defaultLeaveMessage = message;
}
}
I feel like this could be a contributing factor here. I have a large chain of presenters that I could be implementing wrong.
This is making it crazy hard for me to identify issues in my client side code. I have to go through my changes, reverting them until I no longer get this message. Which is just ridiculous. If you can see that I am doing something wrong or need more information let me know please! Would be so greatly appreciated.
Cheers! Ben

gwt-google-apis call results in "Cannot call method 'newHttpRequest' of undefined"

I am using the gwt-plus-v1-0.2-alpha API to:
Allow Google login
Fetch Signed in user's information
Google login works, but fetching the user's information fails with a
Cannot call method 'newHttpRequest' of undefined
error.
The following is my GoogleApi helper class:
public final class GoogleApi {
private static final Plus plus = GWT.create(Plus.class);
private final String clientId;
private final ClientOAuth2Login oAuth2Login;
private ClientGoogleApiRequestTransport requestTransport;
/**
* #param clientId
* This app's personal client ID assigned by the Google APIs
* Console (http://code.google.com/apis/console)
*/
public GoogleApi(EventBus eventBus, String clientId) {
this.clientId = clientId;
requestTransport = new ClientGoogleApiRequestTransport();
requestTransport.setApplicationName(MY_APP_NAME)
.setApiAccessKey(MY_API_KEY);
plus.initialize(eventBus, requestTransport);
oAuth2Login = new ClientOAuth2Login(clientId);
oAuth2Login.withScopes(PlusAuthScope.PLUS_ME);
}
public void login(final Receiver<String> callback) {
oAuth2Login.login(new Receiver<String>() {
#Override
public void onSuccess(String response) {
requestTransport.setAccessToken(response);
callback.onSuccess(response);
}
#Override
public void onFailure(ServerFailure error) {
Window.alert(error.getMessage());
}
});
}
public void getUserInfo(Receiver<Person> receiver) {
plus.people().get("me").to(receiver).fire();
}
}
The following shows where the failure occurs:
GoogleApi googleApi = new GoogleApi(eventBus, MY_CLIENT_ID);
googleApi.login(new Receiver<String>() {
#Override
public void onSuccess(final String token) {
// login is successful and access token is received
// but the following call fails with "Cannot call method 'newHttpRequest'
// of undefined" error
googleApi.getUserInfo(new Receiver<Person>() {
#Override
public void onSuccess(Person person) {
// never gets here
}
#Override
public void onFailure(ServerFailure error) {
// nor here
}
});
}
}
Try updating your gwt-google-apis libraries, there's been a change back in June that replaced calls to $wnd.googleapis.newHttpRequest() with calls to $wnd.gapi.client.rpcRequest(): https://code.google.com/p/gwt-google-apis/source/detail?r=2041
The reason authentication works it that it doesn't use the same code to talk to the server.
I have played around with the code (still using version 1.0.2-alpha) and made it work! The following is the updated code:
public final class GoogleApi {
private static final Plus plus = GWT.create(Plus.class);
private final String clientId;
private EventBus eventBus;
private final ClientOAuth2Login oAuth2Login;
private ClientGoogleApiRequestTransport requestTransport;
private String accessToken;
/**
* #param clientId
* This app's personal client ID assigned by the Google APIs
* Console (http://code.google.com/apis/console)
*/
public GoogleApi(final EventBus eventBus, String clientId) {
this.eventBus = eventBus;
this.clientId = clientId;
oAuth2Login = new ClientOAuth2Login(clientId);
oAuth2Login.withScopes(PlusAuthScope.PLUS_ME);
}
public void login(final Receiver<String> callback) {
oAuth2Login.login(new Receiver<String>() {
#Override
public void onSuccess(String response) {
accessToken = response;
callback.onSuccess(response);
}
#Override
public void onFailure(ServerFailure error) {
Window.alert(error.getMessage());
}
});
}
public void getUserInfo(final Receiver<Person> receiver) {
requestTransport = new ClientGoogleApiRequestTransport();
requestTransport.setApplicationName(ClientConstants.GOOGLE_APP_NAME)
.setApiAccessKey(ClientConstants.GOOGLE_API_KEY)
.setAccessToken(accessToken);
requestTransport.create(new Receiver<GoogleApiRequestTransport>() {
#Override
public void onSuccess(GoogleApiRequestTransport transport) {
plus.initialize(eventBus, transport);
plus.people().get("me").to(receiver).fire();
}
});
}
}
I don't know how efficient this is, though. A call to Plus's initialize() method is made every time a call to getUserInfo() is made.
Next step for me is to manually build a jar with the latest version of the API and adjust my code accordingly :/ Wish me luck!

Rails flash messages in Java

What's the best way to achieve Rails-like flash messages such as "Update successful" http://api.rubyonrails.org/classes/ActionController/Flash.html) in the Java world? I'm using Spring MVC.
I have done just that in Spring MVC with a session scoped bean.
public class FlashImpl implements Flash, Serializable{
private static final long serialVersionUID = 1L;
private static final String ERROR = "error";
private static final String WARNING = "warning";
private static final String NOTICE = "notice";
private String message;
private String klass;
public void message(String klass, String message) {
this.klass = klass;
this.message = message;
}
public void notice(String message) {
this.message(NOTICE, message);
}
public void warning(String message) {
this.message(WARNING, message);
}
public void error(String message) {
this.message(ERROR, message);
}
public boolean isEmptyMessage() {
return message == null;
}
public void clear() {
this.message = null;
this.klass = null;
}
public String getMessage() {
String msg = message;
this.clear();
return msg;
}
public void setMessage(String message) {
this.message = message;
}
public String getKlass() {
return klass;
}
public void setKlass(String klass) {
this.klass = klass;
}}
The trick is in consumming the message once it's been read for the first time. This way it can survive to a redirect after post.
I am assuming that there will be only one type of message for request!. If you don't want this you could create a hashmap as already suggested.
I inject this bean in my controllers (actually I inject it in a base controller inherited by all the others).
In your JSP you have to add some code like this:
<c:if test="${!flash.emptyMessage}" >
<div class="${flash.klass}">${fn:escapeXml(flash.message)}</div>
</c:if>
I would recommend implementing this as a session-wide HashTable, with string keys mapping to custom FlashItem objects. The FlashItem will simply contain the object or string you're storing plus a boolean value, possibly called IsNew, which should be set to true when you insert a new item into the HashTable.
On each page load you then iterate the HashTable, set any IsNew = true items to false, and delete any items where IsNew is already false. That should give you a work-alike to Rails's flash feature.
This has been added to Spring MVC 3.1.RC1:
3.1.15 Flash Attributes and RedirectAttributes
Flash attributes can now be stored in a FlashMap and saved in the HTTP session to survive a redirect. For an overview of the general support for flash attributes in Spring MVC see Section 16.6, “Using flash attributes”.
In annotated controllers, an #RequestMapping method can add flash attributes by declaring a method argument of type RedirectAttributes. This method argument can now also be used to get precise control over the attributes used in a redirect scenario. See Section 16.3.3.10, “Specifying redirect and flash attributes” for more details.
(JIRA issue: SPR-6464)
I've used Manolo Santos' example with with Spring MVC as follows:
Annotate the Flash class with #Component, and add a boolean variable to indicate if the message should live for one more request.
#Component
public class Flash {
private static final String INFO = "info";
private static final String SUCCESS = "success";
private static final String ERROR = "error";
private static final String WARNING = "warning";
private static final String NOTICE = "notice";
private final Map msgs = new HashMap();
private boolean isKept; // keep msg for one more request (when the controller method redirects to another)
private void message(String severity, String message) {
msgs.put(message, severity);
}
public void info(String message) {
this.message(INFO, message);
}
public void success(String message) {
this.message(SUCCESS, message);
}
public void notice(String message) {
this.message(NOTICE, message);
}
public void warning(String message) {
this.message(WARNING, message);
}
public void error(String message) {
this.message(ERROR, message);
}
public boolean isEmptyMessage() {
return msgs.isEmpty();
}
public void clear() {
msgs.clear();
isKept = false;
}
public Map getMessage() {
return msgs;
}
public boolean isKept() {
return isKept;
}
public void keep() {
isKept = true;
}
public void unKeep() {
isKept = false;
}
}
Use an interceptor to add the flash message to the model object.
public class FlashMessageInterceptor extends HandlerInterceptorAdapter {
#Resource
Flash flash;
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if (!flash.isKept()) {
modelAndView.addObject("flash", flash);
}
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
if (flash.isKept()) {
flash.unKeep();
}
else {
flash.clear();
}
}
}
In your controller, if you have a method that redirects to another method you could just say; flush.keep(), to have the flash message displayed.
#Controller
public class ComputerCampLove {
#Resource
private Flash flash;
#RequestMapping(method = RequestMethod.GET)
public String takeMeToAnotherPlace(Model model) {
flash.info("Fa-fa-fa!");
flash.keep();
return "redirect:somewhere";
}
}
If you have not invested a huge amount of work into your spring java app, you could look at running rails on jruby. The beauty of running jRuby on Rails is that you can mix and match ruby gems and java libs.
If you have already put a fair amount of work into your application then this is more then likely not an option.

Categories