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.
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.
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
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);
}
}
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());
}
}
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.
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.