/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.isis.viewer.wicket.viewer; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.Future; import com.google.common.base.Function; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; import org.apache.wicket.Application; import org.apache.wicket.Component; import org.apache.wicket.ConverterLocator; import org.apache.wicket.IConverterLocator; import org.apache.wicket.Page; import org.apache.wicket.RuntimeConfigurationType; import org.apache.wicket.SharedResources; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.authentication.IAuthenticationStrategy; import org.apache.wicket.authentication.strategy.DefaultAuthenticationStrategy; import org.apache.wicket.authroles.authentication.AuthenticatedWebApplication; import org.apache.wicket.authroles.authentication.AuthenticatedWebSession; import org.apache.wicket.core.request.mapper.MountedMapper; import org.apache.wicket.guice.GuiceComponentInjector; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.head.filter.JavaScriptFilteredIntoFooterHeaderResponse; import org.apache.wicket.markup.html.IHeaderContributor; import org.apache.wicket.markup.html.IHeaderResponseDecorator; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.request.cycle.IRequestCycleListener; import org.apache.wicket.request.cycle.PageRequestHandlerTracker; import org.apache.wicket.request.cycle.RequestCycleListenerCollection; import org.apache.wicket.request.resource.CssResourceReference; import org.apache.wicket.settings.RequestCycleSettings; import org.apache.wicket.util.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wicketstuff.select2.ApplicationSettings; import org.apache.isis.core.commons.authentication.AuthenticationSession; import org.apache.isis.core.commons.config.IsisConfiguration; import org.apache.isis.core.commons.config.IsisConfigurationDefault; import org.apache.isis.core.commons.configbuilder.IsisConfigurationBuilder; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.core.metamodel.deployment.DeploymentCategory; import org.apache.isis.core.metamodel.specloader.validator.MetaModelInvalidException; import org.apache.isis.core.runtime.logging.IsisLoggingConfigurer; import org.apache.isis.core.runtime.runner.IsisInjectModule; import org.apache.isis.core.runtime.system.DeploymentType; import org.apache.isis.core.runtime.system.context.IsisContext; import org.apache.isis.core.runtime.system.session.IsisSessionFactory; import org.apache.isis.core.runtime.threadpool.ThreadPoolSupport; import org.apache.isis.core.webapp.IsisWebAppBootstrapper; import org.apache.isis.core.webapp.WebAppConstants; import org.apache.isis.schema.utils.ChangesDtoUtils; import org.apache.isis.schema.utils.CommandDtoUtils; import org.apache.isis.schema.utils.InteractionDtoUtils; import org.apache.isis.viewer.wicket.model.isis.WicketViewerSettings; import org.apache.isis.viewer.wicket.model.isis.WicketViewerSettingsAccessor; import org.apache.isis.viewer.wicket.model.mementos.ObjectAdapterMemento; import org.apache.isis.viewer.wicket.model.models.ImageResourceCache; import org.apache.isis.viewer.wicket.model.models.PageType; import org.apache.isis.viewer.wicket.ui.ComponentFactory; import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistry; import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistryAccessor; import org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions.AdditionalLinksPanel; import org.apache.isis.viewer.wicket.ui.components.scalars.string.MultiLineStringPanel; import org.apache.isis.viewer.wicket.ui.components.widgets.select2.Select2BootstrapCssReference; import org.apache.isis.viewer.wicket.ui.components.widgets.select2.Select2JsReference; import org.apache.isis.viewer.wicket.ui.pages.PageClassRegistry; import org.apache.isis.viewer.wicket.ui.pages.PageClassRegistryAccessor; import org.apache.isis.viewer.wicket.ui.pages.accmngt.AccountConfirmationMap; import org.apache.isis.viewer.wicket.ui.panels.PanelUtil; import org.apache.isis.viewer.wicket.viewer.integration.isis.DeploymentTypeWicketAbstract; import org.apache.isis.viewer.wicket.viewer.integration.isis.WicketServer; import org.apache.isis.viewer.wicket.viewer.integration.isis.WicketServerPrototype; import org.apache.isis.viewer.wicket.viewer.integration.wicket.AuthenticatedWebSessionForIsis; import org.apache.isis.viewer.wicket.viewer.integration.wicket.ConverterForObjectAdapter; import org.apache.isis.viewer.wicket.viewer.integration.wicket.ConverterForObjectAdapterMemento; import org.apache.isis.viewer.wicket.viewer.integration.wicket.WebRequestCycleForIsis; import org.apache.isis.viewer.wicket.viewer.settings.IsisResourceSettings; import de.agilecoders.wicket.core.Bootstrap; import de.agilecoders.wicket.core.markup.html.bootstrap.behavior.BootstrapBaseBehavior; import de.agilecoders.wicket.core.settings.BootstrapSettings; import de.agilecoders.wicket.core.settings.IBootstrapSettings; import de.agilecoders.wicket.webjars.WicketWebjars; import de.agilecoders.wicket.webjars.settings.IWebjarsSettings; import de.agilecoders.wicket.webjars.settings.WebjarsSettings; import net.ftlines.wicketsource.WicketSource; /** * Main application, subclassing the Wicket {@link Application} and * bootstrapping Isis. * * <p> * Its main responsibility is to allow the set of {@link ComponentFactory}s used * to render the domain objects to be registered. This type of customisation is * common place. At a more fundamental level, also allows the {@link Page} * implementation for each {@link PageType page type} to be overridden. This is * probably less common, because CSS can also be used for this purpose. * * <p> * New {@link ComponentFactory}s can be specified in two ways. The preferred * approach is to use the {@link ServiceLoader} mechanism, whereby the * {@link ComponentFactory} implementation class is specified in a file under * <tt>META-INF/services</tt>. See <tt>views-gmaps2</tt> for an example of this. * Including a jar that uses this mechanism on the classpath will automatically * make the {@link ComponentFactory} defined within it available. * * <p> * Alternatively, {@link ComponentFactory}s can be specified by overridding {@link #newIsisWicketModule()}. * This mechanism allows a number of other aspects to be customized. */ public class IsisWicketApplication extends AuthenticatedWebApplication implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketViewerSettingsAccessor { private static final long serialVersionUID = 1L; private static final Logger LOG = LoggerFactory.getLogger(IsisWicketApplication.class); private static final String STRIP_WICKET_TAGS_KEY = "isis.viewer.wicket.stripWicketTags"; private static final boolean STRIP_WICKET_TAGS_DEFAULT = true; private static final String AJAX_DEBUG_MODE_KEY = "isis.viewer.wicket.ajaxDebugMode"; private static final boolean AJAX_DEBUG_MODE_DEFAULT = false; private static final String WICKET_SOURCE_PLUGIN_KEY = "isis.viewer.wicket.wicketSourcePlugin"; private static final boolean WICKET_SOURCE_PLUGIN_DEFAULT = false; private static final String WICKET_REMEMBER_ME_COOKIE_KEY = "isis.viewer.wicket.rememberMe.cookieKey"; private static final String WICKET_REMEMBER_ME_COOKIE_KEY_DEFAULT = "isisWicketRememberMe"; private static final String WICKET_REMEMBER_ME_ENCRYPTION_KEY = "isis.viewer.wicket.rememberMe.encryptionKey"; private final IsisLoggingConfigurer loggingConfigurer = new IsisLoggingConfigurer(); /** * Convenience locator, downcasts inherited functionality. */ public static IsisWicketApplication get() { return (IsisWicketApplication) AuthenticatedWebApplication.get(); } /** * {@link com.google.inject.Inject Inject}ed when {@link #init() initialized}. */ @com.google.inject.Inject private ComponentFactoryRegistry componentFactoryRegistry; /** * {@link com.google.inject.Inject Inject}ed when {@link #init() initialized}. */ @SuppressWarnings("unused") @com.google.inject.Inject private ImageResourceCache imageCache; /** * {@link com.google.inject.Inject Inject}ed when {@link #init() initialized}. */ @SuppressWarnings("unused") @com.google.inject.Inject private WicketViewerSettings wicketViewerSettings; /** * {@link com.google.inject.Inject Inject}ed when {@link #init() initialized}. */ @com.google.inject.Inject private PageClassRegistry pageClassRegistry; /** * {@link com.google.inject.Inject Inject}ed when {@link #init() initialized}. */ @com.google.inject.Inject private IsisSessionFactory isisSessionFactory; /** * {@link com.google.inject.Inject Inject}ed when {@link #init() initialized}. */ @com.google.inject.Inject private IsisConfiguration configuration; /** * {@link com.google.inject.Inject Inject}ed when {@link #init() initialized}. */ @com.google.inject.Inject private DeploymentCategory deploymentCategory; @com.google.inject.Inject private WicketViewerSettings settings; private boolean determiningConfigurationType; private DeploymentTypeWicketAbstract deploymentType; // ///////////////////////////////////////////////// // constructor, init // ///////////////////////////////////////////////// public IsisWicketApplication() { } /** * Although there are warnings about not overriding this method, it doesn't seem possible * to call {@link #setResourceSettings(org.apache.wicket.settings.ResourceSettings)} in the * {@link #init()} method. */ @Override protected void internalInit() { // replace with custom implementation of ResourceSettings that changes the order // in which search for i18n properties, to search for the application-specific // settings before any other. setResourceSettings(new IsisResourceSettings(this)); // this doesn't seem to accomplish anything // addListenerToStripRemovedComponentsFromAjaxTargetResponse(); super.internalInit(); } // idea here is to avoid XmlPartialPageUpdate spitting out warnings, eg: // // 13:08:36,642 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[AjaxLink [Component id = copyLink]]' with markupid: 'copyLink94c' not rendered because it was already removed from page // 13:08:36,642 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[SimpleClipboardModalWindow [Component id = simpleClipboardModalWindow]]' with markupid: 'simpleClipboardModalWindow94e' not rendered because it was already removed from page // 13:08:36,643 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[AjaxFallbackLink [Component id = link]]' with markupid: 'link951' not rendered because it was already removed from page // 13:08:36,643 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[AjaxFallbackLink [Component id = link]]' with markupid: 'link952' not rendered because it was already removed from page // 13:08:36,655 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[AjaxLink [Component id = clearBookmarkLink]]' with markupid: 'clearBookmarkLink953' not rendered because it was already removed from page // // however, doesn't seem to work (even though the provided map is mutable). // must be some other sort of side-effect which causes the enqueued component(s) to be removed from page between // this listener firing and XmlPartialPageUpdate actually attempting to render the change components // private boolean addListenerToStripRemovedComponentsFromAjaxTargetResponse() { return getAjaxRequestTargetListeners().add(new AjaxRequestTarget.AbstractListener(){ @Override public void onBeforeRespond(Map<String, Component> map, AjaxRequestTarget target) { List<String> idsToRemove = Lists.newArrayList(); final Set<Map.Entry<String, Component>> entries = map.entrySet(); for (Map.Entry<String, Component> entry : entries) { final Component component = entry.getValue(); final Page page = component.findParent(Page.class); if(page == null) { idsToRemove.add(entry.getKey()); } } for (String id : idsToRemove) { map.remove(id); } } }); } /** * Initializes the application; in particular, bootstrapping the Isis * backend, and initializing the {@link ComponentFactoryRegistry} to be used * for rendering. */ @Override protected void init() { List<Future<Object>> futures = null; try { super.init(); futures = startBackgroundInitializationThreads(); String isisConfigDir = getServletContext().getInitParameter("isis.config.dir"); configureLogging(isisConfigDir); getRequestCycleSettings().setRenderStrategy(RequestCycleSettings.RenderStrategy.REDIRECT_TO_RENDER); getResourceSettings().setParentFolderPlaceholder("$up$"); final IsisConfigurationBuilder isisConfigurationBuilder = obtainConfigBuilder(); isisConfigurationBuilder.addDefaultConfigurationResourcesAndPrimers(); final IsisConfigurationDefault configuration = isisConfigurationBuilder.getConfiguration(); DeploymentTypeWicketAbstract deploymentType = determineDeploymentType(configuration.getString("isis.deploymentType")); RequestCycleListenerCollection requestCycleListeners = getRequestCycleListeners(); IRequestCycleListener requestCycleListenerForIsis = newWebRequestCycleForIsis(); requestCycleListeners.add(requestCycleListenerForIsis); requestCycleListeners.add(new PageRequestHandlerTracker()); // // create IsisSessionFactory // final DeploymentCategory deploymentCategory = deploymentType.getDeploymentCategory(); final IsisInjectModule isisModule = newIsisModule(deploymentCategory, configuration); final Injector injector = Guice.createInjector(isisModule, newIsisWicketModule()); initWicketComponentInjection(injector); injector.injectMembers(this); // populates this.isisSessionFactory getServletContext().setAttribute(WebAppConstants.ISIS_SESSION_FACTORY, this.isisSessionFactory); if (requestCycleListenerForIsis instanceof WebRequestCycleForIsis) { WebRequestCycleForIsis webRequestCycleForIsis = (WebRequestCycleForIsis) requestCycleListenerForIsis; webRequestCycleForIsis.setPageClassRegistry(pageClassRegistry); } this.getMarkupSettings().setStripWicketTags(determineStripWicketTags(configuration)); configureSecurity(configuration); getDebugSettings().setAjaxDebugModeEnabled(determineAjaxDebugModeEnabled(configuration)); // must be done after injected componentFactoryRegistry into the app itself buildCssBundle(); filterJavascriptContributions(); configureWicketSourcePluginIfNecessary(); // TODO ISIS-987 Either make the API better (no direct access to the map) or use DB records int maxEntries = 1000; setMetaData(AccountConfirmationMap.KEY, new AccountConfirmationMap(maxEntries, Duration.days(1))); mountPages(); @SuppressWarnings("unused") SharedResources sharedResources = getSharedResources(); final MetaModelInvalidException mmie = IsisContext.getMetaModelInvalidExceptionIfAny(); if(mmie != null) { log(mmie.getValidationErrors()); } } catch(RuntimeException ex) { // because Wicket's handling in its WicketFilter (that calls this method) does not log the exception. LOG.error("Failed to initialize", ex); throw ex; } finally { ThreadPoolSupport.join(futures); } } protected List<Future<Object>> startBackgroundInitializationThreads() { return ThreadPoolSupport.invokeAll(Lists.newArrayList( new Callable<Object>() { @Override public Object call() throws Exception { configureWebJars(); return null; } }, new Callable<Object>() { @Override public Object call() throws Exception { configureWicketBootstrap(); return null; } }, new Callable<Object>() { @Override public Object call() throws Exception { configureWicketSelect2(); return null; } }, new Callable<Object>() { @Override public Object call() throws Exception { ChangesDtoUtils.init(); return null; } }, new Callable<Object>() { @Override public Object call() throws Exception { InteractionDtoUtils.init(); return null; } }, new Callable<Object>() { @Override public Object call() throws Exception { CommandDtoUtils.init(); return null; } } )); } /** * protected visibility to allow ad-hoc overriding of some other authentication strategy. */ protected void configureSecurity(final IsisConfiguration configuration) { getSecuritySettings().setAuthenticationStrategy(newAuthenticationStrategy(configuration)); } /** * protected visibility to allow ad-hoc overriding of some other authentication strategy. */ protected IAuthenticationStrategy newAuthenticationStrategy(final IsisConfiguration configuration) { final String cookieKey = configuration.getString(WICKET_REMEMBER_ME_COOKIE_KEY, WICKET_REMEMBER_ME_COOKIE_KEY_DEFAULT); final String encryptionKey = configuration.getString(WICKET_REMEMBER_ME_ENCRYPTION_KEY, defaultEncryptionKeyIfNotConfigured()); return new DefaultAuthenticationStrategy(cookieKey, encryptionKey); } /** * As called by {@link #newAuthenticationStrategy(IsisConfiguration)}; if an encryption key for the 'rememberMe' * cookie hasn't been configured, then use a different encryption key for the 'rememberMe' cookie each time the * app is restarted. */ protected String defaultEncryptionKeyIfNotConfigured() { return getDeploymentCategory().isProduction() ? UUID.randomUUID().toString() : "PrototypingEncryptionKey"; } private void log(final Set<String> validationErrors) { log(""); logBanner(); log(""); for (String validationError : validationErrors) { logError(validationError); } log(""); log("Please inspect the above messages and correct your domain model."); log(""); logBanner(); log(""); } private void configureWicketSelect2() { ApplicationSettings select2Settings = ApplicationSettings.get(); select2Settings.setCssReference(new Select2BootstrapCssReference()); select2Settings.setJavaScriptReference(new Select2JsReference()); } protected void configureWicketSourcePluginIfNecessary() { if(isWicketSourcePluginEnabled(this.configuration)) { configureWicketSourcePlugin(); } } protected void configureWicketSourcePlugin() { if(!deploymentCategory.isProduction()) { WicketSource.configure(this); } } // ////////////////////////////////////// /** * Install 2 default collector instances: (FileAssetPathCollector(WEBJARS_PATH_PREFIX), JarAssetPathCollector), * and a webjars resource finder. * * <p> * Factored out for easy (informal) pluggability. * </p> */ protected void configureWebJars() { IWebjarsSettings settings = new WebjarsSettings(); WicketWebjars.install(this, settings); } protected void configureWicketBootstrap() { final IBootstrapSettings settings = new BootstrapSettings(); settings.setDeferJavascript(false); Bootstrap.install(this, settings); getHeaderContributorListeners().add(new IHeaderContributor() { @Override public void renderHead(IHeaderResponse response) { BootstrapBaseBehavior bootstrapBaseBehavior = new BootstrapBaseBehavior(); bootstrapBaseBehavior.renderHead(settings, response); } }); } // ////////////////////////////////////// /** * Factored out for easy (informal) pluggability. */ protected void configureLogging(String isisConfigDir) { final String loggingPropertiesDir; if(isisConfigDir != null) { loggingPropertiesDir = isisConfigDir; } else { loggingPropertiesDir = getServletContext().getRealPath("/WEB-INF"); } loggingConfigurer.configureLogging(loggingPropertiesDir, new String[0]); } // ////////////////////////////////////// /** * Factored out for easy (informal) pluggability. */ protected IRequestCycleListener newWebRequestCycleForIsis() { return new WebRequestCycleForIsis(); } // ////////////////////////////////////// /** * Made protected visibility for easy (informal) pluggability. */ protected void determineDeploymentTypeIfRequired() { if(deploymentType != null) { return; } determiningConfigurationType = true; try { final IsisConfigurationBuilder isisConfigurationBuilder = obtainConfigBuilder(); final IsisConfiguration configuration = isisConfigurationBuilder.peekConfiguration(); String deploymentTypeFromConfig = configuration.getString("isis.deploymentType"); deploymentType = determineDeploymentType(deploymentTypeFromConfig); } finally { determiningConfigurationType = false; } } /** * Made protected visibility for easy (informal) pluggability. */ protected DeploymentTypeWicketAbstract determineDeploymentType(String deploymentTypeFromConfig) { final DeploymentTypeWicketAbstract prototype = new WicketServerPrototype(); final DeploymentTypeWicketAbstract deployment = new WicketServer(); if(deploymentTypeFromConfig != null) { final DeploymentType deploymentType = DeploymentType.lookup(deploymentTypeFromConfig); return !deploymentType.getDeploymentCategory().isProduction() ? prototype : deployment; } else { return usesDevelopmentConfig() ? prototype : deployment; } } // ////////////////////////////////////// private IsisConfigurationBuilder isisConfigurationBuilder; protected IsisConfigurationBuilder obtainConfigBuilder() { return isisConfigurationBuilder != null ? isisConfigurationBuilder : (isisConfigurationBuilder = IsisWebAppBootstrapper.obtainConfigBuilderFrom(getServletContext())); } // ////////////////////////////////////// /** * Override if required */ protected Module newIsisWicketModule() { return new IsisWicketModule(); } // ////////////////////////////////////// /** * Made protected visibility for easy (informal) pluggability. */ protected void buildCssBundle() { // get the css for all components built by component factories final Set<CssResourceReference> references = cssResourceReferencesForAllComponents(); // some additional special cases. addSpecialCasesToCssBundle(references); // create the bundle getResourceBundles().addCssBundle( IsisWicketApplication.class, "isis-wicket-viewer-bundle.css", references.toArray(new CssResourceReference[]{})); } /** * Additional special cases to be included in the main CSS bundle. * * <p> * These are typically either superclasses or components that don't have their own ComponentFactory, or * for {@link ComponentFactory}s (such as <tt>StringPanelFactory</tt>) that don't quite follow the usual pattern * (because they create different types of panels). * * <p> * Note that it doesn't really matter if we miss one or two; their CSS will simply be served up individually. */ protected void addSpecialCasesToCssBundle(final Set<CssResourceReference> references) { // abstract classes // ... though it turns out we cannot add this particular one to the bundle, because // it has CSS image links intended to be resolved relative to LinksSelectorPanelAbstract.class. // Adding them into the bundle would mean these CSS links are resolved relative to IsisWicketApplication.class // instead. // references.add(PanelUtil.cssResourceReferenceFor(LinksSelectorPanelAbstract.class)); // components without factories references.add(PanelUtil.cssResourceReferenceFor(AdditionalLinksPanel.class)); // non-conforming component factories references.add(PanelUtil.cssResourceReferenceFor(MultiLineStringPanel.class)); } protected final static Function<ComponentFactory, Iterable<CssResourceReference>> getCssResourceReferences = new Function<ComponentFactory, Iterable<CssResourceReference>>(){ @Override public Iterable<CssResourceReference> apply(final ComponentFactory input) { final CssResourceReference cssResourceReference = input.getCssResourceReference(); return cssResourceReference != null? Collections.singletonList(cssResourceReference): Collections.<CssResourceReference>emptyList(); } }; protected Set<CssResourceReference> cssResourceReferencesForAllComponents() { // TODO mgrigorov: ISIS-537 temporary disabled to not mess up with Bootstrap styles // Collection<ComponentFactory> componentFactories = getComponentFactoryRegistry().listComponentFactories(); return Sets.newLinkedHashSet( // Iterables.concat( // Iterables.transform( // componentFactories, // getCssResourceReferences)) ); } // ////////////////////////////////////// /** * filters Javascript header contributions so rendered to bottom of page. * * <p> * Factored out for easy (informal) pluggability. * </p> */ protected void filterJavascriptContributions() { setHeaderResponseDecorator(new IHeaderResponseDecorator() { @Override public IHeaderResponse decorate(IHeaderResponse response) { // use this header resource decorator to load all JavaScript resources in the page // footer (after </body>) return new JavaScriptFilteredIntoFooterHeaderResponse(response, "footerJS"); } }); } // ////////////////////////////////////// /** * Map entity and action to provide prettier URLs. * * <p> * Factored out for easy (informal) pluggability. * </p> */ protected void mountPages() { mountPage("/signin", PageType.SIGN_IN); mountPage("/signup", PageType.SIGN_UP); mountPage("/signup/verify", PageType.SIGN_UP_VERIFY); mountPage("/password/reset", PageType.PASSWORD_RESET); mountPage("/entity/#{objectOid}", PageType.ENTITY); // nb: action mount cannot contain {actionArgs}, because the default // parameters encoder doesn't seem to be able to handle multiple args mountPage("/action/${objectOid}/${actionOwningSpec}/${actionId}/${actionType}", PageType.ACTION_PROMPT); } protected void mountPage(final String mountPath, final PageType pageType) { final Class<? extends Page> pageClass = this.pageClassRegistry.getPageClass(pageType); mount(new MountedMapper(mountPath, pageClass)); } // ////////////////////////////////////// private void logError(String validationError) { log(validationError); } private static void logBanner() { String msg = "################################################ ISIS METAMODEL VALIDATION ERRORS ################################################################"; log(msg); } private static void log(String msg) { System.err.println(msg); LOG.error(msg); } // ////////////////////////////////////// /** * Whether Wicket tags should be stripped from the markup, as specified by configuration settings.. * * <p> * If the <tt>isis.viewer.wicket.stripWicketTags</tt> is set, then this is used, otherwise the default is to strip * the tags because they may break some CSS rules. */ private boolean determineStripWicketTags(IsisConfiguration configuration) { final boolean strip = configuration.getBoolean(STRIP_WICKET_TAGS_KEY, STRIP_WICKET_TAGS_DEFAULT); return strip; } // ////////////////////////////////////// /** * Whether the Ajax debug should be shown, as specified by configuration settings. * * <p> * If the <tt>isis.viewer.wicket.ajaxDebugMode</tt> is set, then this is used, otherwise the default is to disable. */ private boolean determineAjaxDebugModeEnabled(IsisConfiguration configuration) { final boolean debugModeEnabled = configuration.getBoolean(AJAX_DEBUG_MODE_KEY, AJAX_DEBUG_MODE_DEFAULT); return debugModeEnabled; } /** * Whether the Wicket source plugin should be enabled, as specified by configuration settings. * * <p> * If the <tt>isis.viewer.wicket.wicketSourcePlugin</tt> is set, then this is used, otherwise the default is to disable. */ private boolean isWicketSourcePluginEnabled(IsisConfiguration configuration) { final boolean pluginEnabled = configuration.getBoolean(WICKET_SOURCE_PLUGIN_KEY, WICKET_SOURCE_PLUGIN_DEFAULT); return pluginEnabled; } // ////////////////////////////////////// @Override protected void onDestroy() { try { if (isisSessionFactory != null) { isisSessionFactory.destroyServicesAndShutdown(); } getServletContext().setAttribute(WebAppConstants.ISIS_SESSION_FACTORY, null); super.onDestroy(); } catch(final RuntimeException ex) { // symmetry with #init() LOG.error("Failed to destroy", ex); throw ex; } } // ////////////////////////////////////// @Override public RuntimeConfigurationType getConfigurationType() { if(determiningConfigurationType) { // avoiding an infinite loop; have already passed through here once before // this time around, just delegate to web-inf return super.getConfigurationType(); } determineDeploymentTypeIfRequired(); return deploymentType.getConfigurationType(); } protected IsisInjectModule newIsisModule( final DeploymentCategory deploymentCategory, final IsisConfigurationDefault isisConfiguration) { return new IsisInjectModule(deploymentCategory, isisConfiguration); } // ////////////////////////////////////// protected void initWicketComponentInjection(final Injector injector) { getComponentInstantiationListeners().add(new GuiceComponentInjector(this, injector, false)); } // ///////////////////////////////////////////////// // Wicket Hooks // ///////////////////////////////////////////////// /** * Installs a {@link AuthenticatedWebSessionForIsis custom implementation} * of Wicket's own {@link AuthenticatedWebSession}, effectively associating * the Wicket session with the Isis's equivalent session object. * * <p> * In general, it shouldn't be necessary to override this method. */ @Override protected Class<? extends AuthenticatedWebSession> getWebSessionClass() { return AuthenticatedWebSessionForIsis.class; } /** * Installs a {@link ConverterLocator} preconfigured with a number of * implementations to support Isis specific objects. */ @Override protected IConverterLocator newConverterLocator() { final ConverterLocator converterLocator = new ConverterLocator(); converterLocator.set(ObjectAdapter.class, new ConverterForObjectAdapter()); converterLocator.set(ObjectAdapterMemento.class, new ConverterForObjectAdapterMemento()); return converterLocator; } // ///////////////////////////////////////////////// // Component Factories // ///////////////////////////////////////////////// @Override public final ComponentFactoryRegistry getComponentFactoryRegistry() { return componentFactoryRegistry; } // ///////////////////////////////////////////////// // Page Registry // ///////////////////////////////////////////////// /** * Access to other page types. * * <p> * Non-final only for testing purposes; should not typically be overridden. */ @Override public PageClassRegistry getPageClassRegistry() { return pageClassRegistry; } /** * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. */ @Override public Class<? extends Page> getHomePage() { return getPageClassRegistry().getPageClass(PageType.HOME); } /** * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. */ @SuppressWarnings("unchecked") @Override public Class<? extends WebPage> getSignInPageClass() { return (Class<? extends WebPage>) getPageClassRegistry().getPageClass(PageType.SIGN_IN); } /** * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. */ @SuppressWarnings("unchecked") public Class<? extends WebPage> getSignUpPageClass() { return (Class<? extends WebPage>) getPageClassRegistry().getPageClass(PageType.SIGN_UP); } /** * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. */ @SuppressWarnings("unchecked") public Class<? extends WebPage> getSignUpVerifyPageClass() { return (Class<? extends WebPage>) getPageClassRegistry().getPageClass(PageType.SIGN_UP_VERIFY); } /** * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. */ @SuppressWarnings("unchecked") public Class<? extends WebPage> getForgotPasswordPageClass() { return (Class<? extends WebPage>) getPageClassRegistry().getPageClass(PageType.PASSWORD_RESET); } public AuthenticationSession getAuthenticationSession() { return isisSessionFactory.getCurrentSession().getAuthenticationSession(); } public DeploymentCategory getDeploymentCategory() { return deploymentCategory; } @Override public WicketViewerSettings getSettings() { return settings; } }