Using Spring with Mojarra InjectionProvider
Are you using SpringBeanFacesELResolver to handle dependency injection on managed beans? It works nice, but your faces-config grows fast with this approach.
Two months ago, I posted about using Spring injection outside application context. I forgot to mention that the same trick can be used to replace ELResolver solution if you are using Mojarra as your JSF provider.
You only need to add a servlet context parameter:
<context-param> <param-name> com.sun.faces.injectionProvider </param-name> <param-value> mypkg.SpringJSFInjectionProvider </param-value> </context-param>
And the implementation of SpringJSFInjectionProvider is as simple as:
public class SpringJSFInjectionProvider implements InjectionProvider { private WebContainerInjectionProvider wcip = new WebContainerInjectionProvider(); @Override public void inject(Object bean) throws InjectionProviderException { FacesContextUtils .getWebApplicationContext( FacesContext.getCurrentInstance()) .getAutowireCapableBeanFactory() .autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false); wcip.inject(bean); } @Override public void invokePreDestroy(Object bean) throws InjectionProviderException { wcip.invokePreDestroy(bean); } @Override public void invokePostConstruct(Object bean) throws InjectionProviderException { wcip.invokePostConstruct(bean); } }
Adding this, you can shrink your faces-context, removing all "managed-property" tags. Your managed beans can look more JavaEE 5 after you change this:
private BO bo; public void setBo(BO bo) { this.bo = bo; }
To this:
@Resource(name="bean-on-appctx") private BO bo;
No setter, no ELResolver. This means less code to maintain! If you plan to migrate from Spring to EJB, this means you will only need to promote the BO to an stateless bean and change @Resource to @EJB!
Executable WAR files using Winstone and Maven
After a couple of years developing webapps, I'm starting to get problems to design a non-web UI. Not a big deal, since I only use Swing on personal projects. The biggest problem with web projects is the need of at least a servlet container. If you host a lot of projects, Tomcat, JBoss, Glassfish and Geronimo are good choices. But, if you need to host only one "web-container-only" project, Jetty or Winstone are way better.
I'm planning to use maven instead of ant for all of my projects, both personal and professional. It is easier to maintain and more IDE-portable. When I'm developing, I can use "maven jetty:run" to automatically compile and start Jetty (ctrl-c ends the server). The whole process is faster than a "ant dist / deploy / OOM / kill -9" when using Tomcat. You only need to add jetty plugin to your POM (inside "project > build > plugins"):
<plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty-plugin</artifactId> <configuration> <contextPath>/</contextPath> </configuration> </plugin>
This is useful to develop, but, when I want to create the "executable WAR", I prefer using Winstone - it's even lighter than Jetty. This means I need another plugin:
<plugin> <groupId>net.sf.alchim</groupId> <artifactId>winstone-maven-plugin</artifactId> </plugin>
Running "winstone:embed" will create a "xxx-standalone.jar" on "target" folder. This JAR can be run with "java -jar" and your application becames avaliable on "http://localhost:8080/". This plugin has a lot of configurations, and, using maven, you can configure it to run this goal on package phase. Read its manual for more information.
Password and confirmation with JSF
If you need to validate a field in JSF and the value is dependent of another field (like password/confirmation), BalusC posted a nice tutorial about it. You need to create a JSF Validator and add a
Spring injection outside application context
Are you using ApplicationContext (and friends) to create a single "spring context" for all your web application? If so, you are on the right way. And it is possible that you feel the need to inject properties where Spring isn't accessible (like a Servlet Filter). You can use this little trick:
public static void inject(ServletContext context, Object bean) { WebApplicationContext wCtx = WebApplicationContextUtils .getWebApplicationContext(context); if (wCtx != null) { wCtx.getAutowireCapableBeanFactory() .autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false); } }
Hibernate JPA and Enums
Assume you have a simple enum:
public enum Status { ACCEPTED, REJECTED, NOTREADDEN }
And your legacy database stores it as ordinal values (ACCEPTED = 0, REJECTED = 1, NOTREADDEN = 2). For reasons lost in time, DBA created the column as "varchar". If you want to use JPA, you will have a bad time using Hibernate:
java.lang.IllegalArgumentException: No enum const class mypkg.Status.1 java.lang.Enum.valueOf(Enum.java:196) org.hibernate.type.EnumType.nullSafeGet(EnumType.java:110)
After digging into EnumType.java, I noticed it is hardcoded "if-varchar-then-name-else-ordinal". I assume they never dealed with ancient, legacy databases.
If I switch to TopLink, it works as expected... One thumb down to Hibernate...
JAAS and Filters
Small tip of the day. If you define a servlet filter and container managed security on the same application, keep these in mind:
- If your filter is "url-mapped", it is executed BEFORE the security manager;
- If it is "servlet-mapped", it is executed AFTER the security manager.
I tested it when developing a filter to handle "user life cycle" (expired passwords, agreement signing, etc). If I map both filter and security to "/*", filter was run. If I map security to "/*" and filter to "Faces Servlet", it is not executed until authenticated (even when accessing "/faces/index.jsp").
Tomcat manager with SSO and a different Realm
Tomcat Manager's default configuration isn't pratical (nor secure): you must store your users and passwords in a plain XML file. Of course, you can change the "<Realm>" definition on "<Engine name='Catalina'>", but it has no effect if you need a host with Single Sign On.
Since Manager needs to share the same host with managed applications, you need to create a context.xml for your Manager:
<?xml version="1.0" encoding="UTF-8"?> <Context path="/manager" privileged="true"> <Realm className="my.secure.and.custom.Realm"/> </Context>
Put it on "conf/<engine>/<host>/manager.xml". The "privileged='true'" will prevent a "java.lang.SecurityException: Servlet of class org.apache.catalina.manager.HTMLManagerServlet is privileged and cannot be loaded by this web application".
You must change "webapps/manager/WEB-INF/web.xml" and replace "<role-name>manager</role-name>" with the name of the role you want.
MQSeries not closing connections
I got this error while connecting to MQSeries:
MQJE001: An MQException occurred: Completion Code 2, Reason 2009 MQJE016: MQ queue manager closed channel immediately during connect Closure reason = 2009
After some trial-and-error, I found the problem: MQ's InitialContext opens a connection, but only closes it if I close the IC itself:
ic.close();
Since my application creates a lot of instances of InitialContext, a lot of connections got leaked, giving that annoying error.
+1 to the IBM's hall-of-shame, section "useless error messages".
Changing HSQLDB in-process database default password
HSQLDB is not the kind of database I can use in enterprise systems (with terabytes of data), but it is specially useful if I need a simple RDBMS to small (or temporary) databases.
While trying it with Glassfish, I got a big problem: if you create an in-process database (using "jdbc:hsqldb:mem" or "jdbc:hsqldb:file" or "jdbc:hsqldb:res"), HSQLDB creates a default user named "SA" with an empty password. But Glassfish does not accept data sources without passwords (I get a "No PasswordCredential found").
To solve this, I grab the HSQLDB sources and changed the org.hsqldb.Database class. The "reopen" method has the following command:
if (isNew) { sessionManager.getSysSession().sqlExecuteDirectNoPreChecks( "CREATE USER SA PASSWORD \"\" ADMIN"); logger.synchLogForce() }
I just changed it to:
if (isNew) { sessionManager.getSysSession().sqlExecuteDirectNoPreChecks( "CREATE USER " + urlProperties.getProperty("user") + " PASSWORD \"" + urlProperties.getProperty("password") + "\" ADMIN"); logger.synchLogForce(); }
Compiling with "ant jar" (in build dir), the new hsqldb.jar will now allow you to create the user based on the credentials passed as login. But this will only work if you compile with a JDK 5, because a lot of methods required by JDBC in JDK 6 are implemented by throwing UnsupportedOperationException.
For some reason, Glassfish and HSQLDB with in-process only works with OpenJPA. I've tryed with Hibernate and TopLink, with really strange results. If I find a solution, I'll post later.
Java SCBCD 5.0 Certification
I am strudying for the SCBCD certification, and I realized there is almost no free material about it. I found this website and, IMHO it is an incredible source of information.
They also have some materials for other certifications (SCJP, SCWCD, etc), too, but I did not read them yet.