diff options
author | Pekka Hyvönen <pekka@vaadin.com> | 2016-12-20 14:36:53 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-12-20 14:36:53 +0200 |
commit | 1407b8ddaea9a8aace0973864ad9ce093e20a566 (patch) | |
tree | 59c512fc7a5cf3c05b65da5cf6bd80cf44fff293 /server | |
parent | 912c86800baa0b4260c472fd06dc9360fb5c6666 (diff) | |
download | vaadin-framework-1407b8ddaea9a8aace0973864ad9ce093e20a566.tar.gz vaadin-framework-1407b8ddaea9a8aace0973864ad9ce093e20a566.zip |
Remove Google App Engine support, tests and documentation (#8034)
* Remove Google App Engine support and tests
Fixes #8033
Diffstat (limited to 'server')
-rw-r--r-- | server/pom.xml | 7 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/server/GAEVaadinServlet.java | 460 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/server/VaadinServlet.java | 17 | ||||
-rw-r--r-- | server/src/main/java/com/vaadin/util/SerializerHelper.java | 160 |
4 files changed, 8 insertions, 636 deletions
diff --git a/server/pom.xml b/server/pom.xml index ef586bd6dd..07da0ff9fb 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -39,13 +39,6 @@ <scope>provided</scope> </dependency> - <!-- Google App Engine --> - <dependency> - <groupId>com.google.appengine</groupId> - <artifactId>appengine-api-1.0-sdk</artifactId> - <scope>provided</scope> - </dependency> - <!-- Bean Validation API --> <dependency> <groupId>javax.validation</groupId> diff --git a/server/src/main/java/com/vaadin/server/GAEVaadinServlet.java b/server/src/main/java/com/vaadin/server/GAEVaadinServlet.java deleted file mode 100644 index bfa2925dcb..0000000000 --- a/server/src/main/java/com/vaadin/server/GAEVaadinServlet.java +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Copyright 2000-2016 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.NotSerializableException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import com.google.appengine.api.datastore.Blob; -import com.google.appengine.api.datastore.DatastoreService; -import com.google.appengine.api.datastore.DatastoreServiceFactory; -import com.google.appengine.api.datastore.Entity; -import com.google.appengine.api.datastore.EntityNotFoundException; -import com.google.appengine.api.datastore.FetchOptions.Builder; -import com.google.appengine.api.datastore.Key; -import com.google.appengine.api.datastore.KeyFactory; -import com.google.appengine.api.datastore.PreparedQuery; -import com.google.appengine.api.datastore.Query; -import com.google.appengine.api.datastore.Query.FilterOperator; -import com.google.appengine.api.memcache.Expiration; -import com.google.appengine.api.memcache.MemcacheService; -import com.google.appengine.api.memcache.MemcacheServiceFactory; -import com.google.apphosting.api.DeadlineExceededException; - -/** - * ApplicationServlet to be used when deploying to Google App Engine, in - * web.xml: - * - * <pre> - * <servlet> - * <servlet-name>HelloWorld</servlet-name> - * <servlet-class>com.vaadin.server.GAEApplicationServlet</servlet-class> - * <init-param> - * <param-name>UI</param-name> - * <param-value>com.vaadin.demo.HelloWorld</param-value> - * </init-param> - * </servlet> - * </pre> - * - * Session support must be enabled in appengine-web.xml: - * - * <pre> - * <sessions-enabled>true</sessions-enabled> - * </pre> - * - * Appengine datastore cleanup can be invoked by calling one of the applications - * with an additional path "/CLEAN". This can be set up as a cron-job in - * cron.xml (see appengine documentation for more information): - * - * <pre> - * <cronentries> - * <cron> - * <url>/HelloWorld/CLEAN</url> - * <description>Clean up sessions</description> - * <schedule>every 2 hours</schedule> - * </cron> - * </cronentries> - * </pre> - * - * It is recommended (but not mandatory) to extract themes and widgetsets and - * have App Engine server these statically. Extract VAADIN folder (and it's - * contents) 'next to' the WEB-INF folder, and add the following to - * appengine-web.xml: - * - * <pre> - * <static-files> - * <include path="/VAADIN/**" /> - * </static-files> - * </pre> - * - * Additional limitations: - * <ul> - * <li/>Do not change application state when serving an ApplicationResource. - * <li/>Avoid changing application state in transaction handlers, unless you're - * confident you fully understand the synchronization issues in App Engine. - * <li/>The application remains locked while uploading - no progressbar is - * possible. - * </ul> - */ -public class GAEVaadinServlet extends VaadinServlet { - - // memcache mutex is MUTEX_BASE + sessio id - private static final String MUTEX_BASE = "_vmutex"; - - // used identify ApplicationContext in memcache and datastore - private static final String AC_BASE = "_vac"; - - // UIDL requests will attempt to gain access for this long before telling - // the client to retry - private static final int MAX_UIDL_WAIT_MILLISECONDS = 5000; - - // Tell client to retry after this delay. - // Note: currently interpreting Retry-After as ms, not sec - private static final int RETRY_AFTER_MILLISECONDS = 100; - - // Properties used in the datastore - private static final String PROPERTY_EXPIRES = "expires"; - private static final String PROPERTY_DATA = "data"; - - // path used for cleanup - private static final String CLEANUP_PATH = "/CLEAN"; - // max entities to clean at once - private static final int CLEANUP_LIMIT = 200; - // appengine session kind - private static final String APPENGINE_SESSION_KIND = "_ah_SESSION"; - // appengine session expires-parameter - private static final String PROPERTY_APPENGINE_EXPIRES = "_expires"; - - // sessions with undefined (-1) expiration are limited to this, but explicit - // longer timeouts can be used - private static final int DEFAULT_MAX_INACTIVE_INTERVAL = 24 * 3600; - - protected void sendDeadlineExceededNotification( - VaadinServletRequest request, VaadinServletResponse response) - throws IOException { - criticalNotification(request, response, "Deadline Exceeded", - "I'm sorry, but the operation took too long to complete. We'll try reloading to see where we're at, please take note of any unsaved data...", - "", null); - } - - protected void sendNotSerializableNotification(VaadinServletRequest request, - VaadinServletResponse response) throws IOException { - criticalNotification(request, response, "NotSerializableException", - "I'm sorry, but there seems to be a serious problem, please contact the administrator. And please take note of any unsaved data...", - "", - getApplicationUrl(request).toString() + "?restartApplication"); - } - - protected void sendCriticalErrorNotification(VaadinServletRequest request, - VaadinServletResponse response) throws IOException { - criticalNotification(request, response, "Critical error", - "I'm sorry, but there seems to be a serious problem, please contact the administrator. And please take note of any unsaved data...", - "", - getApplicationUrl(request).toString() + "?restartApplication"); - } - - @Override - protected void service(HttpServletRequest unwrappedRequest, - HttpServletResponse unwrappedResponse) - throws ServletException, IOException { - VaadinServletRequest request = new VaadinServletRequest( - unwrappedRequest, getService()); - VaadinServletResponse response = new VaadinServletResponse( - unwrappedResponse, getService()); - - if (isCleanupRequest(request)) { - cleanDatastore(); - return; - } - - if (isStaticResourceRequest(request)) { - // no locking needed, let superclass handle - super.service(request, response); - cleanSession(request); - return; - } - - if (ServletPortletHelper.isAppRequest(request)) { - // no locking needed, let superclass handle - getApplicationContext(request, - MemcacheServiceFactory.getMemcacheService()); - super.service(request, response); - cleanSession(request); - return; - } - - final HttpSession session = request - .getSession(getService().requestCanCreateSession(request)); - if (session == null) { - try { - getService().handleSessionExpired(request, response); - } catch (ServiceException e) { - throw new ServletException(e); - } - 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 = System.currentTimeMillis(); - while (System.currentTimeMillis() - - started < MAX_UIDL_WAIT_MILLISECONDS) { - locked = memcache.put(mutex, 1, Expiration.byDeltaSeconds(40), - MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT); - if (locked || ServletPortletHelper.isUIDLRequest(request)) { - /* - * Done if we got a lock. Will also avoid retrying if - * there's a UIDL request because those are retried from the - * client without keeping the server thread stalled. - */ - break; - } - try { - Thread.sleep(RETRY_AFTER_MILLISECONDS); - } catch (InterruptedException e) { - getLogger() - .finer("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 - VaadinSession 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 + (getMaxInactiveIntervalSeconds(session) * 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) { - getLogger().log(Level.WARNING, "DeadlineExceeded for {0}", - session.getId()); - sendDeadlineExceededNotification(request, response); - } catch (NotSerializableException e) { - getLogger().log(Level.SEVERE, "Not serializable!", e); - - // TODO this notification is usually not shown - should we redirect - // in some other way - can we? - sendNotSerializableNotification(request, response); - } catch (Exception e) { - getLogger().log(Level.WARNING, - "An exception occurred while servicing request.", e); - - sendCriticalErrorNotification(request, response); - } finally { - // "Next, please!" - if (locked) { - memcache.delete(mutex); - } - cleanSession(request); - } - } - - /** - * Returns the maximum inactive time for a session. This is used for - * handling the expiration of session related information in caches etc. - * - * @param session - * @return inactive timeout in seconds, greater than zero - */ - protected int getMaxInactiveIntervalSeconds(final HttpSession session) { - int interval = session.getMaxInactiveInterval(); - if (interval <= 0) { - getLogger().log(Level.FINE, - "Undefined session expiration time, using default value instead."); - return DEFAULT_MAX_INACTIVE_INTERVAL; - } - return interval; - } - - protected VaadinSession getApplicationContext(HttpServletRequest request, - MemcacheService memcache) throws ServletException { - HttpSession session = request.getSession(); - String id = AC_BASE + session.getId(); - byte[] serializedAC = (byte[]) memcache.get(id); - if (serializedAC == null) { - DatastoreService ds = DatastoreServiceFactory.getDatastoreService(); - Key key = KeyFactory.createKey(AC_BASE, id); - Entity entity = null; - try { - entity = ds.get(key); - } catch (EntityNotFoundException e) { - // Ok, we were a bit optimistic; we'll create a new one later - } - if (entity != null) { - Blob blob = (Blob) entity.getProperty(PROPERTY_DATA); - serializedAC = blob.getBytes(); - // bring it to memcache - memcache.put(AC_BASE + session.getId(), serializedAC, - Expiration.byDeltaSeconds( - getMaxInactiveIntervalSeconds(session)), - MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT); - } - } - if (serializedAC != null) { - ByteArrayInputStream bais = new ByteArrayInputStream(serializedAC); - ObjectInputStream ois; - try { - ois = new ObjectInputStream(bais); - VaadinSession vaadinSession = (VaadinSession) ois.readObject(); - getService().storeSession(vaadinSession, - new WrappedHttpSession(session)); - } catch (IOException | ClassNotFoundException e) { - getLogger().log(Level.WARNING, - "Could not de-serialize ApplicationContext for " - + session.getId() - + " A new one will be created. ", - e); - } - } - - // will create new context if the above did not - try { - return getService().findVaadinSession(createVaadinRequest(request)); - } catch (Exception e) { - throw new ServletException(e); - } - } - - private boolean isCleanupRequest(HttpServletRequest request) { - String path = request.getPathInfo(); - if (path != null && path.equals(CLEANUP_PATH)) { - return true; - } - return false; - } - - /** - * Removes the ApplicationContext from the session in order to minimize the - * data serialized to datastore and memcache. - * - * @param request - */ - private void cleanSession(VaadinServletRequest request) { - // Should really be replaced with a session storage API... - WrappedSession wrappedSession = request.getWrappedSession(false); - if (wrappedSession == null) { - return; - } - VaadinSession serviceSession = getService().loadSession(wrappedSession); - if (serviceSession == null) { - return; - } - - /* - * Inform VaadinSession.valueUnbound that it should not kill the session - * even though it gets unbound. - */ - serviceSession.setAttribute( - VaadinService.PRESERVE_UNBOUND_SESSION_ATTRIBUTE, Boolean.TRUE); - getService().removeSession(serviceSession.getSession()); - - // Remove preservation marker - serviceSession.setAttribute( - VaadinService.PRESERVE_UNBOUND_SESSION_ATTRIBUTE, null); - } - - /** - * This will look at the timestamp and delete expired persisted Vaadin and - * appengine sessions from the datastore. - * - * TODO Possible improvements include: 1. Use transactions (requires entity - * groups - overkill?) 2. Delete one-at-a-time, catch possible exception, - * continue w/ next. - */ - private void cleanDatastore() { - long expire = new Date().getTime(); - try { - DatastoreService ds = DatastoreServiceFactory.getDatastoreService(); - // Vaadin stuff first - { - Query q = new Query(AC_BASE); - q.setKeysOnly(); - - q.addFilter(PROPERTY_EXPIRES, FilterOperator.LESS_THAN_OR_EQUAL, - expire); - PreparedQuery pq = ds.prepare(q); - List<Entity> entities = pq - .asList(Builder.withLimit(CLEANUP_LIMIT)); - if (entities != null) { - getLogger().log(Level.INFO, - "Vaadin cleanup deleting {0} expired Vaadin sessions.", - entities.size()); - List<Key> keys = new ArrayList<>(); - for (Entity e : entities) { - keys.add(e.getKey()); - } - ds.delete(keys); - } - } - // Also cleanup GAE sessions - { - Query q = new Query(APPENGINE_SESSION_KIND); - q.setKeysOnly(); - q.addFilter(PROPERTY_APPENGINE_EXPIRES, - FilterOperator.LESS_THAN_OR_EQUAL, expire); - PreparedQuery pq = ds.prepare(q); - List<Entity> entities = pq - .asList(Builder.withLimit(CLEANUP_LIMIT)); - if (entities != null) { - getLogger().log(Level.INFO, - "Vaadin cleanup deleting {0} expired appengine sessions.", - entities.size()); - List<Key> keys = new ArrayList<>(); - for (Entity e : entities) { - keys.add(e.getKey()); - } - ds.delete(keys); - } - } - } catch (Exception e) { - getLogger().log(Level.WARNING, "Exception while cleaning.", e); - } - } - - private static final Logger getLogger() { - return Logger.getLogger(GAEVaadinServlet.class.getName()); - } -} diff --git a/server/src/main/java/com/vaadin/server/VaadinServlet.java b/server/src/main/java/com/vaadin/server/VaadinServlet.java index a8b9a0b90a..f0d01bce08 100644 --- a/server/src/main/java/com/vaadin/server/VaadinServlet.java +++ b/server/src/main/java/com/vaadin/server/VaadinServlet.java @@ -298,9 +298,9 @@ public class VaadinServlet extends HttpServlet implements Constants { * Gets the currently used Vaadin servlet. The current servlet is * automatically defined when initializing the servlet and when processing * requests to the server (see {@link ThreadLocal}) and in - * {@link VaadinSession#access(Runnable)} and {@link UI#access(Runnable)}. In - * other cases, (e.g. from background threads), the current servlet is not - * automatically defined. + * {@link VaadinSession#access(Runnable)} and {@link UI#access(Runnable)}. + * In other cases, (e.g. from background threads), the current servlet is + * not automatically defined. * <p> * The current servlet is derived from the current service using * {@link VaadinService#getCurrent()} @@ -553,7 +553,7 @@ public class VaadinServlet extends HttpServlet implements Constants { * if the writing failed due to input/output error. * * @deprecated As of 7.0. This method is retained only for backwards - * compatibility and for {@link GAEVaadinServlet}. + * compatibility and for GAEVaadinServlet. */ @Deprecated protected void criticalNotification(VaadinServletRequest request, @@ -778,7 +778,7 @@ public class VaadinServlet extends HttpServlet implements Constants { } response.setHeader("Cache-Control", cacheControl); response.setDateHeader("Expires", - System.currentTimeMillis() + (resourceCacheTime * 1000)); + System.currentTimeMillis() + resourceCacheTime * 1000); // Find the modification timestamp long lastModifiedTime = 0; @@ -1339,10 +1339,9 @@ public class VaadinServlet extends HttpServlet implements Constants { throws MalformedURLException { final URL reqURL = new URL((request.isSecure() ? "https://" : "http://") + request.getServerName() - + ((request.isSecure() && request.getServerPort() == 443) - || (!request.isSecure() - && request.getServerPort() == 80) ? "" - : ":" + request.getServerPort()) + + (request.isSecure() && request.getServerPort() == 443 + || !request.isSecure() && request.getServerPort() == 80 + ? "" : ":" + request.getServerPort()) + request.getRequestURI()); String servletPath = ""; if (request diff --git a/server/src/main/java/com/vaadin/util/SerializerHelper.java b/server/src/main/java/com/vaadin/util/SerializerHelper.java deleted file mode 100644 index 41ef150d51..0000000000 --- a/server/src/main/java/com/vaadin/util/SerializerHelper.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2000-2016 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.util; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -/** - * Helper class for performing serialization. Most of the methods are here are - * workarounds for problems in Google App Engine. Used internally by Vaadin and - * should not be used by application developers. Subject to change at any time. - * - * @since 6.0 - */ -public class SerializerHelper { - - /** - * Serializes the class reference so {@link #readClass(ObjectInputStream)} - * can deserialize it. Supports null class references. - * - * @param out - * The {@link ObjectOutputStream} to serialize to. - * @param cls - * A class or null. - * @throws IOException - * Rethrows any IOExceptions from the ObjectOutputStream - */ - public static void writeClass(ObjectOutputStream out, Class<?> cls) - throws IOException { - if (cls == null) { - out.writeObject(null); - } else { - out.writeObject(cls.getName()); - } - - } - - /** - * Serializes the class references so - * {@link #readClassArray(ObjectInputStream)} can deserialize it. Supports - * null class arrays. - * - * @param out - * The {@link ObjectOutputStream} to serialize to. - * @param classes - * An array containing class references or null. - * @throws IOException - * Rethrows any IOExceptions from the ObjectOutputStream - */ - public static void writeClassArray(ObjectOutputStream out, - Class<?>[] classes) throws IOException { - if (classes == null) { - out.writeObject(null); - } else { - String[] classNames = new String[classes.length]; - for (int i = 0; i < classes.length; i++) { - classNames[i] = classes[i].getName(); - } - out.writeObject(classNames); - } - } - - /** - * Deserializes a class references serialized by - * {@link #writeClassArray(ObjectOutputStream, Class[])}. Supports null - * class arrays. - * - * @param in - * {@link ObjectInputStream} to read from. - * @return Class array with the class references or null. - * @throws ClassNotFoundException - * If one of the classes could not be resolved. - * @throws IOException - * Rethrows IOExceptions from the ObjectInputStream - */ - public static Class<?>[] readClassArray(ObjectInputStream in) - throws ClassNotFoundException, IOException { - String[] classNames = (String[]) in.readObject(); - if (classNames == null) { - return null; - } - Class<?>[] classes = new Class<?>[classNames.length]; - for (int i = 0; i < classNames.length; i++) { - classes[i] = resolveClass(classNames[i]); - } - - return classes; - } - - /** - * List of primitive classes. Google App Engine has problems - * serializing/deserializing these (#3064). - */ - private static final Class<?>[] primitiveClasses = new Class<?>[] { byte.class, - short.class, int.class, long.class, float.class, double.class, - boolean.class, char.class }; - - /** - * Resolves the class given by {@code className}. - * - * @param className - * The fully qualified class name. - * @return A {@code Class} reference. - * @throws ClassNotFoundException - * If the class could not be resolved. - */ - public static Class<?> resolveClass(String className) - throws ClassNotFoundException { - for (Class<?> c : primitiveClasses) { - if (className.equals(c.getName())) { - return c; - } - } - - return Class.forName(className); - } - - /** - * Deserializes a class reference serialized by - * {@link #writeClass(ObjectOutputStream, Class)}. Supports null class - * references. - * - * @param in - * {@code ObjectInputStream} to read from. - * @return Class reference to the resolved class - * @throws ClassNotFoundException - * If the class could not be resolved. - * @throws IOException - * Rethrows IOExceptions from the ObjectInputStream - */ - public static Class<?> readClass(ObjectInputStream in) - throws IOException, ClassNotFoundException { - String className = (String) in.readObject(); - if (className == null) { - return null; - } else { - return resolveClass(className); - - } - - } - - private SerializerHelper() { - } - -} |