My Issue:
I'm not certain, but I believe there is an SAP provided authentication Servlet Filter, redirecting to the SAP default logon page (which of course is causing a problem since it isn't returning an xml response), which is making the FullAjaxExceptionHandler to not be invoked. I'm looking for help in determining what the root cause really is, or if there are any alternatives to fix it.
My environment:
SAP Netweaver 7.4 (servlet 2.5 with JSF 2.1.x support)
SAP JVM 6.1 (SAP's 1.6 jvm)
Mojarra 2.1.29
Omnifaces 1.11
Background:
I have a sample application which has session-config session-timeout set to 1 minute, and have the Omnifaces FullAjaxExceptionHandlerFactory configured in faces-config.xml.
web.xml
...
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/errorPage.xhtml</location>
</error-page>
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/WEB-INF/viewExpired.xhtml</location>
</error-page>
<session-config>
<session-timeout>1</session-timeout>
</session-config>
...
faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd" version="2.1">
<factory>
<exception-handler-factory>org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory</exception-handler-factory>
</factory>
...
The way I've determined Netweaver works, is it will mark the session as "expired" after one minute, but the session itself is not cleaned up immediately (I understand this is common in web containers). If I submit an ajax request after the session expired but before the session is completely removed by the server, the FullAjaxExceptionHandler kicks in, and properly displays my View Expired page. If however the server has already cleaned up the expired session, nothing happens, and the page appears "dead" until a physical refresh is performed.
From reading many answers by #balusc, I suspect that an SAP provided http filter is kicking in, which is redirecting the ajax request to the SAP login page, which is why the application isn't working.
Is there any way of determining for sure what is causing it? If it is a server provided filter, is there anyway to override it?
Yet again, thanks BalusC! You pushed me in the right direction.
SAP Netweaver has a global-web.xml file which defines some default functionality. One of them is as follows:
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>com.sap.engine.services.servlets_jsp.server.servlet.AuthenticationFilter</filter-class>
</filter>
So what I did was used the same <filter-name> in my own web.xml, providing a class which extends SAP's Authentication Filter.
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>com.example.AjaxAwareAuthenticationFilter</filter-class>
</filter>
Since the <filter-name> is the same, the one defined in my local web.xml wins.
Here is my class:
package com.example;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import com.sap.engine.services.servlets_jsp.server.servlet.AuthenticationFilter;
public class AjaxAwareAuthenticationFilter extends AuthenticationFilter {
#Override
public void destroy() {
super.destroy();
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpSession session = httpRequest.getSession(false);
if (session == null && "partial/ajax".equals(httpRequest.getHeader("Faces-Request"))) {
// JSF ajax request. Return special XML response which instructs
// JavaScript that it should in turn perform a redirect.
response.setContentType("text/xml");
response.getWriter()
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
.printf("<partial-response><redirect url=\"%s\"></redirect></partial-response>",
httpRequest.getContextPath());
} else {
super.doFilter(request, response, chain);
}
}
#Override
public void init(FilterConfig config) throws ServletException {
super.init(config);
}
}
Related
Thanks a lot in advance for reading this question.
Setup
I am using:
spring-security-oauth2:2.0.7.RELEASE
spring-cloud-security:1.0.1.RELEASE
spring-session:1.0.1.RELEASE
and would have a question regarding the persistence of spring-security-oauth2 OAuth2ClientContext in a Redis datastore when using spring-session (via #EnableRedisHttpSession) in a Single-Sign-On (#EnableOAuth2Sso), reverse proxy (#EnableZuulProxy) gateway.
Problem
It seems to me that the SessionScoped JdkDynamicAopProxied DefaultOAuth2ClientContext created in org.springframework.cloud.security.oauth2.client.OAuth2ClientAutoConfiguration is not correctly persisted in the Redis datastore.
#Configuration
#ConditionalOnBean(OAuth2SsoConfiguration.class)
#ConditionalOnWebApplication
protected abstract static class SessionScopedConfiguration extends BaseConfiguration {
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
public OAuth2ClientContext oauth2ClientContext() {
return new DefaultOAuth2ClientContext(accessTokenRequest);
}
}
Debugging the creation of the oauth2ClientContext without #EnableRedisHttpSession shows that (as expected) the bean will be instantiated once per client session and stored in the HttpSession. This instance will then be reused to store the fetched OAuth2 bearerToken details in addition to storing the OAuth2 accessToken in Spring SecurityContext's org.springframework.security.core.Authentication.
However, once using #EnableRedisHttpSession, the oauth2ClientContext bean will be first created on the session creation but also later on (while still using the same client session). Debugging the Redis client session content confirms that oauth2ClientContext is not correctly being persisted by session creation:
Before we retrieve the OAuth2 bearerToken (NO SpringContext, NO scopedTarget.oauth2ClientContext):
~$ redis-cli hkeys "spring:session:sessions:17c5e80b-390c-4fd6-b5f9-a6f225dbe8ea"
1) "maxInactiveInterval"
2) "sessionAttr:org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN"
3) "lastAccessedTime"
4) "creationTime"
5) "sessionAttr:SPRING_SECURITY_SAVED_REQUEST"
After we retrieved the OAuth2 bearerToken (SpringContext persisted, but NO scopedTarget.oauth2ClientContext):
~$ redis-cli hkeys "spring:session:sessions:844ca2c4-ef2f-43eb-b867-ca6b88025c8b"
1) "sessionAttr:org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN"
2) "lastAccessedTime"
3) "creationTime"
4) "sessionAttr:SPRING_SECURITY_LAST_EXCEPTION"
5) "sessionAttr:SPRING_SECURITY_CONTEXT"
6) "maxInactiveInterval"
If we now try to access one of the configurer Zuul's routes (therefore requiring to call org.springframework.security.oauth2.client.DefaultOAuth2ClientContext#getAccessToken), another instance of oauth2ClientContext will be created (since not persisted in Redis, with a null AccessToken.
Funnily enough, this instance will later be persisted in Redis (but a null instance is persisted since the AccessToken is not re-asked for):
~$ redis-cli hkeys "spring:session:sessions:c7120835-6709-4c03-8d2c-98f830ed6104"
1) "sessionAttr:org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN"
2) "sessionAttr:SPRING_SECURITY_LAST_EXCEPTION"
3) "sessionAttr:scopedTarget.oauth2ClientContext"
4) "sessionAttr:SPRING_SECURITY_CONTEXT"
5) "maxInactiveInterval"
6) "creationTime"
7) "lastAccessedTime"
8) "sessionAttr:org.springframework.web.context.request.ServletRequestAttributes.DESTRUCTION_CALLBACK.scopedTarget.oauth2ClientContext"
Creating a Simple ScopedProxyMode.TARGET_CLASS Injected bean worked as expected however with the bean being persisted correctly in Redis.
public class HelloWorldService implements Serializable {
public HelloWorldService(){
System.out.println("HelloWorldService created");
}
private String name = "World";
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public String getHelloMessage() {
return "Hello " + this.name;
}
}
#Configuration
public class AppConfig {
private SecureRandom random = new SecureRandom();
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public HelloWorldService myHelloService(){
HelloWorldService s = new HelloWorldService();
String name = new BigInteger(130, random).toString(32);
System.out.println("name = " + name);
s.setName(name);
System.out.println("Resource HelloWorldService created = " + s);
return s;
}
}
Example
The described problem can be reproduced in #dave-syer example for an OAuth2 reverse proxy gateway by adding the following dependencies:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
as well as the #EnableRedisHttpSession annotation in UiApplication.
Question
Should we ignore org.springframework.cloud.security.oauth2.client.OAuth2ClientAutoConfiguration from AutoConfiguration and manually create a oauth2ClientContext with a different setup to enable spring-session persistence in Redis? If so, can you please provide an example?
Otherwise: how to persist oauth2ClientContext in Redis?
Many in advance to anyone reading this question an trying to help.
There's a known issue there (https://github.com/spring-projects/spring-session/issues/129 and https://github.com/spring-projects/spring-boot/issues/2637). You can work around it by adding a RequestContextFilter.
#dave-syer hint was correct.
I post here the configuration which can be used to setup the RequestContextFilter and enable spring-session persistence of spring-security-oauth objects. In case this can help someone...
#Configuration
public class RequestContextFilterConfiguration {
#Bean
#ConditionalOnMissingBean(RequestContextFilter.class)
public RequestContextFilter requestContextFilter() {
return new RequestContextFilter();
}
#Bean
public FilterRegistrationBean requestContextFilterChainRegistration(
#Qualifier("requestContextFilter") Filter securityFilter) {
FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
registration.setOrder(SessionRepositoryFilter.DEFAULT_ORDER + 1);
registration.setName("requestContextFilter");
return registration;
}
}
I came across this post and I have the exact same issue with some minor differences:
my application is not a Spring Boot application
I use JDBC persistence instead of Redis
However, and this might save some hours of future readers, the above solution also worked for me. Since I'm not using Spring Boot, I'll publish the solution here to be applied in a non Spring Boot application using web.xml configuration.
The "trick" is to define in the web.xml the RequestContextFilter. As far as my testing goes I have not seen any border effects of having both the request context filter living aside the request context listener.
What is important is the ordering of the filters. You need to define the filters in this order in your web.xml:
session repository filter
request context filter
security filter
So something like:
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
If this helps you save a few hours of digging into Stackoverflow and other web sites, it makes my day.
Is there a way I can restrict access to /monitoring url generated by Java-Melody plugin in Grails using Shiro roles?
Update: a little bit more details. It's no problem so secure most Grails ressources with shiro. But in case of the java melody plugin, it seems that the melody filter is executed before the shiro filter gets executed. This renders shiro useless.
There are some solutions which say that this might be fixed through a change in the web.xml, but this is not a quick hit and I (rdmueller) didn't manage to make it work yet. The web.xml plugin also seems to promise some help, but I don't want to add another plugin just to secure one plugin.
Some older statements found on the web state that this problem should be already solved through the usage of the loadAfter list in this file: https://github.com/javamelody/grails-melody-plugin/blob/master/GrailsMelodyGrailsPlugin.groovy - but it seems that this only worked for older versions of Grails.
Update2: In order to make it easier to propose a solution, I've create a Grails 2.2.4 sample: https://github.com/rdmueller/SO30739581
just clone the project, do a grailsw run-app and navigate to
http://localhost:8080/SO30739581/dbdoc
and you'll get a login screen via shiro. Navigate to
http://localhost:8080/SO30739581/monitoring
and you'll get the melody screen without being logged in :-(
I ended up doing so by making changes to web.xml for HTTP authentication. Add this to you web.config file.
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Monitoring</realm-name>
</login-config>
<security-role>
<role-name>monitoring</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>Monitoring</web-resource-name>
<url-pattern>/monitoring</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>monitoring</role-name>
</auth-constraint>
</security-constraint>
Then add a user and role to your tomcat-users.xml
<user username="yourusername" password="yourpassword" roles="monitoring"/>
I assume you're using Grails 2.x, you could hardcode it this way :
<!-- language: java-->
// grails-app/conf/MonitoringFilters.groovy
import org.apache.shiro.SecurityUtils
class MonitoringFilters {
def dependsOn = [ShiroSecurityFilters]
def filters = {
myMonitoringArea(uri: "/monitoring") {
before = {
SecurityUtils.subject.hasRole('ADMIN')
}
}
}
}
This is not a "quick hit", but the following approach should work with Shiro or whatever security framework your Grails app uses.
In web.xml, add the following elements above any existing <filter> elements:
<filter>
<filter-name>melodyFilter</filter-name>
<filter-class>com.your.package.MelodyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>melodyFilter</filter-name>
<url-pattern>/monitoring/*</url-pattern>
</filter-mapping>
This will call com.your.package.MelodyFilter any time the /monitoring/* url pattern is invoked.
Next, you'll need to create a MelodyFilter Java class in /src/java/com/your/package/MelodyFilter.java.
In the body of the doFilter method, you may call a Grails service method to perform any desired security checks, as follows:
package com.your.package;
import com.my.grails.app.MyService;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class MelodyFilter implements Filter {
#Override
public void destroy() { }
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String uri = ((HttpServletRequest)request).getRequestURI();
HttpSession session = ((HttpServletRequest)request).getSession(false);
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(session.getServletContext());
// replace MyService with your actual service
MyService myService = (MyService)ctx.getBean("myService");
// replace isUserAuthorized with your actual service method;
// session and uri params included to demonstrate how to pass them
// your argument list can be whatever your service method requires
boolean authorized = myService.isUserAuthorized(session, uri);
if (authorized) { chain.doFilter(request,response); }
else {
request.setAttribute("error", "User is not authorized to access " + uri);
request.getRequestDispatcher("/someController/someAction").forward(request, response);
}
}
#Override
public void init(FilterConfig filterConfig) throws ServletException { }
}
Then simply implement myService.isUserAuthorized() to perform whatever security checks you desire.
I have verified this technique works in Grails-2.3.6 with grails-melody:1.59.0
Just to list all available options:
the shiro-protect-any - plugin seems to work, but IMHO, it seems to be to be a bit too complicated and the plugin is "not fully tested" (says the author)...
As now web.xml will be generated dynamically, I'd need to add a simple filter class at the top of of it without writing or using a plugin.
What code would I need and where should I put it?
Or could I somehow get a template of what grails would generate for a web.xml and I could modify and override it?
I saw in the documentation
grails.war.resources = { stagingDir, args ->
copy(file: "grails-app/conf/custom-web.xml",
tofile: "${stagingDir}/WEB-INF/web.xml")
}
but first: would this function in application.yml?
and second: I'd still need an appropriate web.xml template to change...
Thanks!
From cfrick's suggestion, I tried this, (applies to grails 3)
Create a filter e.g. [grails-project]/src/main/java/hello/SimpleCORSFilter.java
(Create the 'java' directory, or create a SimpleCORSFilter.groovy in the [grails-project/src/main/groovy directory)
You can use the example in one of the spring guides.
package hello;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
#Component
public class SimpleCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
// response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
// I used this instead
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
Then in [grails-project]/grails-app/conf/spring/resources.groovy, add the filter, e.g.
import hello.SimpleCORSFilter
// Place your Spring DSL code here
beans = {
myFilter(SimpleCORSFilter)
}
Also, in the grails docs, to order your filter:
import hello.SimpleCORSFilter
import org.springframework.boot.context.embedded.FilterRegistrationBean
import org.springframework.core.Ordered
myFilter(FilterRegistrationBean) {
filter = bean(SimpleCORSFilter)
urlPatterns = ['/*']
order = Ordered.HIGHEST_PRECEDENCE
}
i know it's not the anwers to the actual question, but adding filters "springboot" style is just providing a #Bean for it. E.g. you can just put it in grails-app/conf/spring/resources.groovy. More details can be found at: How to add a filter class in Spring Boot?
Grails 3 embraces Springboot and with that came the good bye to XML based config (not saying it is forbidden, but its discouraged)
You can use install-templates command, which will copy templates to src/templates directory, there you will find web.xml template inside war directory, you can customize it
See http://www.grails.org/doc/latest/ref/Command%20Line/install-templates.html
I have a Java EE 6 War project containing the following:
An EJB declared as so (it's also a JAX-RS Service):
#Path("/booksList")
#Produces("application/json")
#Stateless
#LocalBean
#Local(BooksListEJB.class)
public class BooksListEJBImpl implements BooksListEJB
A WebComponent declared as so:
#WebServlet(urlPatterns="/initDbData")
public class DataInitListener extends HttpServlet {
#EJB
private BooksListEJB booksListEJB;
An empty beans.xml file in the WEB-INF folder
When I deploy it in WebLogic 12c, I get the following error:
<Warning> <weblogic.jaxrs.onwls.deploy.ejb.provider.EJBComponentProviderFactory> <BEA-000000> <An instance of EJB class com.shivandragon.jee6TestService.ejb.impl.BooksListEJBImpl could not be looked up using simple form name. Attempting to look up using the fully-qualified form name.
javax.naming.NameNotFoundException: While trying to look up comp/env/BooksListEJBImpl in /app/webapp/jee6Test-service-0.1-SNAPSHOT.war/2039754748.; remaining na
me 'comp/env/BooksListEJBImpl'
at weblogic.jndi.internal.BasicNamingNode.newNameNotFoundException(BasicNamingNode.java:1180)
at weblogic.jndi.internal.ApplicationNamingNode.lookup(ApplicationNamingNode.java:146)
at weblogic.jndi.internal.WLEventContextImpl.lookup(WLEventContextImpl.java:253)
at weblogic.jndi.internal.WLContextImpl.lookup(WLContextImpl.java:426)
at weblogic.jndi.factories.java.ReadOnlyContextWrapper.lookup(ReadOnlyContextWrapper.java:45)
Truncated. see log file for complete stacktrace
I've looked similar questions, and found the suggestion to add #ManagedBean to the servlet. Tried that but had the same error.
My question is:
Shouldn't this work, am I misusing some Java EE 6 directive/standard?
In EJB 3.1 have been added new Bean view - LocaBean. You can develop a bean without need implement any inerfaces. That beans view is "no-interface view", annotated with #LocalBean and injected by classname. There are beans that implemented some local interfaces and has "local view" and should be injected via local interface. In your code you mixed no-interface view bean and local view bean. You should delete the #LocalBean annotation as #Sam answered.
Updated
I test it on WebLogic Server 12.1.1.0.
Create a simple interface with one method:
package ejbrest;
public interface SessionEJBLocal {
public String hello();
}
Then create a EJB with the RESTful annotations:
package ejbrest;
// ... imports
#Path("/booksList")
#Produces("application/json")
#Stateless
#Local(SessionEJBLocal.class)
public class SessionEJBBean implements SessionEJBLocal {
public SessionEJBBean() {
}
#Override
#GET
public String hello() {
return "Hello, world";
}
}
The deployment descriptor, web.xml (you can see it does not have any servlet definitions):
<?xml version = '1.0' encoding = 'UTF-8'?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
</web-app>
You can create a servlet for the local bean injection demo:
package ejbrest;
// ... imports
#WebServlet(name = "DemoServlet", urlPatterns = { "/demoservlet" })
public class DemoServlet extends HttpServlet {
private static final String CONTENT_TYPE = "text/html; charset=UTF-8";
#EJB
private SessionEJBLocal ejb;
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType(CONTENT_TYPE);
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>DemoServlet</title></head>");
out.println("<body>");
out.println("<p>The servlet has received a GET. This is the reply: " +
ejb.hello() + "</p>");
out.println("</body></html>");
out.close();
}
}
After deployment you can try call your RESTful service by url:
http://[host]:[port]/[appcontext]/resources/booksList
Response:
Hello, world
Also, your demo servlet will be accessable by url:
http://[host]:[port]/[appcontext]/demoservlet
Response:
The servlet has received a GET. This is the reply:Hello, world
By default Oracle WebLogic Server use resources as link on Jersey servlet. Please read the official documentation for informs about all supported deployments variants.
I'm working on a Java EE (which I'm fairly new to) web application (JSF, JPA, EJB), and am in the process of incorporating PrettyFaces for human readable/bookmarkable URLs. I've been using a Filter for 2 reasons.
to make sure a user is logged in.
to wrap the call to filterChain.doFilter(...) in a transaction so JPA lazy loading works when generating views (for example, I can just set a Department object in the backing bean, and use #{backingBean.department.employees} to get the associated list of employees in the .xhmtl file).
Before incorporating PrettyFaces, I was using a url-pattern (in web.xml) of *.xhmtl (although the filter doesn't really need to run for the login page) for the Filter. With PrettyFaces, trying to specify a url-pattern for Filters seems to be a bit of a problem, mainly due to the lack of flexibility of the url-pattern rules (lack of support for regular expressions). Is there another way of accomplishing what I need with-out using Filters (and without having to duplicate code)?
Also, I know I can add a static portion to the beginning of the URL (like, /dept/#{deptName}/... and then use a Filter with a url-pattern of /dept/*, but I was hoping to just start with something like /#{deptName}/... (and using a url-pattern of /* runs the filter on everything, including images, javascript, css, etc.)
Basically, the filter has a transaction injected...
#Resource UserTransaction tx;
And does something like this.
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpRes = (HttpServletResponse) response;
HttpSession session = httpReq.getSession();
User currentUser = (User)session.getAttribute("userInSession");
if (currentUser == null) {
httpRes.sendRedirect("...") //redirect to LoginServlet
} else {
try {
tx.begin();
chain.doFilter(httpReq, httpRes);
}
catch (Exception e) { }
finally {
try {
tx.commit();
}
catch (Exception e) { }
}
}
I have a managed bean that is like this...
#ManagedBean
#RequestScoped
#URLMapping(
id="uutSerialNumber",
pattern="/#{uutSerialNumberController.programId}/uut/#{uutSerialNumberController.uutId}",
viewId="/uutSerialNumber.xhtml"
)
public class UutSerialNumberController {
#EJB private ProgramServiceBean programServiceBean;
#EJB private UutServiceBean uutServiceBean;
private Integer programId;
private Integer uutId;
private Program program;
private Uut uut;
#URLAction
public String loadData() {
program = programServiceBean.findByProgramId(programId);
uut = uutServiceBean.findUutByUutId(uutId);
return null;
}
//other stuff, setters/getters
}
In the view uutSerialNumber.xhmtl, I do something like this (which requires lazy-loading, unless I want to go to the trouble of manually pre-fetching collections in my uutServiceBean.findUutByUutId())...
<ul>
<c:forEach var="serialNumber item="#{uut.serialNumbers}>
<li>#{serialNumber.description}</li>
</c:forEach>
</ul>
Turns out I didn't have PrettyFaces configured correctly (doh!). It was a little confusing because in the PrettyFaces Reference Guide, it says that you don't need to edit web.xml if using Servlet 3.0 (which I am). But, doing the following solved my problem.
<!-- PrettyFaces Filter -->
<filter>
<filter-name>Pretty Filter</filter-name>
<filter-class>com.ocpsoft.pretty.PrettyFilter</filter-class>
</filter>
<!-- My own view Filter -->
<filter>
<filter-name>View Filter</filter-name>
<filter-class>com.jasoni.ViewFilter</filter-class>
</filter>
<!-- mappings -->
<filter-mapping>
<filter-name>Pretty Filter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<filter-mapping>
<filter-name>View Filter</filter-name>
<url-pattern>*.xhtml</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
With this, I'm able to run a filter on my views and use a Transaction View Pattern (similar to the one mentioned in Pro JPA 2, except using a Filter instead of a Serlvet, so lazy loading works with JPA), and also check that the user has a session going.
I cant see how using PrettyFaces may affect your filter - you can always get PrettyContext from HttpServletRequest and get all you need for processing url's, including regular expressions. As for JPA and lazy loading - this is quite a different story, I'd suggest to be more specific in this area and provide more details, code snippets, etc for us to be able to help.