From 132eee59b8914fdbf9c42a9314e3db95f2f51520 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leif=20=C3=85strand?= Date: Tue, 4 Jun 2013 16:17:23 +0300 Subject: [PATCH] Add @VaadinServletConfiguration (#11970) Change-Id: Ic902028826adae8132bfa18b6cde7d80a2e876c4 --- .../VaadinServletConfiguration.java | 143 ++++++++++++++++++ .../DefaultDeploymentConfiguration.java | 56 ++++--- .../src/com/vaadin/server/VaadinServlet.java | 38 +++++ .../com/vaadin/server/MockServletConfig.java | 15 +- .../VaadinServletConfigurationTest.java | 98 ++++++++++++ 5 files changed, 328 insertions(+), 22 deletions(-) create mode 100644 server/src/com/vaadin/annotations/VaadinServletConfiguration.java create mode 100644 server/tests/src/com/vaadin/server/VaadinServletConfigurationTest.java diff --git a/server/src/com/vaadin/annotations/VaadinServletConfiguration.java b/server/src/com/vaadin/annotations/VaadinServletConfiguration.java new file mode 100644 index 0000000000..38e3ff2ef0 --- /dev/null +++ b/server/src/com/vaadin/annotations/VaadinServletConfiguration.java @@ -0,0 +1,143 @@ +/* + * Copyright 2000-2013 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.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.vaadin.server.Constants; +import com.vaadin.server.DefaultDeploymentConfiguration; +import com.vaadin.server.DeploymentConfiguration; +import com.vaadin.server.DeploymentConfiguration.LegacyProperyToStringMode; +import com.vaadin.server.VaadinServlet; +import com.vaadin.server.VaadinSession; +import com.vaadin.ui.UI; + +/** + * Annotation for configuring subclasses of {@link VaadinServlet}. For a + * {@link VaadinServlet} class that has this annotation, the defined values are + * read during initialization and will be available using + * {@link DeploymentConfiguration#getApplicationOrSystemProperty(String, String)} + * as well as from specific methods in {@link DeploymentConfiguration}. Init + * params defined in web.xml or the @WebServlet + * annotation take precedence over values defined in this annotation. + * + * @since 7.1 + * @author Vaadin Ltd + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface VaadinServletConfiguration { + /** + * Defines the init parameter name for methods in + * {@link VaadinServletConfiguration}. + * + * @since 7.1 + * @author Vaadin Ltd + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @Documented + public @interface InitParameterName { + /** + * The name of the init parameter that the annotated method controls. + * + * @return the parameter name + */ + public String value(); + } + + /** + * Whether Vaadin is in production mode. + * + * @return true if in production mode, false otherwise. + * + * @see DeploymentConfiguration#isProductionMode() + */ + @InitParameterName(Constants.SERVLET_PARAMETER_PRODUCTION_MODE) + public boolean productionMode(); + + /** + * Gets the default UI class to use for the servlet. + * + * @return the default UI class + */ + @InitParameterName(VaadinSession.UI_PARAMETER) + public Class ui(); + + /** + * The time resources can be cached in the browser, in seconds. The default + * value is + * {@value DefaultDeploymentConfiguration#DEFAULT_RESOURCE_CACHE_TIME}. + * + * @return the resource cache time + * + * @see DeploymentConfiguration#getResourceCacheTime() + */ + @InitParameterName(Constants.SERVLET_PARAMETER_RESOURCE_CACHE_TIME) + public int resourceCacheTime() default DefaultDeploymentConfiguration.DEFAULT_RESOURCE_CACHE_TIME; + + /** + * The number of seconds between heartbeat requests of a UI, or a + * non-positive number if heartbeat is disabled. The default value is + * {@value DefaultDeploymentConfiguration#DEFAULT_HEARTBEAT_INTERVAL}. + * + * @return the time between heartbeats + * + * @see DeploymentConfiguration#getHeartbeatInterval() + */ + @InitParameterName(Constants.SERVLET_PARAMETER_HEARTBEAT_INTERVAL) + public int heartbeatInterval() default DefaultDeploymentConfiguration.DEFAULT_HEARTBEAT_INTERVAL; + + /** + * Whether a session should be closed when all its open UIs have been idle + * for longer than its configured maximum inactivity time. The default value + * is {@value DefaultDeploymentConfiguration#DEFAULT_CLOSE_IDLE_SESSIONS}. + * + * @return true if UIs and sessions receiving only heartbeat requests are + * eventually closed; false if heartbeat requests extend UI and + * session lifetime indefinitely + * + * @see DeploymentConfiguration#isCloseIdleSessions() + */ + @InitParameterName(Constants.SERVLET_PARAMETER_CLOSE_IDLE_SESSIONS) + public boolean closeIdleSessions() default DefaultDeploymentConfiguration.DEFAULT_CLOSE_IDLE_SESSIONS; + + /** + * The default widgetset to use for the servlet. The default value is + * {@value VaadinServlet#DEFAULT_WIDGETSET}. + * + * @return the default widgetset name + */ + @InitParameterName(VaadinServlet.PARAMETER_WIDGETSET) + public String widgetset() default VaadinServlet.DEFAULT_WIDGETSET; + + /** + * The legacy Property.toString() mode used. The default value is + * {@link LegacyProperyToStringMode#DISABLED} + * + * @return The Property.toString() mode in use. + * + * @deprecated as of 7.1, should only be used to ease migration + */ + @Deprecated + @InitParameterName(Constants.SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING) + public LegacyProperyToStringMode legacyPropertyToStringMode() default LegacyProperyToStringMode.DISABLED; +} diff --git a/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java b/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java index 80c3644d77..a55c3231f3 100644 --- a/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java +++ b/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java @@ -30,6 +30,27 @@ import com.vaadin.shared.communication.PushMode; * @since 7.0.0 */ public class DefaultDeploymentConfiguration implements DeploymentConfiguration { + /** + * Default value for {@link #getResourceCacheTime()} = {@value} . + */ + public static final int DEFAULT_RESOURCE_CACHE_TIME = 3600; + + /** + * Default value for {@link #getHeartbeatInterval()} = {@value} . + */ + public static final int DEFAULT_HEARTBEAT_INTERVAL = 300; + + /** + * Default value for {@link #isCloseIdleSessions()} = {@value} . + */ + public static final boolean DEFAULT_CLOSE_IDLE_SESSIONS = false; + + /** + * Default value for {@link #getLegacyPropertyToStringMode()} = + * {@link LegacyProperyToStringMode#WARNING}. + */ + public static final LegacyProperyToStringMode DEFAULT_LEGACY_PROPERTY_TO_STRING = LegacyProperyToStringMode.WARNING; + private final Properties initParameters; private boolean productionMode; private boolean xsrfProtectionEnabled; @@ -66,20 +87,17 @@ public class DefaultDeploymentConfiguration implements DeploymentConfiguration { private void checkLegacyPropertyToString() { String param = getApplicationOrSystemProperty( - Constants.SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING, "warning"); - if ("true".equals(param)) { - legacyPropertyToStringMode = LegacyProperyToStringMode.ENABLED; - } else if ("false".equals(param)) { - legacyPropertyToStringMode = LegacyProperyToStringMode.DISABLED; - } else { - if (!"warning".equals(param)) { - getLogger() - .log(Level.WARNING, - Constants.WARNING_UNKNOWN_LEGACY_PROPERTY_TOSTRING_VALUE, - param); - } - legacyPropertyToStringMode = LegacyProperyToStringMode.WARNING; + Constants.SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING, + DEFAULT_LEGACY_PROPERTY_TO_STRING.name().toLowerCase()); + try { + legacyPropertyToStringMode = LegacyProperyToStringMode + .valueOf(param.toUpperCase()); + } catch (IllegalArgumentException e) { + getLogger().log(Level.WARNING, + Constants.WARNING_UNKNOWN_LEGACY_PROPERTY_TOSTRING_VALUE, + param); + legacyPropertyToStringMode = DEFAULT_LEGACY_PROPERTY_TO_STRING; } } @@ -250,11 +268,11 @@ public class DefaultDeploymentConfiguration implements DeploymentConfiguration { resourceCacheTime = Integer .parseInt(getApplicationOrSystemProperty( Constants.SERVLET_PARAMETER_RESOURCE_CACHE_TIME, - "3600")); + Integer.toString(DEFAULT_RESOURCE_CACHE_TIME))); } catch (NumberFormatException e) { getLogger().warning( Constants.WARNING_RESOURCE_CACHING_TIME_NOT_NUMERIC); - resourceCacheTime = 3600; + resourceCacheTime = DEFAULT_RESOURCE_CACHE_TIME; } } @@ -263,18 +281,18 @@ public class DefaultDeploymentConfiguration implements DeploymentConfiguration { heartbeatInterval = Integer .parseInt(getApplicationOrSystemProperty( Constants.SERVLET_PARAMETER_HEARTBEAT_INTERVAL, - "300")); + Integer.toString(DEFAULT_HEARTBEAT_INTERVAL))); } catch (NumberFormatException e) { getLogger().warning( Constants.WARNING_HEARTBEAT_INTERVAL_NOT_NUMERIC); - heartbeatInterval = 300; + heartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL; } } private void checkCloseIdleSessions() { closeIdleSessions = getApplicationOrSystemProperty( - Constants.SERVLET_PARAMETER_CLOSE_IDLE_SESSIONS, "false") - .equals("true"); + Constants.SERVLET_PARAMETER_CLOSE_IDLE_SESSIONS, + Boolean.toString(DEFAULT_CLOSE_IDLE_SESSIONS)).equals("true"); } private void checkPushMode() { diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index 6f166db2b5..05e3335c00 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; +import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; @@ -38,6 +39,8 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import com.vaadin.annotations.VaadinServletConfiguration; +import com.vaadin.annotations.VaadinServletConfiguration.InitParameterName; import com.vaadin.sass.internal.ScssStylesheet; import com.vaadin.server.communication.ServletUIInitHandler; import com.vaadin.shared.JsonConstants; @@ -66,6 +69,8 @@ public class VaadinServlet extends HttpServlet implements Constants { super.init(servletConfig); Properties initParameters = new Properties(); + readConfigurationAnnotation(initParameters); + // Read default parameters from server.xml final ServletContext context = servletConfig.getServletContext(); for (final Enumeration e = context.getInitParameterNames(); e @@ -96,6 +101,39 @@ public class VaadinServlet extends HttpServlet implements Constants { CurrentInstance.clearAll(); } + private void readConfigurationAnnotation(Properties initParameters) + throws ServletException { + VaadinServletConfiguration configAnnotation = UIProvider + .getAnnotationFor(getClass(), VaadinServletConfiguration.class); + if (configAnnotation != null) { + Method[] methods = VaadinServletConfiguration.class + .getDeclaredMethods(); + for (Method method : methods) { + InitParameterName name = method + .getAnnotation(InitParameterName.class); + assert name != null : "All methods declared in VaadinServletConfiguration should have a @InitParameterName annotation"; + + try { + Object value = method.invoke(configAnnotation); + + String stringValue; + if (value instanceof Class) { + stringValue = ((Class) value).getName(); + } else { + stringValue = value.toString(); + } + + initParameters.setProperty(name.value(), stringValue); + } catch (Exception e) { + // This should never happen + throw new ServletException( + "Could not read @VaadinServletConfiguration value " + + method.getName(), e); + } + } + } + } + protected void servletInitialized() throws ServletException { // Empty by default } diff --git a/server/tests/src/com/vaadin/server/MockServletConfig.java b/server/tests/src/com/vaadin/server/MockServletConfig.java index b1e046c812..cd1201c249 100644 --- a/server/tests/src/com/vaadin/server/MockServletConfig.java +++ b/server/tests/src/com/vaadin/server/MockServletConfig.java @@ -19,8 +19,8 @@ */ package com.vaadin.server; -import java.util.Collections; import java.util.Enumeration; +import java.util.Properties; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; @@ -33,6 +33,15 @@ import javax.servlet.ServletContext; public class MockServletConfig implements ServletConfig { private ServletContext context = new MockServletContext(); + private final Properties initParameters; + + public MockServletConfig() { + this(new Properties()); + } + + public MockServletConfig(Properties initParameters) { + this.initParameters = initParameters; + } /* * (non-Javadoc) @@ -61,7 +70,7 @@ public class MockServletConfig implements ServletConfig { */ @Override public String getInitParameter(String name) { - return null; + return initParameters.getProperty(name); } /* @@ -71,7 +80,7 @@ public class MockServletConfig implements ServletConfig { */ @Override public Enumeration getInitParameterNames() { - return Collections.enumeration(Collections.EMPTY_LIST); + return initParameters.propertyNames(); } } diff --git a/server/tests/src/com/vaadin/server/VaadinServletConfigurationTest.java b/server/tests/src/com/vaadin/server/VaadinServletConfigurationTest.java new file mode 100644 index 0000000000..80cb1d7b0c --- /dev/null +++ b/server/tests/src/com/vaadin/server/VaadinServletConfigurationTest.java @@ -0,0 +1,98 @@ +/* + * Copyright 2000-2013 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.util.Properties; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; + +import junit.framework.Assert; + +import org.easymock.EasyMock; +import org.junit.Test; + +import com.vaadin.annotations.VaadinServletConfiguration; +import com.vaadin.server.DeploymentConfiguration.LegacyProperyToStringMode; +import com.vaadin.server.VaadinServletConfigurationTest.MockUI; +import com.vaadin.ui.UI; + +public class VaadinServletConfigurationTest { + public static class MockUI extends UI { + @Override + protected void init(VaadinRequest request) { + // Do nothing + } + } + + @Test + public void testValuesFromAnnotation() throws ServletException { + TestServlet servlet = new TestServlet(); + servlet.init(new MockServletConfig()); + DeploymentConfiguration configuration = servlet.getService() + .getDeploymentConfiguration(); + + Assert.assertEquals(true, configuration.isProductionMode()); + Assert.assertEquals(LegacyProperyToStringMode.DISABLED, + configuration.getLegacyPropertyToStringMode()); + Assert.assertEquals(true, configuration.isCloseIdleSessions()); + Assert.assertEquals(1234, configuration.getHeartbeatInterval()); + Assert.assertEquals(4321, configuration.getResourceCacheTime()); + + Class uiClass = new DefaultUIProvider() + .getUIClass(new UIClassSelectionEvent(new VaadinServletRequest( + EasyMock.createMock(HttpServletRequest.class), servlet + .getService()))); + Assert.assertEquals(MockUI.class, uiClass); + } + + @Test + public void testValuesOverriddenForServlet() throws ServletException { + Properties servletInitParams = new Properties(); + servletInitParams.setProperty("productionMode", "false"); + servletInitParams.setProperty("heartbeatInterval", "1111"); + + TestServlet servlet = new TestServlet(); + servlet.init(new MockServletConfig(servletInitParams)); + DeploymentConfiguration configuration = servlet.getService() + .getDeploymentConfiguration(); + + // Values from servlet init params take precedence + Assert.assertEquals(1111, configuration.getHeartbeatInterval()); + Assert.assertEquals(false, configuration.isProductionMode()); + + // Other params are as defined in the annotation + Assert.assertEquals(LegacyProperyToStringMode.DISABLED, + configuration.getLegacyPropertyToStringMode()); + Assert.assertEquals(true, configuration.isCloseIdleSessions()); + Assert.assertEquals(4321, configuration.getResourceCacheTime()); + + Class uiClass = new DefaultUIProvider() + .getUIClass(new UIClassSelectionEvent(new VaadinServletRequest( + EasyMock.createMock(HttpServletRequest.class), servlet + .getService()))); + Assert.assertEquals(MockUI.class, uiClass); + } +} + +@VaadinServletConfiguration(productionMode = true, ui = MockUI.class, closeIdleSessions = true, heartbeatInterval = 1234, resourceCacheTime = 4321) +class TestServlet extends VaadinServlet { + +} -- 2.39.5