Welcome!

Industrial IoT Authors: Pat Romanski, William Schmarzo, Elizabeth White, Stackify Blog, Yeshim Deniz

Related Topics: Java IoT

Java IoT: Article

A Practical Solution for the Deployment of JavaServer Pages

A Practical Solution for the Deployment of JavaServer Pages

Sometimes it's worthwhile to go back and visit your former projects. It certainly was for me - using presentation as a commodity to be deployed according to network configuration is the concept that resulted from my visit.

The original assignment was to reduce the operating costs of a large banking agency network. How could this be achieved with a network of 23,000 personal computers scattered over 2,000 sites connected by a frame relay with a guaranteed bandwidth of 32Kb? We selected an intranet solution that radically reduced PC client/server applications to a single local program, the browser. To reduce bandwidth needs we deployed one Web server per site to perform only three tasks - handle presentation, maintain reference data, and invoke central system applications. This intranet design saved money by reducing the number of machines to operate from 23,000 to 2,000 by allowing them to be operated with a browser.

We can implement this concept to reduce the load of central farms with Java and J2EE because a fast local loop isn't always available. My goal, however, is to show specifically how we can do it better. The aforementioned approach didn't support a Web server's update on the fly and we had to contend with the central system's synchronization. The solution I present here addresses these issues by allowing Web servers to download their presentations the way browsers download applets. Figure 1 illustrates a possible organization. Let's summarize what we would need:

  1. Inexpensive Java servers able to host JSPs and servlets
  2. An API allowing them to invoke central system applications
  3. A simple way to download a presumably large number of Java servers from any number of central repositories

The first two requirements can be fulfilled with off-the-shelf products, and the local Java server has to address only the following three requirements: Generate presentation Invoke central applications Maintain reference data At least two Open Source products meet these needs: Tomcat                                                                                                   . ( http://jakarta.apache.org/downloads/binindex.html) and Resin (www.caucho.com/download/index.xtp).

EJBs, Remote Method Invocation (RMI), or Java Message Service (JMS) can be used as the API to connect to central systems.

The last point, presentation downloading, implies development. This requires more explanation and thought and is the core of the article. Presentation downloading relies on a Java class loader and leverages on JSPs and servlets specifications, which I'll present first.

Standard
The Java Servlet Specification v2.2 defines a Web application as a collection of HTML pages, servlets, and classes that exists as a structured hierarchy of directories. The root of this hierarchy is the document root that serves files such as images or HTML. If your Java server waits for HTTP requests on www.iamakishirofan.com and you defined your Web application as gunnm, your users will be able to invoke zalem.html, stored at the root with the URL www.iamakishirofan.com/gunnm/zalem.html.

A WEB-INF directory contains a web.xml file that describes, among miscellaneous things, servlet and JSP definitions, initialization parameters, mapping to URL, and security constraints. It can also contain a classes subdirectory in which classes, servlets, taglibs, JSP invoked beans, compiled JSP, and more are stored. A Web application should be packaged in a .war file - the JAR archive of the hierarchy.

This packaging is convenient as it gathers all related components in a single delivery. It has another important property: all servlets and JSPs of a .war are served the same ServletContext, which is different from the ServletContext of other packages. Servlets and JSPs can use this ServletContext to access .war data, such as resources and initialization parameters, or to store and retrieve application-wide attributes.

The servlet container loads and instantiates servlets. It initializes them before their first use by calling their init() method with an object that implements the ServletConfig interface. This provides access to servlet-specific data. When it decides to unload a servlet, the container invokes the servlet destroy() method and unreferences it. Each time the container has to route a request to a servlet, it invokes the servlet's service() method.

A compiled JSP is a servlet, even if it doesn't extend HttpServlet or GenericServlet as a normal servlet but as another class that's application server dependent. In the case of Resin, it's com.caucho.jsp.JavaPage and with Tomcat, org.apache.jasper.runtime.HttpJspBase. As you can see, compiled JSPs are no longer portable even if there are only minor differences. The specification requires a JSP to implement a standard HttpJspPage interface. A JSP indirectly handles container requests as depicted in Figure 2.

A compiled JSP implements a _jspService() method and, optionally, a jspInit() and a jspDestroy() method. The specification implies that, for instance, when the container invokes Servlet.init(), jspInit() is invoked somewhere in the implementation of the JSP base class. I've provided the Tomcat implementation in Listing 1. All Java servers I tested have similar code.

Choices
Back to our requirement - the solution I want to implement involves four participant types:

  • Browsers: Submit HTTP requests
  • Java servers: Process presentation and download the JSPs and servlets from a repository
  • Repositories: Must be accessed with a URL. A suitable repositories list includes HTTP servers as depicted in Figure 3 and FTP servers
  • Java application servers: Process EJB requests
I need to implement a piece of code in the Java server that's able to seamlessly retrieve JSPs and servlets from a central point, cache them, and support remote update. The solution depicted in Figure 3 is just common sense: I define a special servlet, JSPservlet, and package it in a .war file to handle all requests targeting its Web application. This servlet is responsible for loading target JSPs and servlets and forwarding them requests. To minimize data transfers, I handle archives (.jar) files only and cache downloaded archives, not only in memory but also on disk to survive a scheduled shutdown or a crash.

To simplify the development I don't handle JSP compilation. It doesn't mean the solution doesn't support JSPs, only that they have to be precompiled, not a real drawback. Compiling JSPs is the only safe way to ensure a JSP can compile, and I prefer to avoid downloading failing code. I also don't support single thread servlets that guarantee only one thread at a time will execute through a given servlet instance's service() method. The support of this feature would require instantiating a new target servlet when already created target servlets are processing a request. It would add complexity to the logic and have an adverse impact on scalability.

Listing 2 shows the deployment descriptor (web.xml) of the JSPservlet application and how to define that a JSPservlet must handle all requests targeting the application. You specify in <servlet-mapping> <url-pattern>/</url-pattern>, not <url-pattern>*</url-pattern> as you'd expect. Note that I use <init-param> to set every machine-dependent parameter. Deployers can then modify them to accommodate different installation and operating system requirements. cachePath is the directory in which downloaded JARs are locally stored, and remoteLocations indicates a property file in which remote locations are defined. For instance, if a JAR file named myjar must be downloaded from an HTTP server www.mysite.com, remoteLocations will contain an entry myjar=HTTP://www.iamakishirofan.com.

Implementation
Let's look at the class diagram in Figure 4. You see the aforementioned JSPservlet that relies on a JSPhandlers HashMap of JSPhandler. There's a JSPhandler instance per application that reads parameters and maintains ClassEntry objects, one per archive. ClassEntry maintains a target servlets cache and a JSPloader instance.

JSPloader is the class loader itself and maintains a class cache. It's also responsible for saving locally downloaded archives.

We can now see how the solution works. The Java server calls JSPservlet service(). To know which servlet is requested, JSPservlet.service() uses the request object. It first finds the appropriate JSPhandler with getHandler(), passing the application name it retrieves using the request getContextPath(). Then it gets a reference on the target with JSPhandler.get(), passing the path to the target returned by the request getPathInfo(). Eventually it uses this reference to invoke the target object service() method. As you can see in Listing 3 that's all for JSPservlet.

Listing 4 shows the implementation of JSPhandler. Its constructor retrieves parameter values from web.xml using ServletConfig. getInitParameter() and restores remote location properties from their persisted state. I chose to use the first part of the path as the archive name and the remaining part as the class name. Given the URL www.iamakishirofan.com/gunnm/ gally/nano/machine, if the application server is configured with the JSPservlet application on gunnm, ContextPath will be gunnm, the archive will be gally, and the servlet path in the archive, nano/ machine.class. This may seem a bit rough compared to the Web application flexible mapping but it's simpler to administer and implement. So JSPhandler.get() parses the pathInfo string and uses the archive part to find the corresponding ClassEntry in classEntries HashMap. It creates a ClassEntry if the search fails and invokes its get() method.

Now we can look at the ClassEntry implementation in Listing 5. Its constructor creates a JSPloader. Its get() method first tries to get the target servlet from its instance cache, servletObjects. No matter how many times a servlet is invoked, a single object is used and reused. If the object doesn't exist yet, it uses JSPloader to retrieve its class, invokes Class.newInstance() to instantiate it, and Servlet.init() to initialize it. It's extremely close to the Java server's implementations.

Class Loader
Before diving into the last and most complicated piece of code, JSPloader in Listing 6, let's recap what a class loader is and what our class loader is supposed to do. A class loader is an object responsible for loading classes. Given the class name, it can generate or load its binary code. It inherits from ClassLoader, which provides methods you can override (loadClass is the most flexible method). ClassLoader also implements a service method, defineClass, that converts the binary code in the Java class and resolveClass that links it. JSPloader must load classes from JAR files located either in the cachePath or at a URL.

Back to our example: it retrieves the archive from the local cache in cachePath/gally.jar or downloads it from a URL, which is the value of a gally property persisted in remoteLocations. In addition, when JSPloader downloads an archive, it must save this archive in its local cache, cachePath/gally.jar.

I prefer loading classes in a JSPloader constructor to minimize disk and network access duration and numbers. Another advantage is that forced loading can be performed outside peak hours by an administration JSP. JSPloader will then deliver a better response time as classes are already in memory. I found the memory use - same order of magnitude as the size of a downloaded archive - wasn't a showstopper. Note that I link a class only when requested, and ClassEntry instantiates objects only once, when they're first requested.

The JSPloader constructor tries downloading the archive from the local cache with loadClassDataFS() and then from its remote location with loadClassDataURL(). Both methods build a JarInputStream from an input stream that loadClassDataFS() gets from a FileInputStream and loadClassDataURL gets from a URL.openStream(). Since the JarInputStream handling is the same, I implemented it in a parseStream method.

parseStream loops around JarInputStream.getNextJarEntry(), which reads the next JAR file entry and positions the stream at the beginning of the data. Once parseStream has a JAR entry, it gets its name with JarEntry.getName() and uses a BufferedInputStream to read it. Then it converts it to a class with ClassLoader.defineClass and stores it in a classes memory cache. When it has to locally store a remotely downloaded archive, it uses a JarOutputStream; each time it's read an entry it rewrites it using JarOutputStream. putNextEntry() and JarOutputStream.write().

loadClass is invoked with two arguments, the name of the class and a boolean, resolve, that indicates if the class must be linked. Here I use the passive mode on purpose. Who invokes loadClass()? It depends. When ClassEntry invokes loadClass with the class name only, no magic happens. ClassLoader implements a loadClass(name) method that invokes loadClass(name, false). But the loaded class is associated with a JSPloader instance, which becomes the current class loader. If the loaded class uses another class, the Java Virtual Machine (JVM) will invoke JSPloader.loadClass to load it. This is why JSPloader.loadClass delegates class loading for the classes it doesn't find in its classes cache to the system class loader and its parent through the loadForward method.

The JSPloader.loadClass also delegates in two other interesting cases. If the class name starts with "java.", ClassLoader refuses to create it for security reasons. So I don't even try. The other case is "javax.servlet.Servlet". ClassEntry casts the target object it creates in a servlet. As I said, every class is associated with a class loader instance. In fact the JVM maintains the uniqueness of class_name, class_loader_object and not of class_name alone. So a cast of an object of class A loaded by class_loader_object1 to the same class loaded by class_loader_object2 fails. Therefore I check javax.servlet.Servlet and don't risk loading it from the archive.

Considerations
The order of the search has an obvious security impact. I prefer trying the class's memory cache first for speed and flexibility: I really depend on the Java server JDK for Java. I can download anything else, including the EJB, JMS, or RMI library code, but it has a security impact. If you don't trust your remote location, it's safer searching locally first.

My code is reasonably close to JDK 1.1 code: just replace HashMap with Hashtable and JarInputStream with ZipInputStream to run it with JDK 1.1. If local caching and JDK 1.1 have no value for you, consider URLClassLoader as an alternative to JSPloader. However, it's not really optimized for server-side use and you'd probably prefer the compatible NetworkClassLoader of Harish Prabandham provided in Tomcat. Its design is similar to JSPloader but instead of caching defined classes, it caches class data.

In Part 2 I'll describe how to handle images, support Web applications without restriction, and require updates from a browser. In Part 3 I'll demonstrate how to host downloaded classes in a sandbox, such as applets.

Conclusion
Through the class loader comprehensive mechanism it's easy to write a tool that's able to download servlets and JSPs from a remote location. It's even relatively easy to make it portable, though Java servers are probably the most hostile environment since they use class loaders intensively.

The idea probably has value for corporate intranets and B2Bs. Assume company B wants to provide access to its Web application to company A, which maintains a Java server. It simply configures its server to automatically download the code from B to enjoy reduced communication bills and better response time. It's a win-win situation since B doesn't have to process presentation. Now suppose A has many partners. As each downloaded archive is processed by a different class loader instance, it can use the same class names without collision. If A uses a different Web application for each partner, the partners won't share the same context. And A partners don't even have to know about A's Java server host and operating system. However, its real potential may be elsewhere.

If we could define a standard describing how to require a download and from where - for instance with XML over HTTP - even ISPs could host pages. Presentation would become a commodity like routing or a name service.

More Stories By Alexis Grandemange

Alexis Grandemange is an architect and system designer. A Java programmer since 1996 with a background in C++ and COM, his main interest is J2EE with a focus on design, optimization, and performance issues.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


IoT & Smart Cities Stories
Moroccanoil®, the global leader in oil-infused beauty, is thrilled to announce the NEW Moroccanoil Color Depositing Masks, a collection of dual-benefit hair masks that deposit pure pigments while providing the treatment benefits of a deep conditioning mask. The collection consists of seven curated shades for commitment-free, beautifully-colored hair that looks and feels healthy.
The textured-hair category is inarguably the hottest in the haircare space today. This has been driven by the proliferation of founder brands started by curly and coily consumers and savvy consumers who increasingly want products specifically for their texture type. This trend is underscored by the latest insights from NaturallyCurly's 2018 TextureTrends report, released today. According to the 2018 TextureTrends Report, more than 80 percent of women with curly and coily hair say they purcha...
The textured-hair category is inarguably the hottest in the haircare space today. This has been driven by the proliferation of founder brands started by curly and coily consumers and savvy consumers who increasingly want products specifically for their texture type. This trend is underscored by the latest insights from NaturallyCurly's 2018 TextureTrends report, released today. According to the 2018 TextureTrends Report, more than 80 percent of women with curly and coily hair say they purcha...
We all love the many benefits of natural plant oils, used as a deap treatment before shampooing, at home or at the beach, but is there an all-in-one solution for everyday intensive nutrition and modern styling?I am passionate about the benefits of natural extracts with tried-and-tested results, which I have used to develop my own brand (lemon for its acid ph, wheat germ for its fortifying action…). I wanted a product which combined caring and styling effects, and which could be used after shampo...
The platform combines the strengths of Singtel's extensive, intelligent network capabilities with Microsoft's cloud expertise to create a unique solution that sets new standards for IoT applications," said Mr Diomedes Kastanis, Head of IoT at Singtel. "Our solution provides speed, transparency and flexibility, paving the way for a more pervasive use of IoT to accelerate enterprises' digitalisation efforts. AI-powered intelligent connectivity over Microsoft Azure will be the fastest connected pat...
There are many examples of disruption in consumer space – Uber disrupting the cab industry, Airbnb disrupting the hospitality industry and so on; but have you wondered who is disrupting support and operations? AISERA helps make businesses and customers successful by offering consumer-like user experience for support and operations. We have built the world’s first AI-driven IT / HR / Cloud / Customer Support and Operations solution.
Codete accelerates their clients growth through technological expertise and experience. Codite team works with organizations to meet the challenges that digitalization presents. Their clients include digital start-ups as well as established enterprises in the IT industry. To stay competitive in a highly innovative IT industry, strong R&D departments and bold spin-off initiatives is a must. Codete Data Science and Software Architects teams help corporate clients to stay up to date with the mod...
At CloudEXPO Silicon Valley, June 24-26, 2019, Digital Transformation (DX) is a major focus with expanded DevOpsSUMMIT and FinTechEXPO programs within the DXWorldEXPO agenda. Successful transformation requires a laser focus on being data-driven and on using all the tools available that enable transformation if they plan to survive over the long term. A total of 88% of Fortune 500 companies from a generation ago are now out of business. Only 12% still survive. Similar percentages are found throug...
Druva is the global leader in Cloud Data Protection and Management, delivering the industry's first data management-as-a-service solution that aggregates data from endpoints, servers and cloud applications and leverages the public cloud to offer a single pane of glass to enable data protection, governance and intelligence-dramatically increasing the availability and visibility of business critical information, while reducing the risk, cost and complexity of managing and protecting it. Druva's...
BMC has unmatched experience in IT management, supporting 92 of the Forbes Global 100, and earning recognition as an ITSM Gartner Magic Quadrant Leader for five years running. Our solutions offer speed, agility, and efficiency to tackle business challenges in the areas of service management, automation, operations, and the mainframe.