2012/06/24

outside the comfort zone

One of the first things that I liked in NetKernel was that I could easily create applications and make them accessible on the intranet/internet without knowing a whole lot about webserver deployment, jar and war, web.xml, ...

I still don't. I come from a system administrator background. While I can drop files in the directories I'm told to drop them in, I haven't really got a clue what the average (non NetKernel) Java webapplication project looks like, where the classes are, where the static content lives, ... In my everyday explorations, NetKernel abstracts all that for me.

That is of course no excuse ! So when I recently heard that Vaadin was in the comfortzone of quite a lot of developers that would potentially benefit from using NetKernel, I challenged myself to either :
  • Run a Vaadin application inside NetKernel
  • Run NetKernel inside a Vaadin application
Now, using NetKernel from any Java application is trivial, you can point your datasources (or whatever) to a NetKernel exposed endpoint. But that was not the challenge.

I quickly figured out that running a Vaadin application inside NetKernel was beyond me. I could make it so that it ran alongside the other stuff in the fulcrums (= Jetty Webserver), but inside ... no. Maybe it can be done and if anybody knows how I'd be obliged if you tell me, but I could not make that work.

Running NetKernel inside Vaadin however had to work. After all this usage mode is documented in the embedded-demo (courtesy of 1060 Research, just so the thunder god doesn't accuse me of stealing his thunder ... I don't want to end up like Prometeus). And it did ! But - for me - it was not trivial to do it, so here's the low down :
  1. Since you are not serving in NetKernel, you need another webserver. I chose Apache Tomcat, which you can find here.
  2. The Vaadin website has an explicit HelloVaadin example, created with the Vaadin Eclipse Plugin. So I started up Eclipse and installed that plugin.
  3. Back up a step or I guarantee some trouble down the line. I was running the standard Eclipse installation. That got me all kinds of strange errors when creating Vaadin projects, so I installed a fresh Java EE Eclipse and installed the Vaadin Eclipse Plugin on that.
  4. With all that done I followed the HelloVaadin example on the Vaadin site to create a new Vaadin project in Eclipse and pretty soon I had that running inside Eclipse using my installed Tomcat.
  5. So far so good and I decided that the "Hello Vaadin" message had to be replaced with a NetKernel resource calling a groovy program.
  6. You can download my NetKernel module for that here. You'll notice that the module contains an etc directory unused by the module itself that contains a kernel.properties file and a modules.conf file. The former will be used by the embedded NetKernel, the latter contains the locations of the modules that will be known inside the embedded NetKernel. Adapt it to your locations.
  7. Time to modify our HelloVaadin example in Eclipse. Under Java Resources -> Libraries you need to add the NetKernel library. And you also need to copy the jars from that to WebContent -> WEB-INF -> lib where you'll also find the vaadin jar itself.
  8. Under the Java Resources -> src -> <yourpackagename> you'll find the java program itself. I turned that into this :

    package com.example.hellovaadin;

    import com.vaadin.Application;
    import com.vaadin.ui.*;

    import com.ten60.netkernel.cache.se.representation2.ConcurrentCache;
    import com.ten60.netkernel.cache.se.resolution.ResolutionCache;
    import org.netkernel.container.ILogger;
    import org.netkernel.container.impl.Kernel;
    import org.netkernel.layer0.boot.IModuleFactory;
    import org.netkernel.layer0.boot.ModuleManager;
    import org.netkernel.layer0.logging.LogManager;
    import org.netkernel.layer0.nkf.INKFRequestContext;
    import org.netkernel.layer0.nkf.NKFException;
    import org.netkernel.layer0.nkf.impl.NKFTransportContextImpl;
    import org.netkernel.layer0.urii.SimpleIdentifierImpl;
    import org.netkernel.layer0.util.PropertyConfiguration;
    import org.netkernel.layer0.tools.ExtraMimeTypes;
    import org.netkernel.module.standard.StandardModuleFactory;
    import org.netkernel.urii.IIdentifier;
    import org.netkernel.urii.ISpace;

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.URI;
    import java.net.URL;

    public class HellovaadinApplication extends Application {
        private static int SYSTEM_RUN_LEVEL = 1;
        private ModuleManager mModuleManager;
        private ConcurrentCache mRepresentationCache;
        private INKFRequestContext mContext;

        private void startNetKernel() throws Exception {
            // Create a new micro-kernel.
            Kernel kernel = new Kernel();

            // Get system classloader so we can load local physical resources
            ClassLoader classLoader = HellovaadinApplication.class.getClassLoader();

            // NetKernel adds support for missing MIME types to the JDK.
            ExtraMimeTypes.getInstance();

            // NetKernel requires a logger
            ILogger logger = new LogManager(null).getKernelLogger();

            // Set kernel properties.
            // Here read the kernel properties from the property file in the
            // classpath.
            URL kernelProperties = new URL("file:/X:/NK5/project-modules/urn.org.elbeesee.tutorial.embedded.module-1.0.0/etc/kernel.properties");
            PropertyConfiguration config = new PropertyConfiguration(kernelProperties, logger);
            config.setProperty("netkernel.boot.time", Long.toString(System.currentTimeMillis()));
            kernel.setConfiguration(config);
            kernel.setLogger(logger);

            // >>>>> Start of optional cache configuration
            // These two caches are optional, if used then add both as a pair

            // Representation Cache stores computed resource representations
            mRepresentationCache = new ConcurrentCache(kernel);
            kernel.setRepresentationCache(mRepresentationCache);
            kernel.addConfigurationListener(mRepresentationCache);

            // Resolution Cache stores resolved endpoints
            ResolutionCache rcache = new ResolutionCache(kernel);
            kernel.setResolutionCache(rcache);
            kernel.addConfigurationListener(rcache);
            // <<<<< End of optional cache loading code

            // Instantiate the modules factories - only use StandardModule here.
            IModuleFactory[] factories = new IModuleFactory[] { new StandardModuleFactory() };

            // Create a Module Manager
            mModuleManager = new ModuleManager(kernel, factories);
            InputStreamReader is = new InputStreamReader((new URL("file:/X:/NK5/project-modules/urn.org.elbeesee.tutorial.embedded.module-1.0.0/etc/modules.conf")).openStream());
             BufferedReader r = new BufferedReader(is);
            String line;

            // Process the module configuration file
            while ((line = r.readLine()) != null)
            {
                line = line.trim();
                if (!line.startsWith("#") && line.length() > 0)
                {
                    URI source = new URI(line);
                    // Tell the module manager about the module and set its runlevel
                    mModuleManager.addModule(source, SYSTEM_RUN_LEVEL);
                }
            }

            // Set run level - this commissions all modules <= RUNLEVEL and moves
            // the kernel to active state.
            mModuleManager.setRunLevel(SYSTEM_RUN_LEVEL);

            // Kernel is now live and modules are commissioned!

            // Now create a transport context so that we can make requests.
            // Create a reference to the rootspace of our demo module
            IIdentifier spaceIdentifier = new SimpleIdentifierImpl("urn:org:elbeesee:tutorial:embedded:module:public");
            ISpace applicationRootSpace = kernel.getSpace(spaceIdentifier, null, null);

            // Create the INKFRequestContext
            mContext = new NKFTransportContextImpl(kernel, applicationRootSpace, this);
            // We're now ready to make requests!
        }

        private String getHelloWorld() throws NKFException {
            return mContext.source("res:/hellovaadin", String.class);
        }
        private void stopNetKernel()
        {
            if (mModuleManager != null)
            {    // This blocks further requests and then shuts down the kernel
                mModuleManager.stop();
            }
            if (mRepresentationCache != null)
            {    // This stops the monitor thread in the cache
                mRepresentationCache.stop();
            }
        }

        public void close() {
            try {
                stopNetKernel();
            }
            catch (Exception e) {
                e.printStackTrace();
            };
            super.close();
        }

        public void init() {
            try {
              startNetKernel();
            }
            catch (Exception e) {
                e.printStackTrace();
            };
            Window mainWindow = new Window("Hellovaadin Application");
            Label label = null;
            try {
                label = new Label(getHelloWorld());
            } catch (NKFException e) {
                e.printStackTrace();
            }
            mainWindow.addComponent(label);
            setMainWindow(mainWindow);
        }
    }


  9. If this compiles you've done the setup correctly. All that remains is to run it and lo and behold ... the message is served through a NetKernel groovy endpoint (the console will tell you if you did something wrong, you should see a small NetKernel instance being booted up in there). 
That is it. Well, that was a whole lot and if somebody tells me the learning curve of NetKernel is steep again, try doing the above without a lot of knowledge about Java webapplications (like I did) !

One more quid pro quo. The above is not perfect. Files modules.conf and kernel.properties should be loaded from the application's classpath rather then from fixed files. Since this was my first Vaadin experience I didn't know (nor could I quickly find it) how to do that. Etc.

Enjoy !