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.
Related
I have a class:
#Getter
#Setter
public class Message {
private String sender;
private Set<String> receivers;
private String text;
}
The Message class can be extended by EmailMessage and SMSMessage having their own additional fields.
There are 2 services - EmailService and SMSService.
They both do the following operations:
Take the message object which contains sender and receiver user ids.
EmailService calls another service to transform the user ids to email ids. SMSService does the same to transform the user ids to phone numbers.
The Message object should be transformed to EmailMessage and SMSMEssage in their respective services.
Send the EmailMessage and SMSMessage.
I've been going through many design patterns to solve this problem. But I didn't find any such pattern to transform the fields of an object and/or convert the parent object to a child object.
The only design patterns that come close are Strategy and Decorator.
I'm using Strategy Pattern for using either EmailService or SMSService at runtime. It is solving just one part of my problem. Will Decorator Pattern help for solving the other?
Edit: Adding the current implementation
public abstract class AbstractMessageService<T extends Message> {
#KafkaHandler(isDefault = true)
public final void consume(Message message) {
T convertedMessage = getMessageConverterStrategy().convert(message);
send(convertedNotificationMessage);
}
protected abstract void send(T message) throws Exception;
protected abstract MessageConverterStrategy getMessageConverterStrategy();
}
#KafkaListener(
topics = "topicName",
groupId = "email-group")
public class EmailService extends AbstractMessageService<EmailMessage> {
#Override
protected void send(EmailMessage message) {
// Some logic to send email.
}
#Override
protected MessageConverterStrategy getMessageConverterStrategy() {
return new EmailMessageConverterStrategy();
}
}
#KafkaListener(
topics = "topicName",
groupId = "sms-group")
public class SMSService extends AbstractMessageService<SMSMessage> {
#Override
protected void send(SMSMessage message) {
// Some logic to send sms.
}
#Override
protected MessageConverterStrategy getMessageConverterStrategy() {
return new SMSMessageConverterStrategy();
}
}
public interface MessageConverterStrategy<T extends Message> {
T convert(Message message);
}
In the SMSMessageConverterStrategy and EmailMessageConverterStrategy, I want to use other design pattern to do the conversion of Message to SMSMessage and EmailMessage respectively.
Because you have to convert almost everything, I think Message and Email and SMS are not related. I think you should use Factory method like this:
public class Test {
public static void main(String[] args) throws Exception {
Message m = new Message();
Email email = Email.from(m);
EmailService.sendMail(email);
}
}
class Message {
private String sender;
private Set<String> receivers;
private String text;
}
class Email {
private String senderMailId;
private String text;
private Set<String> receiverMailIds;
//make it private
private Email() {
}
public static Email from(Message m) {
Email email = new Email();
email.senderMailId = ""; //call MappingService.toEmailId(m.getSender());
//do rest of the conversion and processing
return email;
}
}
class EmailService {
public static void sendMail(Email e) {
//send the mail
}
}
I would suggest the use of Factory method pattern.
A MessageService which would be implemented by SmsService and EmailService.
interface MessageService {
Message transform(Message message);
}
class SmsService implements MessageService {
#Override
SmsMessage transform(Message message) {
// do appropriate transformation and return.
}
}
class EmailService implements MessageService {
#Override
EmailMessage transform(Message message) {
// do appropriate transformation and return.
}
}
Then use a factory to create the appropriate service. (Assuming an enum MessageType):
public class MessageServiceFactory {
public MessageService getService(Message m, MessageType type){
switch(type) {
case SMS:
return new SmsService(m);
case EMAIL:
return new EmailService(m);
default:
// throw some exception.
}
}
}
Then to do the actual transformation:
Message convertMessage(Message m, MessageType type) {
return new MessageServiceFactory().getService(m, type)
.transform();
}
I'm working on a class that will get a list of strings and process them asynchronously using CompletableFutures. Each string is processed by invoking another class that will perform several operations and return a response or throw an exception if there is an error.
I would like to aggregate the responses that I get, whether they have a valid response or an exception and return them as a list to the caller. I would like the caller to be able to expect a list of SomeResponse and be able to interpret them using polymorphism.
However, I'm stuck on determining if this can be done using polymorphism at all, given that the fields for the success and error response are completely different. I have added some pseudo code below on one alternative I have thought of. Basically have SomeResponse be an interface with an isSuccess method. This will allow the caller to know if it's an error or not. However, the caller would still have to cast it to the correct implementation in order to get the value or the error. Is there a better way to approach this? My requirement is being able to return both a success and error response for each given request in the list. If there is an exception, we don't want to abort the entire operation.
public MyProcessorClass {
private final SomeOtherClass someOtherClass;
public List<SomeResponse> process(List<String> requestList) {
return requestList.stream().map(this::procesRequest)
.collectors(Collect.tolist()):
}
private processRequest(String request) {
CompletableFuture completableFuture = CompletableFuture
.supplyAsync(() => {
return new SomeSuccessResponse(someOtherClass.execute(request));
})
.exceptionally(e -> {
return new SomeErrorResponse(e.getCause);
});
return completableFuture.get();
}
}
public interface SomeResponse {
boolean isSuccess();
}
public class SomeSuccessResponse implements SomeResponse {
private final String value;
#Getter
private final boolean success;
public SomeSuccessResponse(String value) {
this.value = value;
this.success = true;
}
}
public class SomeErrorResponse implements SomeResponse {
private final Throwable error;
#Getter
private final boolean success;
public SomeErrorResponse(Throwable error) {
this.error = error;
this.success = false;
}
}
What you want is the visitor pattern https://en.wikipedia.org/wiki/Visitor_pattern
public class Main {
interface IResponse {
void acceptHandler(IResponseHandler handler);
}
static class ResponseA implements IResponse {
#Override
public void acceptHandler(IResponseHandler handler) {
handler.handle(this);
}
}
static class ResponseB implements IResponse {
#Override
public void acceptHandler(IResponseHandler handler) {
handler.handle(this);
}
}
public interface IResponseHandler {
void handle(ResponseA response);
void handle(ResponseB responseB);
}
public static void main(String[] args) {
final IResponseHandler handler = new IResponseHandler() {
#Override
public void handle(ResponseA response) {
System.out.println("Handle ResponseA");
}
#Override
public void handle(ResponseB responseB) {
System.out.println("Handle ResponseB");
}
};
final IResponse someResponse = new ResponseA();
someResponse.acceptHandler(handler);
}
}
I have a messaging producer (RabbitMQ) and depending on what kind of message i have to send, i need to change the routing key and exchange at runtime.
Given this i'd implemented a strategy to load each class with specific properties, but it's not appear a good solution.
For example:
public class MyProducerStrategy1 extends RabbitMessagingProducer {
private static final String ROUTING_KEY = "order1";
private static final String EXCHANGE = "myexchange1";
#Override
String getRoutingKey() {
return ROUTING_KEY;
}
#Override
String getExchange() {
return EXCHANGE;
}
#Override
public void sendMessage(Message message) {
super.sendMessage(message);
}
}
public class MyProducerStrategy2 extends RabbitMessagingProducer {
private static final String ROUTING_KEY = "fullfilment";
private static final String EXCHANGE = "myexchange2";
#Override
String getRoutingKey() {
return ROUTING_KEY;
}
#Override
String getExchange() {
return EXCHANGE;
}
#Override
public void sendMessage(Message message) {
super.sendMessage(message);
}
}
public abstract class RabbitMessagingProducer implements MessagingProducerStrategy {
#Autowired
private RabbitTemplate rabbitTemplate;
abstract String getRoutingKey();
abstract String getExchange();
#Override
public void sendMessage(Message message) {
rabbitTemplate.convertAndSend(getExchange(), getRoutingKey(), message);
}
}
Does it make sense? or there's another approach to load there properties and have maybe one class?
Do not create multiple class for this scenario.
load the files from a property file based on the message.
Another option is to create a static map of values with message types as the key and routing key as the value. Fetch the values from the map based on the message type.
I have a Spring controller that takes posts and it works. The only problem is that our SMS providers will be sending us headers that contain keys with a capitalized first letter, for example:
{
"FromPhoneNumber":"15177754077",
"ToPhoneNumber":"17572046106",
"ResponseReceiveDate":"7/29/2014 5:25:10 AM",
"Message":"PIN 1234"
}
Spring will throw an error like:
Could not read JSON: Unrecognized field "FromPhoneNumber" (class com.talksoft.spring.rest.domain.CDynePost), not marked as ignorable (4 known properties: "responseReceiveDate", "toPhoneNumber", "fromPhoneNumber", "message"])
So, there must be a way for me to override this behavior. Here is the controller method that handles the CDyne posts:
#RequestMapping(method = RequestMethod.POST, value="/celltrust")
public ResponseEntity<String> cellTrustPost(#RequestBody CDynePost cDynePost) {
String message = "FAILED";
UserInteraction userInteraction = getUserInteraction(cDynePost);
boolean success = someSpringService.logMessage(userInteraction);
if (success) {
message = "OK";
return new ResponseEntity<String>(message, HttpStatus.ACCEPTED);
} else {
return new ResponseEntity<String>(message, HttpStatus.FAILED_DEPENDENCY);
}
}
and here is the CDynePost class:
public class CDynePost {
private String FromPhoneNumber;
private String ToPhoneNumber;
private String ResponseReceiveDate;
private String Message;
public String getFromPhoneNumber() {
return FromPhoneNumber;
}
public void setFromPhoneNumber(String FromPhoneNumber) {
this.FromPhoneNumber = FromPhoneNumber;
}
public String getToPhoneNumber() {
return ToPhoneNumber;
}
public void setToPhoneNumber(String ToPhoneNumber) {
this.ToPhoneNumber = ToPhoneNumber;
}
public String getResponseReceiveDate() {
return ResponseReceiveDate;
}
public void setResponseReceiveDate(String ResponseReceiveDate) {
this.ResponseReceiveDate = ResponseReceiveDate;
}
public String getMessage() {
return Message;
}
public void setMessage(String Message) {
this.Message = Message;
}
}
I've looked at ObjectMapper but I am not sure how to work this into my controller, and truth be told I'd prefer not to have to write a bunch of extra classes if Spring will do it for free.
Simply annotate your field, getter, or setter with #JsonProperty, specifying the exact name that will appear in the JSON. For example
#JsonProperty("FromPhoneNumber")
private String FromPhoneNumber;
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