I read BalusC's excellent tutorial on JSF communication and it helped me establish the basics of my app. I would like to share the currently logged in User object that is set in the SessionScoped BaseBean class with all of its subclasses. Can this be done without injecting BaseBean as a #ManagedProperty for every single backing bean that needs to reference the logged in User?
My classes are listed below. Please let me know if more info is needed and I will be happy to update my question.
BaseBean Class
All other beans are subclasses of this bean. I am doing this to allow code reuse between beans.
#ManagedBean
#SessionScoped
public class BaseBean {
#EJB
protected UserDao userDao;
// Other DAOs along with methods (like isLoggedIn()) shared between beans
private User loggedInUser;
public User getLoggedInUser() {
return loggedInUser;
}
public void setLoggedInUser(User user) {
loggedInUser = user;
}
public boolean isLoggedIn() {
return loggedInUser != null;
}
}
LoginBean Class
Backing bean for the login page. To reduce number of DB calls I used the #ManagedProperty approach from the above tutorial to set the User object in the #SessionScoped BaseBean. Right now logging in and setting loggedInUser works as expected.
#ManagedBean
#RequestScoped
public class LoginBean extends BaseBean {
#ManagedProperty(value = "#{baseBean}")
protected BaseBean baseBean;
private String username;
private String password;
public String login() {
Subject currentUser = SecurityUtils.getSubject();
try {
currentUser.login(username, password);
} catch (Exception e) {
e.printStackTrace();
} finally {
baseBean.setLoggedInUser(userDao.getUser(username));
}
return "index";
}
public String getUserFirstName() {
return baseBean.getLoggedInUser().getFirstName();
}
// Getters and Setters, including for the #ManagedProperty baseBean.
}
CreateReport Class
This is an example of one backing bean from many. I want to reference the currently logged in User in order to create a report, however if the below code runs the User will be null! The only way I can get it to work is by adding a #ManagedProperty entry with getters and setters for BaseBean just like in LoginBean class. I would really like to avoid this as I will be copy-pasting this code to almost every single backing bean that I have!
#ManagedBean
#RequestScoped
public class CreateReport extends BaseBean {
private Report report = new Report();
public String createReport() {
report.setOwner(getLoggedInUser()); // Use inherited method
// instead of DI-ing BaseBean
reportDao.create(report);
return "index";
}
}
Used Software
Glassfish 4
Mojarra 2.2
Edit
One solution that I found is getting the instance of BaseBean from the FacesContext directly (I guess somehow the other beans are not in the same context or "don't see it?"). The following code (from BaseBean) would do what I want, however any bean subclass would have to invoke base() and that seems awkward and wrong.
protected FacesContext context = FacesContext.getCurrentInstance();
public BaseBean base() {
return (BaseBean) context.getApplication().evaluateExpressionGet(context, "#{baseBean}", BaseBean.class);
}
I can see you want to implement authentication and authentication controls, then you can use JSF Filters. You can keep your BaseBean class with session scope, create a new class which implements javax.servlet.Filter and in this class to get your BaseBean class through of session, for example:
public class LoginFilter implements javax.servlet.Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
BaseBean base = (BaseBean) req.getSession().getAttribute("baseBean");
if (base != null && base.isLoggedIn()) {
// to do something
chain.doFilter(request, response);
} else {
// to do something
HttpServletResponse res = (HttpServletResponse) response;
res.sendRedirect(req.getContextPath() + "/index.xhtml");
}
}
public void init(FilterConfig config) throws ServletException {
// to do something
}
public void destroy() {
// to do something
}
}
Now, if you want to create a report with your BaseBean class, you can get your BaseBean class of session:
BaseBean base = (BaseBean) ( FacesContext.getCurrentInstance()
.getExternalContext().getRequest()).getSession().getAttribute("baseBean") );
Then my recommendation in your case is to avoid inheritance and use the advantages of JSF.
I hope this information helps you.
Good Luck.
making BaseBean a managed bean itself, and using it as a superclass for all other managed beans are two things that should not go along.
instead, you can:
remove #ManagedBean annotation from BaseBean.
save loggedInUser to session.
keep isLoggedIn as a protected method in BaseBean. you will be able to reach session via FacesContext there and get loggedInUser from session.
((HttpServletRequest)FacesContext.getCurrentInstance()
.getExternalContext().getRequest()).getSession()
ps: i don't know what the hell i was thinking when offering a static variable.
Related
I am using Spring Rest for creating Rest APIs. For authentication, I have created a filter that extends from OncePerRequestFilter, this filter check if a valid token is present in the header. I have to set some custom object information in Spring context. So that I can retrieve it in my Controller classes. Something like:
AuthenticationFilter extends OncePerRequestFilter {
// validation goes here
requestContext.setSecurityContext(new SecurityContext() {
public Principal getUserPrincipal() {
return new UserInfo("", token, userID, userType);
}
}
});
Don't create custom filters for authentications. User security context instead it's pretty powerful.
I don't know about any option how put custom object into spring context. But I can show you how to create your custom context in spring framework and then use it as any other context. Of course you will be able to put custom objects into it.
PSEUDO CODE:
1.)
Create custom object which you want to have in context:
public class UserInfo implements Serializable {
private String id;
private String email;
//Getters and setters
}
2.)
Create custom context:
#Service
public class UserInfoContext {
private static final ThreadLocal<UserInfo> userInfoThreadLocal = new InheritableThreadLocal<>();
public void setUserInfo(UserInfo userInfo) {
UserInfoContext.userInfoThreadLocal.set(userInfo);
}
public UserInfo getUserInfo() {
return UserInfoContext.userInfoThreadLocal.get();
}
public void clearContext() {
UserInfoContext.userInfoThreadLocal.remove();
}
}
3.)
Create custom interceptor for initializing custom object inside custom context
#Service
public class UserInfoInterceptor implements HandlerInterceptor {
private UserInfoContext userInfoContext;
#Autowired
public UserInfoInterceptor(UserInfoContext userInfoContext) {
this.userInfoContext = userInfoContext;
}
#Override
public boolean preHandle(HttpServletRequest requestServlet, HttpServletResponse responseServlet, Object handler) throws Exception {
// Call userInfoContext.setUserInfo() with custom data.
return true;
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
userInfoContext.clearContext();
}
}
Thanks to ThreadLocal you are able to save custom information about current user.
I would like to have a variable that follows along the full lifecycle of a request in java EE.
For example it could be for a logging function, so that I can filter all log entries by request.
The key part that I want to get at is that it must be relatively easy to implement in an already existing application so if possible some sort of dependency injection that gets the variable related to the specific request.
I've tried injectiong a #RequestScoped variable, but it doesn't work since it is only scoped to the container. I would need to be able to inject the same object to different containers. Is this at all possible?
EDIT: I want something along the lines of this:
#RequestScoped
public class RequestVariables {
public String id;
}
#Stateless
public class Logger {
#Inject
private RequestVariables requestVariables;
public void log(String message) {
System.out.println(requestVariables.id + ":" + message);
}
}
#Stateless
public class Service {
#Inject
private Logger logger;
#Inject
private RequestVariables requestVariables;
public void save(String data) {
logger.log("Save");
session.save(data + requestVariables.id); //Maybe add request parameter to save aswell
}
}
public class API {
#Inject
private Service service;
#Inject
private Logger logger;
#Inject
private RequestVariables requestVariables;
#Path("/1")
#GET
public Response get(#QueryParam("data") String data) {
requestVariables.id = UUID.randomUUID().toString()
service.save(data);
logger.log("Get");
return Response.status(204).build();
}
}
Currently this is what I have experimented with:
#RequestScoped
public class RequestScope {
private int test = 0;
public RequestScope(int test) {
this.test = test;
}
public RequestScope(){}
public int getTest() {
return test;
}
public void setTest(int test) {
this.test = test;
}
}
#Provider
public class RequestScopeFilter implements ContainerRequestFilter {
#Inject
private javax.inject.Provider<RequestScope> requestScopeProvider;
#Context
private HttpServletRequest request;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestScopeProvider.get().setTest(42);
request.setAttribute("test", "superTest");
}
}
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
#TransactionAttribute(value=TransactionAttributeType.NOT_SUPPORTED)
public class Service {
#Context
private HttpServletRequest httpServletRequest;
#Inject
private Provider<RequestScope> requestScopeProvider;
public void test() {
RequestScope scope = requestScopeProvider.get();
String test = (String)httpServletRequest.getAttribute("test");
}
}
So when I get the scope from my service then it is a new object with test set to 0, and then it throws an NPE since httpServletRequest is null
option #1
Implement an Interceptor and set the request id as HttpServletRequest attribute:
#AroundInvoke
public Object setRequestId(InvocationContext ic) throws Exception {
HttpServletRequest request = [..] // getHttpServletRequest(ic);
request.setAttribute("request-id", UUID.randomUUID().toString());
return ic.proceed();
}
Then use HttpServletRequest everywhere you need it
#Context
private HttpServletRequest httpRequest;
option #2
If want just to filter your logs by an unique id, you can configure your Logger to print the thread name: [%t]
Example: Log4j PatternLayout
option #3
Use a custom java bean to encapsulate the request data (query param, request id etc.) and pass this bean across your application services.
public class API {
#Inject
private Service service;
#Path("/1")
#GET
public Response get(MyCustomRequestBean data) {
service.doSomejob(data);
return Response.status(204).build();
}
}
Set the request id and query param in ParamConverter:
Jax-RS ParamConverter - ParamConverterProvider method return type mismatch
You can inject a provider in your service:
#Inject
Provider<RequestVariables> vars
And then call get () to get the instance. If you try to get () in a thread outside a request scope context you'll get an exception. I would however try to structure in a way that would not allow this to happen
A solution that I found is to use ThreadLocal variables. It seems rather dirty, but it works since each request is executed on it's own thread(as far as I am aware). So this is what I got:
public class RequestScope {
private static final ThreadLocal<String> id = ThreadLocal.withInitial(() -> UUID.randomUUID().toString());
public static String get() {
return id.get();
}
}
With that I can also easily exchange the ThreadLocal to return something more specific if so desired.
And I can get the variables from pretty much anywhere, assuming that the request is not starting a different thread
I have a #RestController in which every method needs to pass a (WebSSO) cookie down to a service. The service in turn uses the cookie for authentication. I am autowiring the service bean in controller. The service has a setter setCredentials(String webSSOCookie). One easy way is to call this setter in every method. I would like to do it better way; for instance using HandlerInterceptor. However the HandlerInterceptor does not have access to the controller (and hence its members) - am I right?
In jersey I could use filter. How do I achieve it in SPRING?
#RestController
#RequestMapping("/documents")
public class ECMRestController {
#Autowired
public ECMService ecmService;
#RequestMapping(value="/{documentId}", method=RequestMethod.DELETE)
public void deleteDocument(#RequestParam("documentId") String documentId) throws IllegalArgumentException, HttpClientErrorException {
// I could get and pass the cookie to ecmService in every method.
// ecmService.setCredentials(webSSOCookieObtainedfromRequest);
// However I don't want to do it that way.
ecmService.deleteDocument(documentId);
}
// Other REST Methods that need to pass the cookie in the same way.
}
You can request the SecurityContextHolder to query the current Authentication that you had customized in a filter.
MyCustomAuth auth = (MyCustomAuth) SecurityContextHolder.getContext().getAuthentication();
auth.getCookie();
or you can just use ThreadLocal in a context that can be retrieve from anywhere:
public class CookieContext {
private static final ThreadLocal<Cookie> COOKIE = new ThreadLocal<>();
private static final CookieContext INSTANCE = new CookieContext();
public void setCookie(Cookie value) {
COOKIE.set(value);
}
public Cookie getCookie() {
return COOKIE.get();
}
public static CookieContext getContext() {
return INSTANCE;
}
}
public class CookieInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
CookieContext context = CookieContext.getContext();
context.setCookie(request.getCookies()[0]);
}
}
In my RPCServlet I am using the method AbstractRemoteServiceServlet.getThreadLocalRequest() to get the HttpSession. Now I want to unit-test it. I am using Mockito and thought I just could mock everything, but the method is final and protected.
Is there any other way to Unit-test AbstractRemoteServiceServlet.getThreadLocalRequest().getSession()
At the end you are trying to get a Session. In our case we solve this situation doing this:
Using GUICE for getting our instances (making them available in the GIVEN part of the test)
public class TestServerModule extends com.google.inject.AbstractModule {
#Override
protected void configure() {
.....
bind(HttpServletRequest.class).to(MockRequest.class).in(Singleton.class);
bind(HttpServletResponse.class).to(MockResponse.class).in(Singleton.class);
....
}
....
#Provides
#Singleton
RequestUtil getRequestUtil(final HttpServletRequest req, final HttpServletResponse resp) {
return new RequestUtilsImpl() {
public HttpServletRequest getThreadRequest() {
return req;
}
public HttpServletResponse getThreadResponse() {
return resp;
}
};
}
RequestUitl object contains everything related with Session and more server stuff (that is not important for your problem :D). The important part here is you can have access to the getThreadRequest(), so you have access to getSession() method.
What is the problem? You can not have a real HttpServletRequest object in your instances, so you need to mock them. For doing it, we specified the bind rules at the top.
At the end your test should be something like:
#RunWith(...)
#GuiceModules({TestServerModule.class, ....})
public class YourTest extends junit.framework.TestCase {
#Inject RequestUtil requestUtil;
....
#Test public void
test_session_after_doing_something() {
//GIVEN
HttpSession mockedSession = requestUtil.getThreadRequest().getSession();
....
}
....
}
I have a #Controller with #Autowired fields and handler methods that I want to annotate with custom annotations.
For example,
#Controller
public class MyController{
#Autowired
public MyDao myDao;
#RequestMapping("/home")
#OnlyIfXYZ
public String onlyForXYZ() {
// do something
return "xyz";
}
}
Where #OnlyIfXYZ is an example of a custom annotation. I was thinking I would intercept the Controller bean creation, pass my own CGLIB proxy on which Spring can then set properties, like the autowired field.
I tried using a InstantiationAwareBeanPostProcessor but that solution doesn't work great because postProcessBeforeInstantiation() short-circuits the rest of the process. I tried with postProcessAfterInitialization(), like below
public class MyProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// Here the bean autowired fields are already set
return bean;
}
#Override
public Object postProcessAfterInitialization(Object aBean, String aBeanName) throws BeansException {
Class<?> clazz = aBean.getClass();
// only for Controllers, possibly only those with my custom annotation on them
if (!clazz.isAnnotationPresent(Controller.class))
return aBean;
Object proxy = Enhancer.create(clazz, new MyMethodInterceptor());
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
try {
// get the field and copy it over to the proxy
Object objectToCopy = field.get(aBean);
field.set(proxy, objectToCopy);
} catch (IllegalArgumentException | IllegalAccessException e) {
return aBean;
}
}
return proxy;
}
}
This solution uses reflection to copy over all the fields of the target bean to the proxy bean (kind of hacky for my taste). But I don't have access to the HttpServletRequest and HttpServletResponse objects if those aren't arguments in the method I'm intercepting.
Is there another callback I can inject into Spring bean creation logic to inject my own Proxy Controller before Spring populates its properties? I need to be able to access the HttpServletRequest and HttpServletResponse objects regardless of if the Controller handler method has it in its definition, ie. as arguments.
N.B The #Autowired field is also a proxy, it is annotated with #Transactional so Spring proxies it up.
EDIT: The AOP solution works nicely for intercepting the method invocation, but I can't find a way to access the HttpServletRequest and HttpServletResponse objects, if they aren't already method arguments.
I'm probably going to end up using HandlerInterceptorAdapter, but I was hoping I can do it with OOP so as to not add the overhead to methods that don't need it.
Take a look at Spring AOP. It has exactly the facilities you are after. For your example, you could do something like this:
#Aspect
#Component
public class MyAspect {
#Around("#annotation(path.to.your.annotation.OnlyIfXYZ)")
public Object onlyIfXyz(final ProceedingJoinPoint pjp) throws Exception {
//do some stuff before invoking methods annotated with #OnlyIfXYZ
final Object returnValue = pjp.proceed();
//do some stuff after invoking methods annotated with #OnlyIfXYZ
return returnValue;
}
}
It is worth noting that Spring will only apply the proxy to classes that are a part of its application context. (which it appears is the case in your example)
You can also use Spring AOP to bind parameters to your aspect method. This can be done in various ways, but the one you are after is probably args(paramName).
#Aspect
#Component
public class MyAspect2 {
#Around("#annotation(path.to.your.annotation.OnlyIfXYZ) && " +
"args(..,request,..)")
public Object onlyIfXyzAndHasHttpServletRequest(final ProceedingJoinPoint pjp,
final HttpServletRequest request) throws Exception {
//do some stuff before invoking methods annotated with #OnlyIfXYZ
//do something special with your HttpServletRequest
final Object returnValue = pjp.proceed();
//do some stuff after invoking methods annotated with #OnlyIfXYZ
//do more special things with your HttpServletRequest
return returnValue;
}
}
This aspect should do a part of what you are after. It will proxy methods annotated with #OnlyIfXYZ that ALSO take in a HttpServletRequest as a parameter. Further, it will bind this HttpServletRequest into the Aspect method as a passed in parameter.
I understand that you are after potentially both HttpServletRequest and HttpServletResponse, so you should be able to modify the args expression to take in both request and response.
Taking into account your comment under the question all you need is HandlerInterceptor.
http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/web/servlet/HandlerInterceptor.html
You need to implement that interface and add it to your configuration, for example:
<mvc:interceptors>
<bean id="customInterceptor" class="com.example.interceptors.CustomInterceptor"/>
</mvc:interceptors>
This interface provides method preHanlde, which has request, response and HandlerMethod. To check if the method is annotated just try this:
HandlerMethod method = (HandlerMethod) handler;
OnlyIfXYZ customAnnotation = method.getMethodAnnotation(OnlyIfXYZ.class);
I think that not, but I supose that you could autowire the proxy after creating it.
public class MyProcessor extends InstantiationAwareBeanPostProcessorAdapter
implements BeanFactoryAware {
private AutowireCapableBeanFactory beanFactory;
#Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
// This is where I thought I would do it, but it then skips setting fields alltogether
if (beanClass.isAnnotationPresent(Controller.class)) {
Object proxy = Enhancer.create(beanClass, new MyInterceptor());
// autowire
beanFactory.autowireBean(proxy);
return proxy;
}
return null;
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
}
}
Other alternative is to create a Spring AOP Proxy (using ProxyFactory) in postProcessAfterInitialization method. For this way AbstractAutoProxyCreator could be useful. See BeanNameAutoProxyCreator as sample. But imho, an annotation pointcut (Nicholas answer) do the same and is simpler.
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation will short-circuit the bean creation approach. The only processing applied is postProcessAfterInitialization. Which means that, autowiring won't happen because AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues will never be called. Therefore, you should manually inject or autowire the properties of the proxied beans in postProcessAfterInitialization method.
Question: Does moving the proxying logic in postProcessAfterInitialization method have an impact to your business requirements? If none, I suggest you do the proxying there.
FYI: If you are not building an API, do the annotation approach as suggested by #nicholas.hauschild.