Archive for the 'News' Category

Zero turn-around in Java?

Tuesday, November 21st, 2006

Zero turn-around is one of the selling points of PHP, Ruby, Python and other interpreted languages. Turns out it also works out-of-the-box with Aranea and here we’ll describe how we did it, what are the limitations and show off the compositional nature of Aranea. If you just want to get it to work see the reference manual entry on the topic.

Zero turn-around usually refers to the way changes made to the code are immediately visible in most interpreted languages. Indeed, since there is no compilation and almost no deployment, the time from making a change to seeing it in the browser is a fast as pressing Ctrl + S, Alt + Tab and F5. The situation in the JEE world is different — although compilation is relatively fast, together with deployment, cache reloads and framework initialization it might easily take up to a minute to see a change propagate in a large application.

The problem is that JVM spec clearly states that we can’t just reload code of a single class after it has been loaded into the JVM. HotSpot JVM offers some way by allowing to replace the code of the methods, but so far it forbids to anyhow alter structure and signatures, so it’s only of limited use. What we can do is to load classes using a particular (possibly our own) classloader, and then let it to be garbage collected among with all loaded classes. Then next time an instance is created the class will be loaded anew.

However even this won’t give us the desired result, as any existing instance of a class will still hold on to its previous definition (with the old classloader) and now we might actually have two conflicting class definitions in two different classloaders which can lead to all sorts of trouble. So we also need to somehow reload the object state in a new classloader.

Turns out that we can archive this with serialization — we just need to modify the ObjectInputStream to take a classloader parameter and load the instances using its classes. Thus what we have to do is:

  1. Load the classes using our classloader.
  2. Serialize the state of the instances
  3. Drop the old classloader and create a new one
  4. Deserialize state of the instances in the new classloader

Of course if some objects cannot be serialized (e.g do not implement Serializable or contain non-serializable fields) the whole scheme fails, since we cannot preserve their state.

Before we get to the actual Aranea stuff let’s try to implement the reloading procedure, that takes an object and reloads it using a fresh classloader. We know it should work through serialization, so it should look something like this:

...
private Serializable reload(Serializable child)
  throws Exception {
  return deepCopy(newClassLoader(), child);
}
...

deepCopy should just serialize and deserialize the object. There is one catch though — it should also resolve the classes using our own classloader, so we have to make a subclass of ObjectInputStream:

...
private static class ReloadingObjectInputStream
extends ObjectInputStream {
  private ClassLoader cl;

  public ReloadingObjectInputStream(
      ClassLoader cl,
      InputStream in)
    throws IOException {
    super(in);
    this.cl = cl;
  }

  /*
   * This method is used to resolve the classes
   * when creating object instances.
   */
  protected Class resolveClass(ObjectStreamClass desc)
    throws IOException, ClassNotFoundException {
    String name = desc.getName();
    return cl.loadClass(name);
  }
}
...

The deepCopy() method itself is straightforward and we could have used Apache SerializationUtils methods if it weren’t for the custom ObjectInputStream:

...
private  Serializable deepCopy(
    ClassLoader cl,
    Serializable original) throws Exception {
  //Serialize to a byte array
  ByteArrayOutputStream baos =
    new ByteArrayOutputStream(512);
  ObjectOutputStream out =
    new ObjectOutputStream(baos);
  try {
    out.writeObject(original);
  }
  finally {
    out.close();
  }

  byte[] serialized = baos.toByteArray();

  //Deserialize to an instance
  ByteArrayInputStream bais =
    new ByteArrayInputStream(serialized);

  ReloadingObjectInputStream in =
    new ReloadingObjectInputStream(cl, bais);
  Object obj = in.readObject();

  return (Serializable) obj;
}
...

We could have used just a usual classloader (most common is URLClassLoader) and just point its classpath to the “/WEB-INF/classes”. However there is also a trouble that the same classes are in the classpath of the web application classloader. The Java specification instructs classloaders to try loading classes with the parent classloader first, however in our case we would be able to reload those classes, so we need to invert this preference by delegating to parent only if we cannot find the class in the classpath:

...
private static class ReloadingClassloader
  extends URLClassLoader {

  public ReloadingClassloader(
      URL[] urls,
      ClassLoader parent) {
    super(urls, parent);
  }

  public Class loadClass(String name)
    throws ClassNotFoundException {
    //If already loaded just return
    Class c = findLoadedClass(name);
    if (c != null)
      return c;

    //First try own classpath
    //then delegate to parent
    try {
      return findClass(name);
    }
    catch (ClassNotFoundException e) {
      return super.loadClass(name);
    }
  }
}
...

We create the classloader by putting “/WEB-INF/classes” into the classpath. getEnvironment().getEntry() is just a way in Aranea to look up services, in this case we need a ServletContext:

...
private ClassLoader newClassLoader()
  throws MalformedURLException {
  //Get the ServletContext
  ServletContext sctx =
    (ServletContext) getEnvironment().getEntry(
        ServletContext.class);
  //Return a classloader for "/WEB-INF/classes"
  return new ReloadingClassloader(
      new URL[] {sctx.getResource("/WEB-INF/classes")},
      getClass().getClassLoader());
}
...

Now we have most of the machinery in place and before we move on to Aranea filter implementation we can spare a thought on the limitations of the solution. Obviously it will only work on serializable classes. What’s more, to get actual gain from it all classes should have serialVersionUID set to some fixed number (e.g. “0″). This should be done so that small changes in the method and class signatures wouldn’t cause a deserialization failure. Even then removing a class (even an inner or anonymous class) will cause serialization to fail as well as changing the field types or order. However it is still much more than HotSwap would allow (at least at the moment).

Now we can go on with implementing the Aranea filter that will do the work. There are several reasons why this solutions suits Aranea so well:

  • Aranea is originally assembled from independent components by containment. Therefore one filter can define the classloader to load its children and do the rest of the tricks.
  • All application widgets in Aranea are serializable and loaded by the parent classloader (most of the filters are configured by Spring and loaded by its classloader).
  • Aranea components can only access their parents through the environment, which can be taken away at any given moment. Moreover parents have references only to their direct children, and can communicate with the indirect children only through messages.

Thus we can create a filter and put it in appropriate place just above the application widgets and we will be able to seamlessly reload all application widgets code.

At the moment we will assume that creating a new classloader and serializing/deserializing does not visibly affect response time in development, so we will just reload all classes before every request. Thus we do not need to check exactly which classes have changed, since any possible changes will be reloaded.

Let’s start by creating the filter service itself. It will have to take the child class name as a string, since we will need to load it reflectively in a freshly created classloader.

public class StandardClassReloadingFilterWidget
    extends BaseApplicationWidget {

  private String childClassName;
  private RelocatableWidget child;

  public void setChildClass(String childClass) {
    this.childClassName = childClass;
  }

  protected void init() throws Exception {
    //Create the classloader and use it
    //to load the child class
    ClassLoader cl = newClassLoader();
    Class childClass = cl.loadClass(childClassName);

    //Create an instance of child class and
    //attach it to the filter
    child =
      new RelocatableDecorator(
          (Widget) childClass.newInstance());
    addWidget("c", child)'
  }
...

Here we create the child widget reflectively in the classloader and add it under a name “c”. The only interesting part is that we also wrap it into a RelocatableDecorator, which we will return to later. Now the next method is called before every request and will do the actual reloading:

...
protected void update(InputData input)
  throws Exception {
  try {
    //Remove all references to parents and
    //reload the child classes
    child._getRelocatable().overrideEnvironment(null);
    child = (RelocatableWidget) reload(child);
  }
  catch (ClassNotFoundException e) {
    log.error("Failed to reload widget classes", e);
  }
  finally {
    //Restore the references to parents
    child._getRelocatable()
      .overrideEnvironment(getEnvironment());
  }

  //Reattach the child new instance to the filter
  _getComposite().attach("c", child);
}
...

Here the point of wrapping the child into the RelocatableDecorator comes out — as all references from children to parents in Aranea have to go through the environment, by removing the environment from the child we also remove all possible links up allowing to serialize children only.

Now the filter is complete (except for the trivial render() method which we leave as an exercise for the reader) and can be tested in e.g. main example or any other Aranea application by adding a similar configuration entry:

<bean id="araneaApplicationStart" singleton="false"
  class="org.araneaframework.framework.filter.StandardClassReloadingFilterWidget">
  <property name="childClass" value="org.araneaframework.example.main.web.DevelWidget"/>
</bean>

Instead of the usual:

<bean id="araneaApplicationStart"
  class="org.araneaframework.example.main.web.LoginWidget"
  singleton="false"/>

You will most probably want to define a different root widget class (in this case DevelWidget), which will start the usual LoginWidget in a StandardFlowContainerWidget. Otherwise you will lose the reloading filter on login.

So, on the plus side we managed to build a useful feature that will save countless developer hours by adding just one filter in a correct place. We also worked through a nice enough example of how to add framework features to Aranea (there definitely are a couple of gotchas here, but they are few and described in the reference manual). The filter can be used to reload widgets and all their helper classes, which makes up basically all of the Aranea application web layer. Since JSPs and most other templating engines can also reload their code easily we get to see all of the changes to the web almost instantly.

On the minus side we are reloading all of the classes in the web layer and serializing/deserializing all of the state on every request, which may become costly for a large application. We could somewhat remedy it by checking that classes have changed before reloading them. Also this trick won’t work for the business layer, since it is usually not serializable and can be referenced from many different places. There are some possible ways around it by they need much more effort and deserve a separate post in the future :)

Aranea 1.0.2 Released

Friday, November 17th, 2006

A bugfix stable release, for detailed description see changelog.

Aranea 1.1M1 is delayed since we are working hard on integration with JSF, GWT and Struts. There is still hope to get at least most of them working by Javapolis and demo it during the presentation.

Aranea 1.0.1 Released

Friday, November 3rd, 2006

In addition to fixes to several bugs that were found in the 1.0 release we have also implemented support for JSP 2.0 and Expression Language by providing a alternative tag library URI:

http://araneaframework.org/tag-library/standard_rt.

To get JSP 2.0 to work just use it instead of the usual URI.

In other news we are working on both new features like AJAX-based partial rendering and integration with other frameworks. We hope to release the 1.1 M1 release in the upcoming weeks including the features we’ve been working on, integration subprojects will come separately from main Aranea releases in their own tempo.

Aranea Kibitzed

Thursday, October 19th, 2006

Our good friends at SourceKibitzer.org have produced software metrics for Aranea framework source code including trends and complexity metrics. We plan to use this tool to finally get a hang of where our source code (which becomes bigger by the hour) is moving :)

Currently they provide basic metrics that give the overall feeling of the project source quality and trends for many Java Open-Source projects. They promise to provide more and more metrics by the moment so definitely keep watch for more interesting stuff.

Aranea 1.0 Final Release Available

Friday, October 13th, 2006

We are glad to announce the immediate availability of Aranea Web Framework 1.0 final release.

Aranea 1.0 is the first stable release of Aranea Web Framework. The API are now fully frozen until 2.x release cycle and documentation includes a 120+ pages Reference Manual, an Introductory Tutorial, a Hands-On Tutorial, Technical Report, Javadoc API and example applications. The framework is widely used in-house in both large and small projects and by now has been successfully deployed to production in most of them.

Aranea is an Open-Source Object-Oriented Web Application Framework that focuses on integration, maintainability and reuse. Our vision of Aranea includes three main themes:

  1. An Object-Oriented Framework. Aranea provides the component model based on static types and usual objects that facilitates applying Object-Oriented design to achieve a high level of maintainability and reuse. It also provides an extensive reusable component library that helps build complex and dynamic user interfaces.
  2. A Lightweight Web Application Integration Platform. Using Aranea component object model it becomes trivial to integrate and assemble seamlessly applications from disparate components. And since a whole application can be considered just a component it is just as simple to integrate whole applications, even dynamically. Aranea can be seen as a viable lightweight alternative to Portlets, especially with remote integration on the way.
  3. A Web Framework Integration Platform. Instead of competing with the numerous web frameworks, we want to harness their power and variety by integrating them with Aranea. We’d like to enable developers to make use of any framework features or components they find useful in their applications and Aranea to serve as glue among the application components. We especially want to support making different components or parts of application using different framework, as different requirements command different approaches.

The next step in Aranea development will focus on the third theme, which at the moment is represented in prototype stage only. Our first candidates for integration are Google Web Toolkit, Java Server Faces and Struts, with the rest to follow later. We expect to present a working integration demo on the JavaPolis 2006 conference in December.

Aranea development is supported by Webmedia, Ltd.

Webmedia is a system development and consultancy company focusing on developing long-term partnerships with corporate customers in public and private sector. Today, Webmedia is one of the largest software development companies in the Baltics, currently employing over 200 people in software development with front offices and development in Estonia, Lithuania and Finland, and an additional development centers in Romania and Serbia. Webmedia sees the software development process as very complex but still dynamic where different competences have to be combined in order to achieve quality applications as well as return on investment. Webmedia offers complete application life-cycle management services.

Aranea 1.0 RC4 Released

Monday, October 9th, 2006

Although almost a month have passed since the 1.0 RC3 release we have not been leisuring around. This release brings you a feature complete, API frozen and documented Aranea, as never seen before! :)

In all seriousness we expect to release the 1.0 GA this Friday at 17:00 EEST. We plan to release the Release Candidate 5 some time this week (most probably Wednesday). RC5 will include some updates to documentation and fixes to any critical bugs we might find during this week, and unless there will be a divine interference, we will declare it to be final (though considering that we release on Friday the 13th, we should rather expect infernal interference).

Changes in Release Candidate 4 include:

  • Last minute cleanup of Lists and Form Lists API
  • Major updates to Reference Manual
  • Some updates to Javadoc and Tutorials
  • A lot of fixes…

Aranea at JavaPolis

Friday, September 15th, 2006

It is our pleasure to announce that “Object-Oriented Web Development” talk will be presented at this year’s JavaPolis. We hope that by this time the last part of the talk concerning integration will reflect the real code in our repository and not just plans :)

Considering that there are almost three months until that period and that our company is going to be present on the expo it is very likely that we will make a full-blown “Aranea with Integration” announcement (and likely a release party) at the JavaPolis.

It will be great to visit again Antwerp and I’m really looking forward to seeing 5-meter-tall word Aranea on the Metropolis screens :)

JavaZone talk

Wednesday, September 13th, 2006

At JavaZone we presented the talk “Object-Oriented Web Development with Aranea”. The talk was received well, with about 200 people attending, and the presentation is available for download. The podcast is also available, though due to technical problems beginning of the talk got lost (it starts from slide 7 and goes on more-or-less fine).

Thanks a lot to the organizers for a wonderful (and still continuing) experience! JavaZone organization is top notch and wireless connection is very available :) . Oslo is a beautiful city, and I look forward to seeing more of it now that my talk is over and I finally can breath freely…

Aranea 1.0 RC3

Monday, September 11th, 2006

Aranea RC2 is behaving well in projects that are using it currently. We have decided to postpone our final 1.0 release in favour of few more release candidates however—we feel that some areas of documentation are still seriously lacking and will not make final release until these shortcomings have been addressed. If our reader should have any comments on our current documentation, please do not hesitate to drop a mail or participate on our forums.

In third release candidate, FormElement Constraint internals have been rewritten (go hunt for new bugs), and documentation has received some minor updates. We hoped to do more docs, but I myself have supported migration of some Aranea-based projects to recent release candidate versions to ensure a bigger testbed, and Jevgeni has been busy polishing his JavaZone talk. Overall we are very happy because this week another developer, Alar, is joining us :)

Let us know about your gripes with Aranea documentation!

Aranea 1.0 RC2

Friday, September 1st, 2006

This is a bugfix and maintenance release following bigger refactors that were made for RC1 (aka “mostly-working-candidate”).

Important bugs fixed

  • forms losing state on page reloads
  • zombified widgets when formelement (control) actionlisteners were invoked
  • broken list filters
  • open in new window feature not working with url-encoded session identificators

Additionally lists LIKE filters have been rewritten, MessageContext has gotten some new methods and few minor renames took place (all documented in changelog). Javadoc has been substantially cleaned up and updated.

More updates due next week!