Showing posts with label jpa. Show all posts
Showing posts with label jpa. Show all posts

Monday, November 15, 2010

Jeans - Running Java Servlet and JSP Webapp in ASP.NET/IIS - Part 4

Retrieving a database connection is one of the most popular problem in software development. Usually, we specify the connection string in a configure file and create the database connection with the connection string. The Java way is wrapping connection string in a javax.sql.DataSource interface and put in the application server config. The application server will put the DataSource instance into a JNDI context. So, this is what should be supported.

Bring in the Unity
Microsoft has a Dependency Injection container called Unity. Even though I am not doing dependency injection, I'd like to configure how to instantiate the data source in web.config. Instead of writing my own configuration classes, Unity is a good alternative as an object builder.

Before doing any configuration, the JDBC driver has to be compiled into .Net assembly since it will be loaded by Unity instead of the Java class loader.

Method Injection
When configuring Unity, we could specify the methods to be called during the instance initialization. The first attempt:
<method name="setURL">
    <param name="url" value="..." />
</method>

However, Unity was smart enough to know there is no such parameter name. I fired-off the object explorer to look the actual parameter name. It seemed that IKVM compiled all parameter name with str, str1, str2, etc. The configuration has to be like this:

<method name="setURL">
    <param name="str" value="..." />
</method>


JNDI Context
Having known that a data source with the name jdbc/testDS is the same as java:comp/env/jdbc/testDS, I tried to mimic the behavior with the reference of this article. It turned out the result is an exception:
javax.persistence.PersistenceException: Exception [EclipseLink-7060] (Eclipse Persistence Services - 2.0.2.v20100323-r6872): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Cannot acquire data source [jdbc/testDS].
Internal Exception: javax.naming.NameNotFoundException: Name jdbc is not bound in this Context
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:399)

So, I reverted to the simpliest way by just creating the jdbc subcontext in the initial context.

Password
The configuration was set. Time to run the test. It did not work:
javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.2.v20100323-r6872): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: Login failed for user 'jsptest'.
Error Code: 18456
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:399)


Well, the password was copied from persistence and very simple:
<method name="setPassword">
    <param name="str" value="passw0rd" />
</method>

Not sure what happened but when the user and password were put in the URL, it worked.

The new source code has been checked-in in http://jeans.codeplex.com/. The persistence.xml has changed to use the <non-jta-data-source>jdbc/testDS</non-jta-data-source>.

Note: sqljdbc4.jar has been removed from the source repository. Please download it from Microsoft and compile it with IKVM to run the JSP demo site.

Saturday, November 13, 2010

Jeans - Running Java Servlet and JSP Webapp in ASP.NET/IIS - Part 2

One of my mentors said, "If you do not write SQL, you cannot do application development." Even though the statement is not quite true as there are ORM frameworks handling the database communication, the statement emphasizes the importance of database. So, running a pure Hello World JSP is a poor proof of concept to ASP.NET. Running a database connected JSP is more meaningful.

Preparation
The following components were gathered in this phase:
  • JPA - the current standard of doing ORM in Java.
    •  EclipseLink is the reference implementation. It should serve the purpose well.
  • SQL Server 2008 Express - As ASP.NET is being used anyway, why don't we work in the whole Microsoft ecosystem?
    • SQL Server JDBC Driver - Yes, I need a JDBC Driver in .Net...
  • AdventureWorksLT - Microsoft used to provide sample databases that works with SQL Server. AdventureWorksLT is the simplified version.
One bottleneck I have is I have no JPA experience. I used Hibernate before but JPA is a standard. The problem of being standard is there are many implementations. The only way to understand it is to read the specification. Otherwise, it's hard to understand how it works in general so that it works for most (if not all) implementations.

META-INF/persistence.xml
One of the challenges encountered was loading the persistence.xml. Luckily, IKVM can load the resource files with by specifying -classloader:ikvm.runtime.ClassPathAssemblyClassLoader when the compiling the class files and JAR files into .Net assembly. However, since all files were compiled into a single DLL. Only the first persistence.xml IKVM read can managed to be the resource.

META-INF/orm.xml
The problem of compiling everything into .Net assembly is losing all annotations in the classes. In the other words, you must use META-INF/orm.xml and initialize it in an IHttpModule.

Rework
The limitation of loading the two XML files is quite nasty. It would not feasible for medium size application that might have a few JAR files containing persistence.xml. Creating orm.xml manually is quite a pain as you know the information is in the classes already. To increase the flexbility, ClassPathAssemblyClassLoader is is wrapped by an URLClassLoader with the following classpaths:
  1. WEB-INF\classes 
  2. All JAR files in WEB-INF\lib directory
Then, it is invoked to the current thread's context class loader to load the JAR and the precompiled JSP classes directly. It works!

Dual Mode
Now, Jeans support two ways to run a Java webapp:
  1. Native mode - With everything compiled into a single DLL
  2. WAR mode - Run a standard Java webapp directory whose structure is the same as in WAR file given:
    1. JSP are precompiled into servlet and put into WEB-INF/classes folder.
    2. JSP Servlet mappings are stored in jspweb.xml to complement the original web.xml.
The consequence is quite obvious. In native mode, it takes around 50MB of memory to load the Hello World page. In WAR mode, it takes around 100MB of memory the run the same page. No benchmark for the application performance but I believed native mode should run faster.