Navigator7 and Google app Engine

I was wondering if anyone has tried to run an application using Navigator7 on Google App Engine.

Looking at the book of Vaadin to run on GAE you must change web.xml to use GAEApplicationServlet instead of ApplicationServlet.

I also noticed Navigator7 uses its own servlet NavigableApplicationServlet.

Am I correct in assuming Navigator7 will not run without modification on GAE?

I can’t see any option to run it without modifications.
But it is not hard to “tune” it - i see two options.

First option is to look into NavigableApplicationServlet code and create your own class, e.g. NavigableGAEApplicationServlet extends GAEApplicationServlet … and i think just copy methods from NavigableApplicationServlet (it should work, but i haven’t tested it)

The other option is to get rid of navigator7 servlet extention. The only reason to represent custom servlet was to create only one instance of application configuration. So you can create your own singleton with configuration and get rid of WebApplication class. But in this way you will need to modify all usages of WebApplication to usages of your configuration class (actually it’s quite easy).

In my app, i’ve replaced WebApplication with spring singletone bean. (and of cause, as i mention above changed all references to WebApplication to my bean)

Here is the code of configuration bean:

   NavigatorConfig navigatorConfig = new NavigatorConfig();

    ParamUriAnalyzer uriAnalyzer = new ParamUriAnalyzer();


    def AppConfiguration() {
        navigatorConfig.registerPages "com.domain.appname" // i've implemented this method to scan annotations in classpath, but you could use registerPage methods to register page classes in cannonical way
        navigatorConfig.registerInterceptor new AuthInterceptor() // it is so easy to secure page transitions!
    }

This is groovy, so getters for navigatorConfig and uriAnalyzer are implicit.

Hope it helps

I just now got GAE to play nicely with Navigator7. I started with Vyacheslav Rusakov’s suggetion, and quickly found that I got errors by just extending GAEApplicationServlet and having the code of NavigableApplicationServlet in a new class. I tried a lot of more elegant solutions than what I ended up going with, but none of them worked, so here’s the solution I’m using.

  1. Copy the following classes into a package that you created, like blah.server. You will need to modify GAEApplicationServlet, and unfortunately the folks at Vaadin used good programming practices, like protected scoped variables and methods. Alternately, you could just unpack the vaadin jar, modify GAEApplicationServlet (step 2) and re-jar it, but you will need to do this process every time you update your vaadin to the next version. This way, until Vaadin 7 includes all of this natively, you shouldn’t have to worry when you upgrade, but it’s up to you.
    AbstractApplicationServlet
    AbstractCommunicationManager
    AbstractWebApplicationContext
    ApplicationServlet
    CommunicationManager
    ComponentSizeValidator
    DragAndDropService
    GAEApplicationServlet
    JsonPaintTarget
    WebApplicationContext
    WebBrowser

  2. Comment out (or remove) all of the calls to cleanSession(HttpRequest). All of the calls are made from the service() method. cleanSession() removes the ApplicationContext, and Navigator7 gets confused about what pages have or haven’t been registered, i.e. causes errors. The service() method in your GAEApplicationServlet should look like this.

@Override
    protected void service(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        if (isCleanupRequest(request)) {
            cleanDatastore();
            return;
        }

        RequestType requestType = getRequestType(request);

        if (requestType == RequestType.STATIC_FILE) {
            // no locking needed, let superclass handle
            super.service(request, response);
//            cleanSession(request);
            return;
        }

        if (requestType == RequestType.APPLICATION_RESOURCE) {
            // no locking needed, let superclass handle
            getApplicationContext(request,
                    MemcacheServiceFactory.getMemcacheService());
            super.service(request, response);
//            cleanSession(request);
            return;
        }

        final HttpSession session = request
                .getSession(requestCanCreateApplication(request, requestType));
        if (session == null) {
            handleServiceSessionExpired(request, response);
//            cleanSession(request);
            return;
        }

        boolean locked = false;
        MemcacheService memcache = null;
        String mutex = MUTEX_BASE + session.getId();
        memcache = MemcacheServiceFactory.getMemcacheService();
        try {
            // try to get lock
            long started = new Date().getTime();
            // non-UIDL requests will try indefinitely
            while (requestType != RequestType.UIDL
                    || new Date().getTime() - started < MAX_UIDL_WAIT_MILLISECONDS) {
                locked = memcache.put(mutex, 1, Expiration.byDeltaSeconds(40),
                        MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT);
                if (locked) {
                    break;
                }
                try {
                    Thread.sleep(RETRY_AFTER_MILLISECONDS);
                } catch (InterruptedException e) {
                    log.info("Thread.sleep() interrupted while waiting for lock. Trying again. "
                            + e);
                }
            }

            if (!locked) {
                // Not locked; only UIDL can get trough here unlocked: tell
                // client to retry
                response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
                // Note: currently interpreting Retry-After as ms, not sec
                response.setHeader("Retry-After", "" + RETRY_AFTER_MILLISECONDS);
                return;
            }

            // de-serialize or create application context, store in session
            ApplicationContext ctx = getApplicationContext(request, memcache);
            
           super.service(request, response);

            // serialize
            started = new Date().getTime();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(ctx);
            oos.flush();
            byte[] bytes = baos.toByteArray();

            started = new Date().getTime();

            String id = AC_BASE + session.getId();
            Date expire = new Date(started
                    + (session.getMaxInactiveInterval() * 1000));
            Expiration expires = Expiration.onDate(expire);

            memcache.put(id, bytes, expires);

            DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
            Entity entity = new Entity(AC_BASE, id);
            entity.setProperty(PROPERTY_EXPIRES, expire.getTime());
            entity.setProperty(PROPERTY_DATA, new Blob(bytes));
            ds.put(entity);

        } catch (DeadlineExceededException e) {
            log.severe("DeadlineExceeded for " + session.getId());
            sendDeadlineExceededNotification(request, response);
        } catch (NotSerializableException e) {
            log.severe("NotSerializableException: " + getStackTraceAsString(e));

            // TODO this notification is usually not shown - should we redirect
            // in some other way - can we?
            sendNotSerializableNotification(request, response);
        } catch (Exception e) {
            log.severe(e + ": " + getStackTraceAsString(e));

            sendCriticalErrorNotification(request, response);
        } finally {
            // "Next, please!"
            if (locked) {
                memcache.delete(mutex);
            }
//            cleanSession(request);
        }
    }
  1. Make a NavigableGAEApplicationServlet by doing exactly as Vyacheslav Rusakov suggested
 It looks like this
public class NavigableGAEApplicationServlet extends GAEApplicationServlet {

    private static final long serialVersionUID = 4497831784753441059L;

    /**
     * Called by the servlet container to indicate to a servlet that the servlet
     * is being placed into service.
     * 
     * @param servletConfig
     *            the object containing the servlet's configuration and
     *            initialization parameters
     * @throws javax.servlet.ServletException
     *             if an exception has occurred that interferes with the
     *             servlet's normal operation.
     */
    @SuppressWarnings("unchecked")
    @Override
    public void init(javax.servlet.ServletConfig servletConfig)
            throws javax.servlet.ServletException {
        super.init(servletConfig);

        WebApplication.init(servletConfig, getServletContext(), getClassLoader());
    }
    
    /** I'd prefer to do that in a Filter, but it would be against the Vaadin current architecture 
     * Note that Vaadin TransactionListeners have no access to the ServletContext => we cannot use TransactionListeners. */
    @SuppressWarnings("unchecked")
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        WebApplication.beforeService(request, response, getServletContext());
        super.service(request, response);
        WebApplication.afterService(request, response, getServletContext());
    }
    
    
}
  1. At this point, your app will work on GAE, but it will throw up lots of unable to serialize errors. This is because many of the Navigator7 classes don’t implement Serializable. Modify the appropriate classes to implement Serializable using either method described in Step 1. Those classes are
    ParamChangeListenerInterceptor
    ParamInjectInterceptor
    UriAnalyzer
    WebApplication
    NavigatorConfig
    If you just modify the Navigator7 jar, you don’t have to do anything. If you copy the relevent classes into your own package, you’ll need to reference you’re instance of WebApplication in your NavigableGAEApplicationServlet and where you register the pages. Again, which way depends on how often you plan on updating Navigator7.

Hi,

I have just hit this issue myself and I’m going through the steps outlined in the post.

Checking out the source for Navigator7 and using the latest jar (7.49) it appears that the following classes still an’t Serializable? Is there any reason for this? I would much prefer to use a new 7.50 version than package my own Serializable Navigator7 classes.

Could they be added into a new version?

ParamChangeListenerInterceptor
ParamInjectInterceptor
UriAnalyzer
WebApplication
NavigatorConfig

Thanks,
Craig.