Introduction
I'm a JBoss user and so I tend to develop against and deploy on a JBoss server (JBoss 7.1 being awesome to the max).But you know, sometimes I look at what I have and I think to myself: do I really *need* to apply all those layers of complexity just to solve a simple problem? Think about EJBs; powerful stuff, but is it really necessary to apply it when you are building a simple webapp? Even if JEE6 makes it simple by allowing you to deploy EJBs in the war, I still don't think so. Do you really need container managed stuff? Other than a datasource: not really.
In stead of plowing ahead and bolting on everything that other smart people come up with, sometimes it is just a good thing to take a step back and return to the basics. I want to create a webapp, a simple war, do some simple JPA/Hibernate 4 database stuff and create some pages using JSF 2.1 and Primefaces (or whatever component library you fancy, if any). And I want to (hot-)deploy it on the simplest and in my opinion most stable Java servlet container of all - Apache Tomcat 7. Aha but I'm still a power user. I also want to use Maven and Eclipse. The plot thickens. Lets see what we can do...
Please be aware that I'm not aiming here to teach you how to create web applications; that is a subject that filled many books. I am not even going to make a weak attempt at it either, although I do my best to fill in as many blanks as possible. I assume you already know your stuff when it comes to web development, JSF, persistence and Maven. The main focus of this article is to bring everything together into a neat, manageable package. To do that I'm splitting the article up in three sections. First of all is the setup of tools, then we are going to get JSF up and running and something hot-deploying, finally we're going to add the database layer.
Setup tools
Of course you need to download and install Tomcat 7 and you need Eclipse. For both, download the latest releases. I'm assuming you are using at least Eclipse 3.7 SR1. We are going to be controlling Tomcat from within Eclipse, so no need to install it as a Windows service (assuming you are doing this on Windows). Just get the zip, unpack it and be almost done.To communicate with a database you need a JDBC driver; Java databases 101. It is Tomcat that will be managing the connection pool for us, so it is Tomcat that needs access to the driver. You cannot simply deploy it with your application, it needs to be on the server classpath. To do this, put the JDBC driver in the lib subdirectory of the Tomcat installation; easy and simple. In the examples I am using HSQLDB.
Since I'm a Maven dude, that is what I'll document. All you need to do is create a simple Maven project. No need for a multi-module setup, just create a single project of type 'war'. After creation there is always the dreaded dependency setup. For now lets add the bare minimum. For more help with Maven, my article on Maven might help. Also, my Article on using Maven from Eclipse.
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
This solves our logging needs. I use log4j which is perhaps a bit outdated; you could also simply use java.util.logging (JUL) and save yourself a dependency. I've tried to do that myself but I've had no success so far configuring that properly so I get something as simple as a dedicated log file for my own applications.
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>7.0.54</version>
<scope>provided</scope>
</dependency>
</dependencies>
These dependency allows us to compile servlet 3.0 code. Note that the servlet-api dependency is provided, as Tomcat itself already provides it for us of course.
The above dependencies will likely be the same for all web projects you do. Later we are going to add a couple more to deal with JSF and JPA. For now, lets wrap up with basic plugin configuration:
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<inherited>true</inherited>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
At this point you will want to refresh your project settings (right click project -> maven -> update project) so that the Eclipse settings match the Maven settings (Java 7 being used). You could of course also specify Java 1.8 if you're going to be running on Java 8.
RECAP: At this point you have
- installed Tomcat 7 or higher and Eclipse 3.7 or higher
- added a JDBC driver to tomcat/lib
- created an Eclipse workspace, a maven project with war packaging and added the above into the pom
- did you check the build path, especially if there are exclusions on the resources directories? There likely are - remove those exclusions or hot-deployment is not going to work right.
- during the setup stages, continuously examine the deployment assembly; this can turn into a mess when using a mavenized project for some reason
Config files
To properly setup the application based on the choices I've made we need a pile of configuration files. Lets tackle them one at a time.webapp/WEB-INF/web.xml
Main configuration file for a java webapp. Right now its quite bare, we'll be adding stuff to it later.
<?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">
<display-name>testapp</display-name>
</web-app>
Notice the servlet spec 3.0; Tomcat 7 provides that for us. If you would be targetting Tomcat 6 for some reason, you'd need to downgrade to servlet 2.5 (and Java 6).
resources/log4j.xml
To make log4j happy, we need to define some minimal configuration.
<?xml version="1.0" encoding="UTF-8" ?>
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="CONSOLE"/>
</appender>
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-d: %-5p [%8c](%F:%L) %x - %m%n"/>
</layout>
</appender>
<category name="org.hibernate">
<priority value="error"/>
</category>
<category name="com.yourpackage">
<priority value="debug"/>
</category>
<root>
<priority value="INFO" />
<appender-ref ref="ASYNC"/>
</root>
</log4j:configuration>
In the above config, replace 'com.yourpackage' with the root package of your own application. With the Hibernate filter in place the noise logging should be minimal. Note that this is aimed at development; when deploying to an actual server outside of your IDE you should add file logging to it. I refer to the log4j manual for more information.
That's it for the basic setup.
TO RECAP: at this point you will have the following files in your Mavenized project.
src/main/webapp/WEB-INF/web.xml
src/main/resources/log4j.xml
For the people who don't use Maven, there will be a summary of deployment setup at the end of the article.
Setting up hot-deployment
Before we get on to adding JSF and JPA to our little test application, you might want to go through the hot-deployment setup first. To make sure it works, lets add a very basic index.html to our webapp.
<html>
<body>
<h1>
Hello world</h1>
Hot deployment seems to be working.
</body>
</html>
Now it is time to take some snapshots and show how to setup Eclipse.First of all, we will want to create a server runtime. Choose window, preferences. In there, unfold Server and select Server runtimes. This is likely a virgin workspace, so no server runtimes will be there. Click Add to add a new one.
Simply select Apache Tomcat 7 and click next. In the next dialog, select the correct directory where you installed Tomcat 7. Click Finish to create the server runtime.
Not there yet. Close the preferences and open the servers view (window, show view, other and in the dialog select server and then servers). In the servers view, right click and select new, server.
Make sure that Tomcat 7 is again selected; Eclipse will automatically setup the runtime you just created. Click next. In the resulting dialog, make sure that your webapp is in the configured list so it will be deployed to the server. Then click finish to create the server instance; you'll notice it in the servers view and you'll also notice that your application is going to be published to the server. Hurray!
Depending on the version of Java you use, it might be a good idea to give Tomcat some more heap space; you can do that by double clicking on the server instance in the server view; an editor window opens up where you can provide fine-grained configuration for the instance. One such configuration is the launch configuration, which you can change by clicking Open launch configuration.
In the launch configuration, select the arguments tab which shows all the java properties being set. We are going to add a new one, -Xmx512m.
That gives the server a lot more breathing space should it be necessary; you can also put a lower value; during development you don't want to give yourself too much breathing space. The higher the value you set, the less chance you'll have of catching heap space related problems during development. Note that in the arguments, take good note of the wtp.deploy property being set in the launch configuration.
At this point you're ready to try it! You can try publishing the application. This will make sure that the application is deployed to the server. Of course you want to check that Eclipse is doing it right, so open up the tomcat/webapps directory and... what the heck? Nothing is deployed there!
Remember that I told you to take note of that property in the launch configuration. That directory is where Eclipse deploys the application in fact. It is going to be a path like: workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps. Here you'll find the deployed files. Don't meddle with these yourself, whatever you want to do: do it from Eclipse or else the published state in Eclipse may go out of sync with the filesystem. Nothing that a good clean of the project cannot fix though. In case of trouble it is good to know the location of these files so you can check just what the heck Eclipse deployed and what it didn't.
Change the deployment location
You may want Eclipse to deploy the files to a somewhat less hard to find location; I certainly do. We can configure it, but before Eclipse will allow you to to that you will need to remove your project from the list of deployables under your server instance. Right click the project name and remove it. Then, publish the server. At this point when you double click the server instance, you will be allowed to make changes to the deployment location (note: when I tried this on Eclipse Luna, I did in fact not need to remove the application from the server first).This selection will move the deployment directory into the tomcat installation directory; much more manageable. You can choose to use the webapps folder, but you may want to just keep the one that Eclipse picked for you; it makes no difference. One thing you have to be aware of: Eclipse will change the server configuration to make this work (backups are created). That is a bit unfortunate as you can't re-use the same Tomcat instance to deploy multiple applications to. I would create a unique copy of Tomcat per application you're developing concurrently.
Whatever the case, after changing the directory you will want to make your application deployable again. You do this by simply dragging the project node in your package explorer to the server instance in the servers view. The project will reappear in the list of deployables.
Fixing the deployment settings
We are going to return to the server view one more time, this time to not look at the server settings but at the settings of the application we're deploying. Double click on the server (make sure the application is going to be deployed to it) and then click on that Modules tab hidden away at the bottom.Here we can change two things;
- The context name; this influence the URL under which we will be able to reach our application on our development localhost environment
- If the webapp is reloadable or not
As said, Eclipse takes over the Tomcat installation and these are the files it manages for your benefit. One of these files is of interest to us at this point: the server.xml file. Open it up. If you scroll all the way to the bottom you'll find an XML element called <Context> which contains exactly the information we just input in the server dialog. Now you know where to look to validate that your settings will be correct when you start the server.
I repeat: reloadable should be false at this point.
There just has to be something wrong
Getting a project to hot-deploy properly can be a bit fiddly. Recurring problems I have is that Eclipse wants to hot-deploy test resources and wants to deploy web resources to both the root and the classes folder for some reason. When things don't go right, do a few basic things.- make sure the Eclipse project is synced with the Maven poms (m2eclipse tends to notify you when the project is not through a build error)
- clean the project and do a manual full publish
- check the build path to make sure there are no exclusions
- check the deployment assembly; make sure no test resources are deployed and that all the elements point to the proper deployment path (and exist only once!)
- perform a clean on the server instance
Note that making changes to the build path tends to also result in automatic changes being made to the deployment assembly; make sure the two are always synced.
If all else fails, check, check and triple check all configuration files, the Maven poms, the Eclipse facets, the deployment assembly and the build path settings.Whatever problem you have, it originates from the build path and the deployment assembly, but the problem might start in a resource which influences those elements (such as a pom). Whatever the problem, use the deployed application as a guideline to figure out what is wrong. Check the files that Eclipse has copied to the Tomcat web application directory (you should have configured which one that is by now) and see what files are there. Which ones are missing? Which ones are in the wrong place? Which ones should not be there? The Tomcat documentation can guide you.
Start the server
At this point, click on the debug icon to start the server. Like when using JBoss, you can easily debug and hot-swap your code when deploying to Tomcat. Tomcat should output some basic logging to the Eclipse console, which is nearly empty right now because we're not deploying much yet. At this point, you can navigate to http://localhost:8080/testapp (or whatever context name you configured earlier) and our basic index page should pop up. Now we're ready to bolt on more stuff, beginning with JSF.Setting up JSF 2
To work with JSF 2 you need to add only one dependency to your Maven pom:
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.faces</artifactId>
<version>2.1.29</version>
</dependency>
Not provided, we're deploying this baby with our webapp because its Tomcat, which does not provide JSF out of the box. When you're deploying on a full JEE application server you will
want to mark this dependency as provided because JSF 2.X is already going to be provided by the container itself.
Plain JSF is likely not enough for you though, you will probably want to add one of the extension frameworks. In my case, I add Primefaces.
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>4.0</version>
</dependency>
Primefaces is nice and simple yet powerful and feature rich, and since version 4 it is also available through Maven Central so you do not need any external repository anymore.JSF 2 - web.xml changes
JSF needs some basic setup in the web.xml so that it can start servicing our Facelets XHTML pages:
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<!-- primefaces -->
<!-- If you want to use a different theme, configure it like this
<context-param>
<param-name>primefaces.THEME</param-name>
<param-value>aristo</param-value>
</context-param>
-->
This will setup the facelets servlet to process .xhtml files only. Note the VALUES_AS_NULL property. This setting will force that submitted values that are empty do not become empty strings but in stead are null. Lo and behold when you try that on Tomcat, it seems like the setting isn't doing anything! That's because Tomcat has a special EL implementation that also needs to be configured. We need to add another startup parameter to the server launch configuration (remember? Where you set the maximum heap space):-Dorg.apache.el.parser.COERCE_TO_ZERO=false
The combination of those two setup steps is what makes it happen under Tomcat 7.
JSF 2 - webapp/WEB-INF/faces-config.xml file
This file has become mostly obsolete with JSF 2 and up, but it is still best to provide it even if it is empty. Some servers keep an eye on it to know when something needs to be redeployed - Tomcat is one of them. And you still need it if you need to setup very specific navigation rules.
<?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">
</faces-config>
JSF 2 - getting something to deploy
That's the setup you need to get to work with JSF. To prove that it works we need some basic code. A very basic facelets template (put it in the webapps folder of your project):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Testapp</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</h:head>
<h:body>
<h:outputStylesheet target="head" library="css" name="main.css" />
<div id="content">
<div id="maincontent">
<ui:insert name="content">Page body here</ui:insert>
</div>
</div>
</h:body>
</html>
One thing you may notice (if you have been using JSF for longer than today) is that the outputStylesheet is in the body tag, not the head tag. This is because I added an extension framework (Primefaces) into the mix, which adds its own stylesheets. By putting the outputStylesheet in the body tag, this enforces that your own stylesheets are always added last to the page, allowing you to override styles of the framework should you be so inclined.
This uses JSF 2 resource management; save the main.css file as webapp/resources/css/main.css so JSF can find it.
Finally, lets define an index.xhtml file:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
template="template.xhtml">
<ui:define name="content">
#{index.hello}
</ui:define>
</ui:composition>
And we need a backing bean to go with that, just to be sure that it all works:
@ManagedBean
public class Index {
public Index(){
}
public String getHello(){
return "hello";
}
}
At this point you should remove the index.html file that you created earlier, our new index.xhtml replaces it. Check that all the new files are deployed (and the index.html you removed is no longer
deployed) and then (re)start the server. At this point the startup logging should provide a hint that JSF is in fact present:
Initializing Mojarra 2.1.29 (SNAPSHOT 20120206) for context '/testapp'
Good, JSF is initializing things. This line tells you under what context name Eclipse is deploying the application which should be the context name you configured earlier. At this point navigate to http://localhost:8080/testapp and if everything is correct, you'll be greeted by the hello message from the backing bean.
TO RECAP: at this point you have the following in your project:
src/main/webapp/WEB-INF/web.xml (modified)
src/main/webapp/WEB-INF/faces-config.xml
src/main/webapp/index.xhtml
src/main/webapp/template.xhtml
src/main/webapp/resources/css/main.css
src/main/resources/log4j.xml
JPA 2 - setup
To be able to use JPA in our webapp we'll need to add a persistence provider to our maven pom. We'll be using Hibernate.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.1.Final</version>
</dependency>
This adds not only Hibernate 4.3 (check what the latest version is though) to the dependencies, but also the JPA API. Oracle does not provide an API dependency for JPA 2 (at the time of writing at least), but luckily most persistence providers do provide their own. Hibernate is not in Maven Central, you'll need to add the JBoss Nexus to your Maven configuration. My Maven article has more details on that.
JPA 2 - datasource setup
Before JPA can do anything, we need to provide a datasource. For testing purposes you don't need to install a real database, you can setup an in-memory one like H2 or HSQL. Because it is my old friend I'll be using HSQL here, but you can easily substitute it for H2 if you so please. First of all we need to add a datasource to our development setup. To do that we are going to modify another file that Eclipse is managing for us: the context.xml file (remember: its in the Servers project that Eclipse created when we setup Tomcat):
<Context>
<Resource name="jdbc/TestappDS" auth="Container" type="javax.sql.DataSource"
driverClassName="org.hsqldb.jdbcDriver" url="jdbc:hsqldb:mem:testapp"
username="sa" password="" maxActive="20" maxIdle="10" maxWait="-1" />
...
</Context>
Here we define a datasource/connection pool "jdbc/TestappDS", which is a simple HSQLDB in memory database. Tomcat is going to manage this datasource for us, so put the hsqldb driver jar in the tomcat/lib folder; I'll make that 100% specific: so it is NOT enough to add the JDBC driver jar to your web application, Tomcat itself needs access to it hence it goes in the Tomcat library folder. The above configuration is still not enough, we also need to hook into into our web.xml file to actually make the datasource available to our application:
<!-- tomcat -->
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/TestappDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
JPA 2 - resources/META-INF/persistence.xml file
Now that we have our datasource we can setup JPA and Hibernate. To make it nice and confusing, this file does not go into the META-INF folder of your webapp - it goes into a META-INF folder that is on the classpath. In maven terms that means that you create the file in src/main/resources/META-INF/persistence.xml so it ends up in webapp/classes/META-INF/persistence.xml.
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">
<persistence-unit name="testapp" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>java:/comp/env/jdbc/TestappDS</non-jta-data-source>
<properties>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.jdbc.batch_size" value="20" />
<property name="hibernate.cache.use_query_cache" value="false" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.cache.use_second_level_cache" value="false"/>
</properties>
</persistence-unit>
</persistence>
Tomcat is not a JEE container, so we are not using JTA or container managed transactions. In stead it is a simple RESOURCE_LOCAL setup. Fortunately, we don't need to declare any entities here; they are still discovered during the setup of the EntityManagerFactory. Hibernate is setup here to auto-generate the database from the entities, which is the most convenient way to work with an in-memory database.
Also note the JNDI name of the datasource. That is as it is documented in the Tomcat datasource HOW-TO.
TO RECAP: right now you'll have the following in your project:
Servers/context.xml (modified)
src/main/webapp/WEB-INF/web.xml (modified)
src/main/webapp/WEB-INF/faces-config.xml
src/main/webapp/index.xhtml
src/main/webapp/template.xhtml
src/main/webapp/resources/css/main.css
src/main/resources/META-INF/persistence.xml
src/main/resources/log4j.xml
And you put a JDBC driver jar of whatever database you are going to use in the tomcat/lib folder.
It might be interesting for people who do not use Maven to know where all the configuration files need to go in the war:
What | Where |
---|---|
web.xml | WEB-INF |
faces-config.xml | WEB-INF |
persistence.xml | WEB-INF/classes/META-INF |
log4j.xml | WEB-INF/classes |
JPA 2 - Managing entities and transactions
Normally the EJB container would take care of doing such things as maintaining transactions or providing a valid EntityManager to our code. But we have no EJB container here, we need to do some things ourselves. But I still like to stick as close as I can to the "EJB" way, so I provide here my solution to the problem: the Dba class. It is really quite simple:
package com.yourapp.service;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** some ugly code to make transaction management nice and dumb.
*
*/
public class Dba {
private static volatile boolean initialized = false;
private static Boolean lock = new Boolean(true);
private static EntityManagerFactory emf = null;
protected Logger logger = LoggerFactory.getLogger(getClass());
private EntityManager outer;
/**
* open Dba and also start a transaction
*/
public Dba() {
this(false);
}
/**
* open Dba; if readonly no JPA transaction is actually started, meaning you will have no persistence store. You can still persist stuff, but the entities won't become managed.
*/
public Dba(boolean readOnly) {
initialize();
openEm(readOnly);
}
public void openEm(boolean readOnly) {
if (outer != null) {
return;
}
outer = emf.createEntityManager();
if (readOnly == false) {
outer.getTransaction().begin();
}
}
/** Get the outer transaction; an active transaction must already exist
for this to succeed.
*/
public EntityManager getActiveEm(){
if(outer == null){
throw new IllegalStateException("No transaction was active!");
}
return outer;
}
/** Close the entity manager, properly committing or rolling back a transaction if one is still active.
*/
public void closeEm(){
if(outer == null){
return;
}
try{
if(outer.getTransaction().isActive()){
if(outer.getTransaction().getRollbackOnly()){
outer.getTransaction().rollback();
} else {
outer.getTransaction().commit();
}
}
} finally {
outer.close();
outer = null;
}
}
/** Mark the transaction as rollback only, if there is an active transaction to begin with.
*/
public void markRollback(){
if(outer != null){
outer.getTransaction().setRollbackOnly();
}
}
public boolean isRollbackOnly(){
return outer != null && outer.getTransaction().getRollbackOnly();
}
// thread safe way to initialize the entity manager factory.
private void initialize(){
if(initialized){
return;
}
synchronized(lock){
if(initialized){
return;
}
initialized = true;
try{
emf = Persistence.createEntityManagerFactory("testapp");
} catch(Throwable t){
logger.error("Failed to setup persistence unit!", t);
return;
}
}
}
}
This incredibly simple class takes care of two basic things:
- using one time on-demand initialization, it sets up the EntityManagerFactory
- it provides a way to create, obtain and properly close an EntityManager
- the Dba class provides an explicit "readOnly" mode which you should use when you are not going to persist any changes (effectively no JPA transaction is created in readonly mode)
- it provides a way for you to trigger a rollback
The ugly init code makes it pretty much self-sufficient, no need to trigger the setup using something like a context listener or whatever.
The usage of the class is really simple; you simply create an instance and from that moment on you use that one instance to manage a transaction. You may need to use the same transaction in multiple methods (DAO, service, whatever); I manage that by simply passing along the Dba object to the service methods. Careful usage of openEm()/closeEm() and getActiveEm() can prevent mistakes here. Example:
// high level service method, or perhaps even code in a JSF backing bean
public class doThingsAndStuff(){
// open transaction
Dba dba = new Dba();
try{
// createUser 'adopts' the transaction
UserDao.createUser(dba, "Someone", "some1", "Some1");
} finally {
// 100% sure that the transaction and entity manager will be closed
dba.closeEm();
}
}
public class UserDao {
// low level DAO method in UserDao, expecting a transaction to already exist
public static User createUser(Dba dba, String name, String username, String password){
EntityManager em = dba.getActiveEm();
User user = new User(name, username, password);
em.persist(user);
return user;
}
}
The Dba class basically gives you the power that Bean Managed Transactions would give you when using EJB technology. You can start and close transactions at your leasure. To make a method adopt a running transaction, simply pass along the Dba object that is managing the transaction and be sure to use getActiveEm() to be 100% positive that the entity manager/transaction already existed. This is important because this way you can make dumb assumptions in your code; createUser() assumes that some other piece of code is responsible for closing the entity manager part of the Dba object that is passed to it. Now say that you make a mistake and you don't actually start a transaction before calling createUser(); then getActiveEm() will simply fail. Easy peasy.
Want to trigger a rollback? As you can see the closeEm() method provides for that; the responsibility you have is to mark the transaction as rollback only through the appropriate Dba method. The easiest way to do that is at the point where you deal with your exceptions. If that is "above" the layer of code that created the Dba object, you'll have to catch the exception, mark the transaction as rollback only and then rethrow the exception.
To minimize the strain on resources, you should only open a EntityManager transaction when you actually need one; which means you will be making changes that need to be persisted, or rolled back. When you only fetch data from the database, you don't need a transaction and thus you should not open one. Dba provides for this by allowing you to open it in readOnly mode.
public List<User> getAllUsers(){
Dba dba = new Dba(true); // open in readOnly mode
try{
return dba.getActiveEm().createQuery("from User u order by u.name").getResultList();
} finally {
dba.closeEm();
}
}
Almost cannot be made more lazy IMO :) You could change the design of Dba to default to readonly in the no-arg constructor; I leave that up to your own discretion.
If all that seems too complicated to you (or perhaps makes your eyes bleed), I'm sure you can come up with an alternative design that works for you personally. You have to find the balance between flexibility, robustness (which also includes catching mistakes you make) and ease of development. Ease of development is not only about minimizing code - it is also about maximizing the opportunity to catch mistakes. But before you pass judgement, I do ask you that you glance over the adding utility methods to Dba paragraph first.
This is by no means the only possibility you have of working with an EntityManager object in a web environment (for example there are inversion of control / bean injection frameworks such as Spring or Google Guice). But why bother when you can do it with a basic (stateless) application design and simply passing along an object?
Simple = better.
Then you get the discussion of how you would manage controller classes. Take the above example; you can distill from the code that UserDao.createUser is a static method. From your teachers to books to your colleagues, it will probably have been hammered into your head that you should avoid static methods and variables where you can. And those people and books are right, you should.
But this type of code you want to keep as stateless as possible, because stateless code is by definition thread safe, works in load balanced environments and there is no avoiding multithreading when you are doing a web application; there are going to be multiple users hammering on buttons at the same time. So I pose the question: why NOT use static methods in your controller classes? You could create an instance of the controller and then call its methods, but you could simply make the controller methods static and not be bothered with having to create an instance and basically be guaranteed that you are not going to slip in code that keeps state.
It is a very personal thing. I certainly don't mind creating static controllers to save me time and effort (and possible the creation of many short-lived objects). When you look at frameworks such as Play framework, it follows exactly the same design.
Simple = better.
JPA 2 - Beware of nested transactions
Using the Dba class it is quite easy to create multiple entity managers and have transactions running in parallel on the same thread. This is however not as fail-proof as it may seem as you may run into the issue of deadlock. The database you are connecting to, even an in-memory one like HSQL or H2, will likely employ table and/or row locking. When firing off multiple nested transactions you may inadvertently try to persist stuff to the same table at the same time, which may just result in a table lock wreaking havoc.I ran into this puppy myself when using HSQLDB - the thread just hangs when you trigger a table lock. By switching in H2 I finally figured out what was going on (because H2 sets a lock timeout by default).
My remedy: don't use nested transactions. Just don't do it. You want to simplify your code layer; the transaction management should follow suit. Sure open and close the transaction as you see fit, but don't go making it any more difficult than it should be; try to keep one transaction active to one thread. Otherwise you will have to start to deal with the complexities of such an environment.
To maintain this, follow another simple rule: don't start a transaction in your backing bean. Only start (and cleanup) transactions in your controller methods. From there you can easily propagate the entity manager as I've already demonstrated; by simply passing it along as a parameter.
JPA 2 - Adding utility functions to Dba
I find the EntityManager interface a bit... verbose. There is more typing going on than is really necessary. Luckily with our Dba class as the base, we can add some wrapper code around EntityManager and Query to seriously reduce the typing involved.Lets start with Query, which we will wrap in a class called QWrap:
public class QWrap {
private Query q;
public QWrap(Query q){
this.q = q;
}
public QWrap par(String pname, Object v){
q.setParameter(pname, v);
return this;
}
@SuppressWarnings("unchecked")
public <T> T single(Class<T> clazz){
try{
return (T) q.getSingleResult();
} catch(NoResultException nre){
return null;
}
}
@SuppressWarnings("unchecked")
public <T> List<T> multi(Class<T> clazz){
return (List<T>) q.getResultList();
}
}
Nothing too shocking; basically these methods are just short-hand versions of the Query methods you will probably use often. You can apply the same trick to other methods you tend to use. Now we can plug that into the Dba class:
public QWrap query(String q){
if(outer == null){
throw new IllegalStateException("Creating a query when there is no active transaction!");
}
return new QWrap(outer.createQuery(q));
}
With a simple call to query() we will now obtain our QWrap object. Lets see it in action:
Dba dba = new Dba(true);
try{
List<Person> people = dba.query("select p from Person p where p.address.city=:city").par("city", "Washington").multi(Person.class);
} finally {
dba.closeEm();
}
Neat huh? Note the abuse of generics here to basically get around having to do typecasts. But thats not all we can do. With a few more utility methods we can save even more typing. Lets begin by laying down a convention: all our entities shall have an auto incrementing ID column (using whatever strategy your DBMS provides). Using that, we can slap an interface on our entities:
public interface IdAcc {
public long getId();
}
This little interface is going to save us plenty of boilerplate code. You simply apply it to all your entities. With this baby in our toolbox, we can make a save() method that does a persist() or a merge() when required and returns the managed instance (if not in readonly mode):
public <T extends IdAcc> T save(T obj){
if(outer == null){
throw new IllegalStateException("Creating a query when there is no active transaction!");
}
if(obj.getId() > 0L){
return outer.merge(obj);
} else {
outer.persist(obj);
return obj;
}
}
I tend to avoid merge() myself if I can help it, but especially in a web application it can sometimes save plenty of time to just have JSF dump the changed values into an entity object and then merge it. In any case using the save() method you cannot only do that, you can easily create forms which can be reused for adding and updating data. More! Lets define a method to fetch an entity by ID and one to 'refresh' an entity, which is the act of making a detached entity attached again:
public <T> T getById(Class<T> clz, long id){
if(outer == null){
throw new IllegalStateException("No transaction was active!");
}
return outer.find(clz, id);
}
@SuppressWarnings("unchecked")
public <T extends IdAcc> T refresh(T obj){
if(outer == null){
throw new IllegalStateException("No transaction was active!");
}
return (T) outer.find(obj.getClass(), obj.getId());
}
The getById() method isn't too special; its just a thin wrapper around the find method of the EntityManager. The refresh() method is incredibly useful however as you can just pass the entity object to it, and the method does the rest. The typecast in refresh() is necessary because of the added IdAcc interface. Not that you'll notice it is there in your calling code.
User user = dba.getById(User.class, userId);
user = dba.refresh(user);
This is just a little bag of tricks; I'm sure based on the ones I've provided you can add quite a deal more.
JPA 2 - Bootstrapping database data
I already discussed how to bootstrap a prototype web application's database here; I would give that a read if you want more information. For now, lets create a simple class in which we can insert initial data:
public class AppConfig {
private static volatile boolean initialized = false;
private static Boolean lock = new Boolean(true);
protected Logger logger = LoggerFactory.getLogger(getClass());
@SuppressWarnings("unchecked")
public static List<User> getAllUsers(Dba dba){
load();
return dba.getActiveEm().createQuery("select u from User u order by u.name").getResultList();
}
private static void load(){
if(initialized){
return;
}
synchronized(lock){
if(initialized){
return;
}
initialized = true;
Dba dba = new Dba();
try{
EntityManager em = dba.getEm();
// insert all test data here
em.persist(new User("testuser", "test1", "Test1"));
// more init here
logger.info("AppConfig initialized succesfully.");
} catch(Throwable t){
logger.error("Failed to setup persistence unit!", t);
} finally {
dba.closeEm();
}
}
}
}
Of course this code is only necessary when you use an in-memory database for prototyping or testing purposes; if you use a physical database then you don't need the AppConfig class at all.
The load() method, much like the Dba class, does an on-demand one time initialization. Reloading the webapp may cause this to happen a second time though, so you might want to build in a check if data already exists in the database to prevent data from inserted multiple times.
In this prototyping setup, it is your job to make sure that load() is called when it is required. This should happen automatically when you make the AppConfig class responsible for providing important data. User objects are a prime example, basically one of the first things you'll do is login most likely so that will trigger the database to be initialized very early on. You could also trigger it to happen in a context listener; then you don't need to have code in place that ensures it happens.
Note that when load() is invoked for the first time, that will be the moment where JPA is setup and the database is generated. So you won't know if you setup all that stuff correctly until you actually trigger your application to need the database.
JPA 2 - Getting something to deploy
So before we get to deploying the app and seeing if it works, we need some minimal stuff to make things happen. We at least need an entity to see that the database is being built, but we also need some bootstrapping code to get the database logic to initialize.Let me copy a slimmed down version of the User entity from my prototype article:
@Entity
public class User implements Serializable {
@Id
@GeneratedValue
private long id;
private String name;
private String username;
private String password;
public User(){
}
public User(String name, String username, String password, ){
this.name = name;
this.username = username;
this.password = password;
}
// getters and setters here...
}
You can put it in any package you want, the class will be found automatically as soon as we cause the EntityManagerFactory to be created.
Now to get the Dba class to initialize, we need to use it at least once. Lets add an init method to our JSF backing bean to see that happening quickly:
@ManagedBean
@ViewScoped
public class Index {
public Index(){
}
@PostConstruct
public void init(){
// breaking my own rule: starting a transaction in the backing bean. This is only for demonstration purposes however.
Dba dba = new Dba();
try{
// some stupid code that triggers the database to be created
AppConfig.getAllUsers(dba);
} finally {
// cleanup the transaction
dba.closeEm();
}
}
public String getHello(){
return "hello";
}
}
The PostConstruct method is abused here to bootstrap our database, just so that you can see it happening. Like I stated before you don't want to ever see the Dba class being used in a backing bean so some sort of Controller method would normally be called here. But I don't want to litter the article with too many pieces of fake code.
As soon as you deploy the application and start the server, nothing special will happen. But as soon as you navigate to the application's index page, the call to Dba will be triggered which will in turn cause the database to be initialized (you'll notice how tables will be created in the logging) and the data bootstrapping code to be executed.
And that's basically it - setting up a project for JSF 2 and JPA 2 to deploy on Tomcat 7, using Eclipse as the IDE. At this point you'll be ready to write some code. The remainder of this article will deal with annoyances and troubles you may run into from this moment on.
Dealing with multiple environments (dev, acceptance, production)
When you develop an application, at one point you will want to publish it to somewhere that is not your own development computer; be it a testing environment or live production. The tricky part about that is that each environment will require certain settings to be entirely different; most notably the datasource will need to point to a completely different database. Fortunately because we use Tomcat, that is relatively easy to manage and Eclipse has already helped us a bunch too; what you do is utilize the context.xml file. We have ours in our development environment neatly tucked away into that Servers project. This file is not part of our project and so will never ever be deployed with the application to any other server.But as said in the previous section, we don't want to put application specific configuration in the global context.xml file or else we'll have difficulties deploying multiple applications at the same time. Instead tomcat allows us to specify a specific configuration file per-application. You put this file in a slightly difficult to formulate directory inside the tomcat_dir/conf directory; the format is:
tomcat_dir/conf/Catalina/HOSTNAME/WARNAME.xmlSo when deploying on the localhost and the war name is testapp.war, the directory and file would be:
tomcat_dir/conf/Catalina/localhost/testapp.xmlInside this file we can then put the information we put in the context.xml in our development setup:
<Context>
<Resource bla bla bla/>
</Context>
You can try to do this in your development environment, but you'll find that Eclipse will throw away your file again. As I said - it has taken control over your Tomcat installation. Hence my suggestion to put it in the global context.xml file instead.Now for a bit of a fun addition, what if we want to know from within the application code if we are running in a development environment or not? That might sound like a design smell, but I for one tend to allow certain things in a development environment that I do not allow in any other environment to expedite the development process; think of relaxing security measures and such. We can facilitate something like that quite easily by defining a property in the JNDI context which we can interrogate from the code. In the global context.xml file (that file in the Servers project) you can add this inside the context element:
<Environment name="DEPLOYMENT" value="DEV" type="java.lang.String" override="false"/>
And then in the code we can do this:
Context initCtx = new InitialContext();
String env = (String) initCtx.lookup("java:comp/env/DEPLOYMENT");
boolean dev = "DEV".equals(env);
On your test/acceptance/production server you then simply do NOT define this element and the boolean variable will end up as false. Easy!
You could add any properties you want of course; if you want them to be global add them to the context.xml, if you want them to be local to one application only, then add them to the application specific xml file.
Tomcat 7: Dealing with republishing
The Java debugger has a neat hotswap function where code changes can be "uploaded" to a running JVM instance, effectively allowing you to change code of a webapp as you have it open in your browser. Since this is a feature of the JVM debugger, it is only actually activated when you start the server in debug mode from Eclipse. This works up to a certain point of course; you cannot make changes to the class structure for example without a full webapp reload being needed.However when you try to make a change to a class or resource while deploying to Tomcat, you may run into the problem that the entire web application reloads (but only if you did not follow my instructions so far)! Not only is that highly annoying and time consuming, but it will quickly make you run into the dreaded "out of permgen space" problem (for which there may be some steps to attack it; check the comments of this article). This problem was especially apparent for me as I bundled Richfaces when starting out with this article; two redeploys is all it took before I had to reboot the server.
Fortunately, this has a very easy fix; by simply telling Tomcat it should not automatically reload the application. This is that "reloadable" property I told you to change all the way back in the section titled fixing the deployment settings. At this point if you still had it on simply disable it, republish the server and try again; you'll find that the webapp no longer redeploys, but the changes you make, even changes to for example a JSF backing bean method, are still automatically visible. Don't forget to hit reload in the browser of course ;)
Eclipse: Web resources not hot-deploying?
I ran into the issue where using this hot-deployment scheme, classes were being properly redeployed but web resources in for example src/main/webapp or src/main/resources were not, even though those directories was part of the deployment assembly! After struggling with it for a long time I figured out that I screwed up the hot-deployment settings; I had set the server publishing settings to never publish automatically; by default that should be automatically publish when resources change. Check that Eclipse is allowed to publish. When you make a change, don't forget to set the reloadable property of the web application to false as described above.Final thoughts: adapt to your needs
Of course JSF is only an example; you can substitute any web framework you want. You could yank out Primefaces and use vanilla JSF or another addon framework (Icefaces, Richfaces). What I wanted to do was create an environment that is very close to an enterprise application but without all the added weight to it. You don't need an application server, you don't need an EJB layer, you don't need an application built out of multiple tiers, layers, boilerplate code and other madness that generally makes Ruby or PHP scripters snigger at you behind your back.Simple = better. Also with Java as the base it can be simple... with some major effort to get it to that point. Always those dreaded configuration files, we don't seem to be able to ever be rid of them no matter how hard people try. I hope this article can help you wade through the boring setup stuff and get you to the juicy meaty side of web development; actually creating something and seeing it appear in your browser!
Excellent article on JSF and JPA on Tomcat! Just what I was looking for. Thanks for the simple step by step process and highlighting what is really important. Simple is Better!
ReplyDeleteIts not 100% done yet though; I refactored the Dba stuff a little and not too long ago I added the last section about republishing woes; if you missed it you are going to find it highly useful :)
ReplyDelete1. From my experience on JBoss "out of permgen space" after web application restart was caused by commons logging or other library (probably cglib) (someone is holding hard references to class loaders with loaded classes, and permgen space after restart will contain classes from both old and new webapp). At least don't put commons-logging into WEB-INF/lib (it may appear from some transitive dependencies).
ReplyDelete2. Some people recommend to try JVM options -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled so CMS garbage collector will try to clean permgen from obsolete classes.
3. JRockit does not have permgen space at all, and will never raise such error :)
4. There is a Memory Analyzer Tool in Eclipse - you can investigate this problem and make a final point in endless discussions about Tomcat, webapps and permgen OOM errors.
Those command line switches will probably help a lot. But my fix, to not have to reload the webapp at all, is probably the most effective :) With your excellent comment I don't really need to add anything more!
ReplyDeleteadded in information about nested transactions and enriched the Dba class with a readOnly mode and the possibility to mark the transaction as rollback only (which makes closeEm() cause a rollback).
ReplyDeleteadded in section on changing the publish directory to something more sane. Also replaced Richfaces with Primefaces for reasons stated in the article. Finally, added in section about using the filesync plugin to hot-deploy web resources if that doesn't happen automatically on save. Anyone have an idea when that can happen?
ReplyDeleteNice article, this article cleared most of my doubts... Thanks a lot... I need to create a new project using Tomcat 7.0, JPA 2.0 and Apache Http Server. And I am new to Java. Can JPA 2.0 be used independently or it requires provides like Hibernate, EclipseLink, etc.
ReplyDeletelike everything in the Java world, JPA is only a specification and not something you can use independently. Hibernate/EclipseLink/OpenJPA/etc. provide the implementation.
ReplyDeleteadded the "adding utility methods to Dba" and "Transform empty strings to null values" paragraphs.
ReplyDeleteIn your Dba.class the initialize method is never called. Am I missing something?
ReplyDeleteOops, that must have fallen off in some refactoring stuff. In my own projects I moved from doing a lazy init to doing initialization stuff in a servlet context listener. Its a bit cleaner.
DeleteAwesome..thanks!
DeleteI took the plunge and completely restructured the article, since it had become a bit of a chaos. The article is now basically split into four isolated sections: basic setup, adding JSF 2, adding JPA 2, troubleshooting. All configuration details are split up into the different sections in stead of being dumped in one go; that makes it slightly less copy/pasty, but it is hopefully easier to relate what exactly does what.
ReplyDeleteGreat contribution!, thank you!
ReplyDeleteThanks! But if something was in some way unclear please do share :)
DeleteThanks for the great tutorial! I followed your steps, but I'm always getting the same error: (Cannot create JDBC driver of class '' for connect URL 'null'). There are a lot of posts about this behaviour, but none of them could help me. Any ideas?
ReplyDeleteThe problem must be in the datasource configuration, as apparently the persistence provider is trying to create connections manually in stead of using a DS. Make sure there is no typo, you define a datasource in both the context.xml AND the web.xml, you refer to that datasource in your persistence.xml and the most important of all: if you make changes make sure those changes are actually deployed to the server properly.
DeleteThanks a lot! The problem was that my project doesn't have the same structure. So I placed them like it has to be after deploying and finally it works.
DeleteThank you.
ReplyDeleteCould it be, that you forgot putting the namespace declaration
xmlns:ui="http://java.sun.com/jsf/facelets"
of the html-element in
template.xhtml and index.xhtml.
Got an error, because of it.
Right you are, the oversight has been corrected. Thanks a lot.
DeleteIs there a way to use placeholders in the persistence.xml, context.xml, and web.xml so that I could perhaps target different databases using a database.properties file?
ReplyDeleteAs you can see in the article a datasource is used. This datasource is something you can configure in the application/web server, it doesn't have to be in the application (shouldn't be to be honest). Then in your web.xml / persistence.xml you can simply refer to the server specific datasource. This blog explains that quite well: http://www.captaindebug.com/2011/07/installing-mysql-datasource-on-tomcat-7.html#.UPlrEmf08uI
DeleteThanks for the info, and I really enjoy your style (K.I.S.S). Trying to mix in Spring with this has been fun.
Deleteimportant update: I finally pinned down where I was screwing up the hot-deployment while I was playing with Wicket 6.5. Previously I was recommending to set to "never publish automatically"; that is wrong and causes non-class resources to not be hot-deployed upon change. Leave it at "automatically publish when resources change", but be sure to set the "reloadable" attribute in the context.xml to false. Then hot-deployment works flawlessly, if the project's deployment assembly is correct of course.
ReplyDeleteGood catch, would it make sense to put the context file in /${project.name}/src/main/webapp/META-INF/context.xml or even /Servers/Tomcat v7.0 Server at localhost-config/context.xml?
ReplyDeleteNo, you should add it to the existing tomcat_install_dir/conf/context.xml as described in the article I put in the comments recently. Soon I will update the article to properly document that here. The intention of the article was to get you up and running for development quickly, but I might as well put the icing on the cake :)
DeleteYeah wait, I think I now understand the reason for you asking this question. Eclipse overrides the configuration files of the server when you hot-deploy to it, so manually changing the context.xml file of the server is not going to work for a local development server; Eclipse will simply overwrite it again with its own version. I need to do some further research and tests.
DeleteYou are correct. Putting it here /Servers/Tomcat v7.0 Server at localhost-config/context.xml causes the default tomcat to get the correct info as it pertains to the Context Resource that we added. Dding it here /${project.name}/src/main/webapp/META-INF/context.xml allegedly allows you to ship the war with these settings in it opposed to blindly accepting the tomcat's globals. I am going to test this later today.
ReplyDeleteNice Article. Can you please explain me what is the meaning when you mentioned this sentence.
ReplyDeleteOracle does not provide an API dependency for JPA 2 (at the time of writing at least), but luckily most persistence providers do provide their own; since we're using Hibernate here I picked the API as provided by Hibernate.
Last time i checked, there was no api library for jpa2 in the maven repositories. There is one for jpa1. I cant make it any more clear than that. If you dont use maven such statements will be meaningless to you.
DeleteGreat. Just what I was looking for. For exactly the same reasons that you created it.
ReplyDeletesince this article I've developed ahead and adopted a strategy to just create a DBA instance on request start and clean it up (either commit or rollback) on request end to take away management of it entirely; by sticking it in ThreadLocal its quite easy to manage an active transaction per request and just get it anywhere in your code. Any web framework will have a possibility to hook into its lifecycle, in the case of JSF that is a phase listener.
DeleteI cannot thank you enough for this article. Even though I found a work around I am puzzled by this simple issue. I tried creating a simple maven project by doing this: file -> new -> other -> Maven -> Maven Project -> archetype: maven-archetype-webapp. Then I followed all the steps until we get to deploying on Tomcat. First the project does not showup on the configured list when configuring the server. Even I tried dragging the project to the server view, it does not work. Then I created a eclipse dynamic web project and this readily was deployable and I was able to drag it and drop it on the server. Can you please provide any help on how to create the initial maven web project?
ReplyDeleteThanks,
Mac
I never use an archetype, I always just create a "simple maven project" and setup everything manually. See my Maven articles for more information.
DeleteDo you have the jboss-tools 'maven integration' installed? (this was formerly m2eclipse). That might help. Otherwise you can mark a module as 'deployable' yourself in the project's right-click menu.
@SreeRama: I had to remove your comments. If you don't like certain technologies that's your own choice, but that doesn't mean you have the right to start spreading advice against their usage in the comments of articles that use them. Create your own blog if you must vent your opinion.
DeleteI too use the maven-archetype-webapp, but I cannot say that I have had the problem you describe. I do recall back a few months ago I did have an issue where that archetype did cause project facets to not be added/set correctly. If you right click the maven project --> Properties --> Project Facets, you should be able set the things that should be set and then try to add it to your tomcat server, it should fix your problem.
Delete@Gimby Neverthless it is your blog. I wish you all the best.
ReplyDeleteI was trying to express my experience and do not want to spread advice against any thing.
I still use Tomcat in commercial application development,
for which I proposed solution to business domain to develop a medium size web-application.
I have used Tomcat few years ago for one of the top U.S. client
Thank you for the great tutorial. Only one point: The entitiymanagerfactory is not closed. This could cause trouble, if the app is deployed among others in the same server. One simply can make the dba class a Weblistener, without changing much in your code. See : http://stackoverflow.com/questions/7862700/best-practice-to-get-entitymanagerfactory.
ReplyDeleteBest Regards
Frank Ruenagel
No, it won't. It is perfectly legal to create multiple factories, each application will simply have its own.
DeleteThis is a great tutorial, at least for JPA - not tried yet JSF. Thank you.
ReplyDeleteWell thank you, but it is absolutely not a tutorial for JPA. The article assumes that you already know it. In fact it is not a tutorial for anything, it is an article aimed at getting you up and running so that you may start to learn by following tutorials (although I would stress you to read books in stead).
DeleteIt took a long time, but I finally dug in and updated the sections dealing with the context.xml file so the article no longer advises you to put a context.xml in the application itself. I also updated some dependency versions and added a section on dealing with multiple deployment environments (dev, prod).
ReplyDeleteAnother major overhaul (one more is coming, of the Dba / mock data section): added many more helpful screenshots, fixed existing screenshots to be more readable. Updated some sections to be more in-line with modern Eclipse versions. Finally found the easiest and most understandable way to set context deployment settings in Eclipse (in the server view Modules tab) so I could remove all meddling with the context.xml file from the article and just let Eclipse do it. Made the article more novice-friendly by specifically stating where to look and showing it in screenshots.
ReplyDeleteJust wanted to let you know that even a few years after your article it saved my day! I spent much of last night trying to get my Tomcat-Maven-JPA-JSF setup to work without success and this morning it worked with your help - thanks a ton!
ReplyDeleteNo problem... to bad the syntax highlighting broke on these older articles. I'll have to fix that...
Deletegreat
ReplyDelete
ReplyDeleteAmazing & Great informative blog,it gives very useful practical information to developer like me. Besides that Wisen has established as Best Hibernate Training in Chennai . or learn thru Online Training mode Hibernate Online Training | Java EE Online Training. Nowadays Hibernate ORM has tons of job opportunities on various vertical industry.
After reading your post I understood that last week was with full of surprises and happiness for you. Congratz! Even though the website is work related, you can update small events in your life and share your happiness with us too.
ReplyDeleteSelenium training in Chennai
Selenium training in Bangalore
Selenium training in Pune
Selenium Online training
I would like to thank you for the efforts you have made in writing this article. I am hoping the same best work from you in the future as well. In fact your creative writing abilities has inspired me to start my own BlogEngine blog now. Really the blogging is spreading its wings rapidly. Your write up is a fine example of it.
ReplyDeletepython Training in Pune
python Training in Chennai
python Training in Bangalore
Attend The Python training in bangalore From ExcelR. Practical Python training in bangalore Sessions With Assured Placement Support From Experienced Faculty. ExcelR Offers The Python training in bangalore.
ReplyDeletepython training in bangalore
This is the exact information I am been searching for, Thanks for sharing the required infos with the clear update and required points. To appreciate this I like to share some useful information.
ReplyDeletebest sap oracle cloud training
best sap oracle cloud education &cerification support
best sap oracle cloud training cost
best sap oracle cloud careers
best sap oracle cloud erp education
best sap oracle cloud courses
best sap oracle cloud training institute
best sap oracle cloud course in bangalore marathalli
btm layout india
It is very good and useful for students and developer.Learned a lot of new things from your post Good creation,thanks for give a good information at sap crm.
ReplyDeletebest sap oracle cloud training
best sap oracle cloud education &cerification support
best sap oracle cloud training cost
best sap oracle cloud careers
best sap oracle cloud erp education
best sap oracle cloud courses
best sap oracle cloud training institute
best sap oracle cloud course in bangalore marathalli
btm layout india
Excellent post for the people who really need information for this technology.
ReplyDeletebest sap oracle cloud training
best sap oracle cloud education &cerification suppor
best sap oracle cloud training cost
best sap oracle cloud careers
best sap oracle cloud erp education
best sap oracle cloud courses
best sap oracle cloud training institute
best sap oracle cloud course in bangalore marathalli
btm layout india
such a great word which you use in your article and article is amazing knowledge. thank you for sharing it.
ReplyDeleteBecame an Expert In Google Cloud Platform Training in Bangalore! Learn from experienced Trainers and get the knowledge to crack a coding interview, @Softgen Infotech Located in BTM Layout.
This comment has been removed by the author.
ReplyDeleteI read your content. I like that your blog is very awesome sir.
ReplyDeletePython Training in Chennai
Python Training in Bangalore
Python Training in Hyderabad
Python Training in Coimbatore
Python Training
python online training
python flask training
python flask online training
This is additionally a generally excellent post which I truly delighted in perusing. It isn't each day that I have the likelihood to see something like this..
ReplyDelete360DigiTMG supply chain analytics training
Fake Bank Statement
ReplyDeleteFake Bank Statement
Fake Bank Statement
Fake Bank Statement
Fake Bank Statement
Fake Bank Statement
Fake Bank Statement
Fake Bank Statement
Fake Bank Statement
ReplyDeleteFake Bank Statement
Fake Bank Statement
Fake Bank Statement
Fake Bank Statement
Fake Bank Statement
Fake Bank Statement
Fake Bank Statement
There are lots of information about latest technology and current project modules click here for latest project title with abstract and code execution MCA Project Topics , MCA Final Year Project , MCA Final Year Project Topics , MCA Mini Project Topics , cse mini projects , M.Sc Computer Science Project Topics , Mini Project Topics for MSc Computer Science , MSc Computer Science Project Topics in Php , MSc Computer Science Project Topics in Python , MSc Computer Science Project Topics in Java
ReplyDeleteSuch a good post .thanks for sharing
ReplyDeleteSpoken english classes in t nagar
Spoken English Classes in Chennai
Great post. keep sharing such a worthy information.
ReplyDeleteSEO Training in Chennai
SEO Course
This post is so useful and informative. Keep updating with more information.....
ReplyDeleteCyber Security Institute In Bangalore
Best Cyber Security Training Institute In Bangalore
It's remarkable. The way you describe the information is awesome. This will really help me out. Thanks for sharing.
ReplyDeleteTableau Online Course
Tableau Online Training
Tableau Certification in Chennai
perde modelleri
ReplyDeletesms onay
TURKCELL MOBİL ÖDEME BOZDURMA
nftnasilalinir
ankara evden eve nakliyat
trafik sigortası
WEB SİTESİ KURMA
AŞK ROMANLARI
smm panel
ReplyDeleteSmm Panel
iş ilanları
instagram takipçi satın al
Hirdavatciburada.com
beyazesyateknikservisi.com.tr
SERVİS
Jeton hilesi indir
Axure RP Pro Full Version With Crack user is style to the develop, answer, or the experience. Edit the custom groups, pages, & design. User easily Axure RP 8 Full Crack
ReplyDeleteKaspersky Total Security 2023 Crack launched a new Kaspersky product in 2021, but it is not an anti-virus product: Kaspersky Security Cloud. Kaspersky Total Security 2023 Crack
ReplyDeleteGood content. You write beautiful things.
ReplyDeletehacklink
hacklink
vbet
sportsbet
taksi
mrbahis
korsan taksi
sportsbet
mrbahis
Success Write content success. Thanks.
ReplyDeletekralbet
betturkey
canlı slot siteleri
canlı poker siteleri
betmatik
betpark
deneme bonusu
kralbet
ReplyDeletebetpark
tipobet
slot siteleri
kibris bahis siteleri
poker siteleri
bonus veren siteler
mobil ödeme bahis
betmatik
0NWC6
amasya
ReplyDeleteantakya
edirne
elazığ
kayseri
N27HT4
kars
ReplyDeletekütahya
aydın
balıkesir
bitlis
7F4JNL
salt likit
ReplyDeletesalt likit
dr mood likit
big boss likit
dl likit
dark likit
7LWW1
شركة تخزين اثاث
ReplyDeleteشركة تركيب كاميرات مراقبة
صبغ ضد الصدأ
ReplyDeleteمادة ضد الصدأ
Respect and that i have a dandy offer: House Renovation home improvement near me
ReplyDeleteThis was such an insightful read! Looking forward to reading more of your content!
ReplyDelete