Showing posts with label servlet. Show all posts
Showing posts with label servlet. 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.

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

Developing Java webapp, we use of Filter for cache control, compression, database connection and transaction handling. So, Filter is another feature I need to support.

Original Thought
In ASP.NET, IHttpModule is like a filter while IHttpHandler is like a servlet. It seemed obvious to modify the existing ServletModule to execute the filter.

Platform Mismatch
The first problem I found is the async design of ASP.NET. The IHttpModule is an event sink and need to implement the event handlers for events like BeginRequest, EndRequest. In contrast, Servlet Filter is using FilterChain to call the filter one by one. I tried to execute the filter chain in BeginRequest event. However, I won't be able to gain the access of Session. Having google around, I found the comprehensive events execute sequence in IHttpModule (http://msdn.microsoft.com/en-us/library/ms178473.aspx):
  • BeginRequest
  • AuthenticateRequest
  • PostAuthenticateRequest
  • AuthorizeRequest
  • PostAuthorizeRequest
  • ResolveRequestCache
  • PostResolveRequestCache
  • PostMapRequestHandler
  • AcquireRequestState
  • PostAcquireRequestState
  • PreRequestHandlerExecute
  • PostRequestHandlerExecute
  • ReleaseRequestState
  • PostReleaseRequestState
  • UpdateRequestCache
  • PostUpdateRequestCache
  • EndRequest
  • PreSendRequestHeaders
  • PreSendRequestContent
    To get the session, the module needs to implement an event handler for PostRequestHandlerExecute event. In order to get the Session, HttpContext.Current.Handler has to be implementing IRequiresSessionState as well.

    There is code to wrap the HttpContext.Current.Handler around (http://stackoverflow.com/questions/276355/can-i-access-session-state-from-an-httpmodule). It worked fine but it does not solve the async problem.

    From Module to Handler
    I managed to run the filter chain in PreRequestHandlerExecute event. It turned out that not only it run the JSP code, but also the source code of the JSP was also append to the result by the default handler. Response.End() has to be invoked. And then some checking for the whether it is my own servlet handler, etc. The code is a bit weird.

    Since it does not matter where the filter chain is executed, I moved to code to ServletHandler and revert the changes in ServletModule, it compiled and seemed working with the Hello World JSP. No more Response.End() and checking. Async problem is solved since executing the filter and servlet are in one place. It matched the semantics of Java webapp.

    Testing
    The simplest way to test is to get a filter that GZIP the output and use fiddler to see if the response is compressed. I grabbed filter from http://onjava.com/pub/a/onjava/2003/11/19/filters.html. Added the setting in web.xml. Somehow it does not work. It turned out it's because the default XML namespace in web.xml generated by Eclipse caused the problem. Removing the namespace made enabled the support of filter.

    XML Namespace
    The default XML namespace is quite annoying. I am not sure why it is there as it is meaningless. I tried a few way to remove it with the .Net XML API but the namespace somehow stayed there. The final resort was invoking string.Replace() to get rid of it.

    The latest source code has been checked-in in http://jeans.codeplex.com/.

    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.

    Friday, November 12, 2010

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

    There are two main camps in enterprise server technology market, Java and .Net Framework. Java side has Servlet, JSP, and Java ServerFace. .Net Framework side has ASP.NET (WebForm and MVC). Both sides are not compatible with each other. This article will describe how to run JSP in IIS natively without connecting to a Java web container.

    IKVM
    Mr. Jeroen Frijters has implemented an JVM in .NET, IKVM.NET. It seems to be a good start.

    Jetty vs. Tomcat
    The first attempt was compiling Jetty to execute web applications directly. Digging into the source code and after a few trials, I found that Jetty is using Jasper library, which is Tomcat's JSP engine, to run JSP. Given that .Net does not have the concept of class file, it is unlikely .NET is able to load the create new class and load it into memory in runtime.

    JSP Precompiler
    Jasper not only is the runtime engine of JSP, it also includes a JSP precompiler to compile JSP into Servlet. Using it is not hard but type the Java command is tedious. It makes sense to use IKVM to create a .Net executable. The Jasper library from Tomcat 5.5 is used:

    • ant.jar
    • catalina-ant.jar
    • commons-el.jar
    • commons-logging-api-1.1.1.jar
    • jasper-compiler-jdt.jar
    • jasper-compiler.jar
    • jasper-runtime.jar
    • jsp-api.jar
    • naming-factory-dbcp.jar
    • naming-factory.jar
    • naming-resources.jar
    • servlet-api.jar

    Then compile the precompiler with this command:
    ikvmc -classloader:ikvm.runtime.ClassPathAssemblyClassLoader -target:exe -main:org.apache.jasper.JspC -out:bin\jspc.exe *.jar

    JSP Compilation
    The command-line arguments of JspC is the same as the Java version. The following command can be used (%1 is the directory of the target web application):
    jspc -uriroot "%1" -d out -compile -v -webxml "%1\WEB-INF\jspweb.xml" -trimSpaces -source 1.5 -target 1.5

    The JSP
    The first JSP is a simple Hello World page to test out the JSTL EL:
    Hello ${param.name}!

    Web Application Compilation
    Everything are still in JARs and classes. However .Net Framework needs DLLs. The following command is executed to get everything in .Net assembly:
    ikvmc -classloader:ikvm.runtime.ClassPathAssemblyClassLoader -recurse:%1\WEB-INF\classes -out:%1\bin\jsp.dll -reference:%1\bin\jspc.exe %1\WEB-INF\lib\*.jar

    Servlet API, in .Net
    Before running the DLL, the following classes are necessary in the Servlet API to run the JSP:
    • HttpServletRequest/Response
    • ServletInputStream/OutputStream
    • ServletRequestDispatcher
    • ServletContext
    • ServletConfig
    and a few more adapters to implement Java I/O.

    ASP.NET IHttpModule and IHttpHandler
    The final bit is the ASP.NET module to initiate the configuration and the handler to execute the servlets/JSP.

    I have made the source code available so you don't need to go through the whole process. Please visit http://jeans.codeplex.com/.

    Thursday, March 22, 2007

    Screwing up HTTP Session for Servlet/JSP

    Prerequisites:
    1. Browsers under Mozilla project (e.g. Firefox, SeaMonkey)
    2. Enable cookies
    3. Tomcat (probably all Java web containers)

    Procedure:
    1. Write a page that use back-slashes as the directory seperator (e.g. http://www.abc.com\app\page.jsp)
    2. Include this page with absolute path in all pages

    Why:
    1. The browser encode back-slashes as %5C. So, http://www.abc.com\app\page.jsp will be encoded as http://www.abc.com%5Capp%5Cpage.jsp
    2. The browser tries to look up the cookies for the domain. However, the browser think that the domain name is www.abc.com%5Capp%5Cpage.jsp instead of www.abc.com
    3. The browser cannot find the cookies for the domain. So, it does not send the cookies to the browser.
    4. When cookies is enabled, Java Servlet and JSP uses it to store the session ID
    5. Since the cookies was not found in the request header, the web container creates a new session and send to session ID with Set-Cookie header.
    6. The browser uses the new session ID for the next request.
    7. As a result, the web container creates new session for each browser request and the sessions are screwed up.

    What about other browsers? I think IE is smart enough to replace all back-slashes to slashes before it does further processing. I don't know about others though...

    You may want to ask why you would use back-slash. My answer is "No, you may not but your staff or your outsource partner may." In a workflow engine we are studying, we found the following JavaScript:
    document.write("<iframe src='<%=request.getContextPath()%>\\app\\page.jsp'>")

    My colleague asked for help about why the session does not work in Firefox. Since the code was written by different parties, we know nothing about the workflow engine. I alone used 6 man-hours to fix the BUG. I think I know fairly well on HTTP and Java Servlet. I tried many possible solutions like finding the HttpSession.invalidate(), session.removeAttribute(), appending the jsessionid, etc. Finally, I used a packet sniffer to dig out the pattern that changes the jsessionid cookie and recognize the weired %5C URL in a GET command.

    Friday, December 15, 2006

    Base64, MHTML, and IE

    In HTML 4.0, binary objects (e.g. image) can be inlined with Base64 encoding. However, Internet Explorer totally ignores the standard. Even in IE7, base64 image is still not supported.

    Two solutions proposed by Dean Edwards and Luiz Angelo De Luca are trying to solve the problem. Both of them are implemented in PHP. Base64Html is a Java implementation of Luiz Angelo De Luca's solution: convert the HTML into MHTML (HTML Mail format).

    Luiz Angelo De Luca's solution can be genernalize to the following steps:
    1. Buffer the output and add a function callback
    2. If the browser is IE, set the HTTP header to MHTML
    3. Convert the HTML to MHTML
    Luiz Angelo De Luca has implemented it in a single PHP. In Java, we make use of HttpServletResponseWrapper (for #1), Serlvet Filter (for #2), and Apache Jakarta Commons Email API (for MHTML construction). Source code and example web app are available at: http://www.shaneng.net/Main/Base64Html