I would approach this problem a bit differently. Let's assume you need only an attribute "attr1" from the session. So what you actually should do is to get this attribute in @Controller pass it to your calculation logic class (@Service), return the result of the calculation back to @Controller and store it in session. Thanks to that your calculation logic will not be independent from a web layer (HttpSession), hence reusable.
E.g.:
@Controller
public class AController {
@Autowired
private SomeCalculationService someCalculationService;
@RequestMapping(value = "/", method = RequestMethod.GET)
@ResponseBody
public Account accounts(HttpSession httpSession) {
MyData myData = httpSession.getAttribute("attr1");
SomeResult someResult = someCalculationService.calculate(myData);
httpSession.setAttribute("attr2", someResult);
}
}
I would approach this problem as I would approach any localization issue: ResourceBundle. I use a class called I18n
that has a static method called getMessage
that takes a key and optionally a list of arguments. The key gets looked up in a ResourceBundle
configured to use the default Locale
or whatever you prefer (specifics here aren't important, so long as the caller to I18n
doesn't have to know about what the Locale
is).
The I18n
class is as follows:
public final class I18n {
private I18n() {
}
private static ResourceBundle bundle;
public static String getMessage(String key) {
if(bundle == null) {
bundle = ResourceBundle.getBundle("path.to.i18n.messages");
}
return bundle.getString(key);
}
public static String getMessage(String key, Object ... arguments) {
return MessageFormat.format(getMessage(key), arguments);
}
}
In your project, you would have in package path.to.i18n, files containing messages.properties (default), messages_en.properties (Locale en), messages_it.properties (Locale it), etc.
When you need to translate an enum value, you shouldn't have to treat it any differently than any other key, except the key is the enum term.
In other words, you have:
public enum AccountStatusEnum {
Active,
Inactive,
Pending
}
When you call I18n.getMessage
, you pass with AccountStatusEnum.Active.toString()
and the translation will be found in a property file with key "Active". If you're like me, and you prefer to use lower-case keys, then you should perform toLowerCase()
on the string. Better still, you create a new I18n.getMessage
that takes an Enum rather than a key, and that method calls the String version of I18n.getMessage
with the enum value converted to string and in lowercase.
So you'd then add this to the I18n
class:
public static String getMessage(Enum<?> enumVal) {
return getMessage(enumVal.toString().toLowerCase());
}
I prefer to use this method because it incorporates the same translation logic in the same part of the program without impacting your design. You may even want to differentiate enum keys from your other keys, in which case you can begin each enum key with "enum.", so you would prepend "enum." when calling getMessage
. Just remember to name your keys in your properties files accordingly.
I hope that helps!
Best Answer
My personal approach would be not to use the session everywhere. I'd try to reduce its usage at the controller layer. In the controller I'd extract what I need for my business logic and the later will never know that there is a web session involved. This will make that logic portable to non-web applications, and will work with all its data in a type-safe manner that is free of boilerplate code.
If you want to avoid the boilerplate code of obtaining the request, then the session, and then the items from the session, your method approach is fine enough, and a common practice in my experience. If you are using it in Java, you can improve it a little by adding some generics:
You can use it now like this:
I am assuming the method is defined in a static
SessionUtils
class. If you define it in a base controller class, want to use this from inside of a controller, you should call it this way:The
this
keyword can be substituted withsuper
, but without it a compile error would occur due to the generics.In addition to my suggestion above, there is another way of hiding the session usage and have a session-free and independent API/facade. By sticking to the programming-to-interfaces paradigm you can have interface implementations that use the session, but it is never shown by the interfaces you enforce in your facade/api. So, for instance you may have a general purpose authorization service, exposed trough interface, that shows you who is the currently logged user, if s/he is a guest and whether s/he has a particular permission. Your application then could use a concrete implementation of the service interface that gets the user from the session. When you use the service, the interface does not expose anything related to the session, so all usages remain session agnostic, and type safe.