package eu.hurion.vaadin.heroku; import com.bsb.common.vaadin.embed.EmbedVaadinConfig; import com.bsb.common.vaadin.embed.EmbedVaadinServer; import com.bsb.common.vaadin.embed.EmbedVaadinServerBuilder; import com.bsb.common.vaadin.embed.application.ApplicationBasedEmbedVaadinTomcat; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.vaadin.ui.UI; import org.apache.catalina.Context; import org.apache.catalina.Manager; import org.apache.catalina.deploy.FilterDef; import org.apache.catalina.deploy.FilterMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletContextListener; import java.util.Arrays; import java.util.List; import java.util.Properties; /** * Customized {@code EmbedVaadinApplication} to accommodate specificity of Heroku. * */ public class VaadinForHeroku extends EmbedVaadinServerBuilder<VaadinForHeroku, EmbedVaadinServer> { private static final Logger LOG = LoggerFactory.getLogger(VaadinForHeroku.class); public static final int DEFAULT_PORT = 8080; public static final String PORT = "PORT"; private final Class<? extends UI> uiClass; private EmbedVaadinConfig config; private MemcachedManagerBuilder memcachedManagerBuilder; private final List<FilterDefinitionBuilder> filterDefinitions = Lists.newArrayList(); private final List<FilterMapBuilder> filterMaps = Lists.newArrayList(); private final List<String> applicationListeners = Lists.newArrayList(); /** * Creates a new instance for the specified application. * * @param uiClass the class of the application to deploy */ private VaadinForHeroku(final Class<? extends UI> uiClass) { super(); assertNotNull(uiClass, "uiClass cannot not be null."); this.uiClass = uiClass; withConfigProperties(EmbedVaadinConfig.loadProperties()); } /** * Creates a bare server for the given application. * * @param applicationClass the class of the application to deploy */ public static VaadinForHeroku forApplication(final Class<? extends UI> applicationClass) { return new VaadinForHeroku(applicationClass); } /** * Configures the given server for local development * * @param server the server to be configured for local development * @since 0.3 */ public static VaadinForHeroku localServer(final VaadinForHeroku server) { return server.withHttpPort(VaadinForHeroku.DEFAULT_PORT) .withProductionMode(false) .openBrowser(true); } /** * Configures the given server for Heroku * * @param server the server to be configured for heroku environment * @since 0.3 */ public static VaadinForHeroku herokuServer(final VaadinForHeroku server) { return server .withMemcachedSessionManager(MemcachedManagerBuilder.memcacheAddOn()) .withHttpPort(Integer.parseInt(System.getenv(VaadinForHeroku.PORT))) .withProductionMode(true) .openBrowser(false); } /** * Configures the given server to be used in automated tests. * * @param server the server to be configured for automated testing * @since 0.3 */ public static VaadinForHeroku testServer(final VaadinForHeroku server){ return localServer(server).wait(false).openBrowser(false); } /** * Returns the {@link UI} type that was used to initialize this instance, if any. * * @return the application class or <tt>null</tt> if a component was set */ protected Class<? extends UI> getUiClass() { return uiClass; } @Override protected VaadinForHeroku self() { return this; } @Override protected EmbedVaadinConfig getConfig() { return config; } @Override public EmbedVaadinWithSessionManagement build() { final Manager manager; LOG.debug("memcached config: " + memcachedManagerBuilder); if (this.memcachedManagerBuilder != null) { manager = memcachedManagerBuilder.build(); } else { manager = null; } final List<FilterDef> filterDefs = Lists.newArrayList(); for (final FilterDefinitionBuilder filterDefinition : filterDefinitions) { filterDefs.add(filterDefinition.build()); } final List<FilterMap> filterMappings = Lists.newArrayList(); for (final FilterMapBuilder filterMap : filterMaps) { filterMappings.add(filterMap.build()); } return new EmbedVaadinWithSessionManagement(getConfig(), getUiClass(), manager, filterDefs, filterMappings, applicationListeners); } @Override public final VaadinForHeroku withConfigProperties(final Properties properties) { this.config = new EmbedVaadinConfig(properties); return self(); } public VaadinForHeroku withMemcachedSessionManager(final MemcachedManagerBuilder memcachedManagerBuilder) { this.memcachedManagerBuilder = memcachedManagerBuilder; return self(); } public VaadinForHeroku withoutMemcachedSessionManager(){ this.memcachedManagerBuilder = null; return self(); } /** * Add filter definitions to the server configuration. * * @param filterDefs the filter definition(s) to add. * @since 0.3 */ public VaadinForHeroku withFilterDefinition(final FilterDefinitionBuilder... filterDefs){ checkVarArgsArguments(filterDefs); this.filterDefinitions.addAll(Arrays.asList(filterDefs)); return self(); } /** * Add filter mappings to the server configuration. * A filter with the same name as the one defined in the mapping should also be defined * using {@link #withFilterDefinition(FilterDefinitionBuilder...)} * * @param filterMaps the filter mapping(s) * @since 0.3 */ public VaadinForHeroku withFilterMapping(final FilterMapBuilder... filterMaps){ checkVarArgsArguments(filterMaps); this.filterMaps.addAll(Arrays.asList(filterMaps)); return self(); } /** * Add an application listener to the configuration of the server. * * @param listeners the application listener(s) to add to the server configuration. * @since 0.3 */ public VaadinForHeroku withApplicationListener(final String... listeners){ checkVarArgsArguments(listeners); this.applicationListeners.addAll(Arrays.asList(listeners)); return self(); } private void checkVarArgsArguments(final Object[] objects) { Preconditions.checkArgument(objects != null); for (Object object : objects) { Preconditions.checkArgument(object != null); } } /** * * @param listeners class of the {@code ApplicationListener} to add in the configuration of the server. * @since 0.3 */ public VaadinForHeroku withApplicationListener(final Class<? extends ServletContextListener>... listeners){ checkVarArgsArguments(listeners); for (final Class<?> listener : listeners) { applicationListeners.add(listener.getName()); } return self(); } /** * An {@link com.bsb.common.vaadin.embed.EmbedVaadinServer} implementation that will configure tomcat to store session in memcached. */ static final class EmbedVaadinWithSessionManagement extends ApplicationBasedEmbedVaadinTomcat { private final Manager manager; private final List<FilterDef> filterDefinitions; private final List<FilterMap> filterMaps; private final List<String> applicationListeners; /** * @param config the config to use * @param applicationClass the class of the application to handle * @param manager Custom Session manager. Optional * @param filterDefinitions the list of filter definitions. * @param filterMaps the list of filter maps. */ private EmbedVaadinWithSessionManagement(final EmbedVaadinConfig config, final Class<? extends UI> applicationClass, final Manager manager, final List<FilterDef> filterDefinitions, final List<FilterMap> filterMaps, final List<String> applicationListeners) { super(config, applicationClass); this.manager = manager; this.filterDefinitions = filterDefinitions; this.filterMaps = filterMaps; this.applicationListeners = applicationListeners; } Context getContextForTest() { return super.getContext(); } @Override protected void configure() { super.configure(); if (manager != null) { getContext().setManager(manager); } for (final String applicationListener : applicationListeners) { getContext().addApplicationListener(applicationListener); } for (final FilterDef filterDefinition : filterDefinitions) { getContext().addFilterDef(filterDefinition); } for (final FilterMap filterMap : filterMaps) { getContext().addFilterMap(filterMap); } } } }