///////////////////////////////////////////////////////////////////////////// // // Project ProjectForge Community Edition // www.projectforge.org // // Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de) // // ProjectForge is dual-licensed. // // This community edition is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 3 of the License. // // This community edition is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General // Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, see http://www.gnu.org/licenses/. // ///////////////////////////////////////////////////////////////////////////// package org.projectforge.web.wicket; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletContext; import org.apache.commons.lang.StringUtils; import org.apache.wicket.Application; import org.apache.wicket.ConverterLocator; import org.apache.wicket.IConverterLocator; import org.apache.wicket.Page; import org.apache.wicket.RuntimeConfigurationType; import org.apache.wicket.Session; import org.apache.wicket.core.request.handler.PageProvider; import org.apache.wicket.core.request.handler.RenderPageRequestHandler; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.protocol.http.PageExpiredException; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.request.IRequestHandler; import org.apache.wicket.request.Request; import org.apache.wicket.request.Response; import org.apache.wicket.request.cycle.AbstractRequestCycleListener; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.resource.PackageResourceReference; import org.apache.wicket.resource.loader.BundleStringResourceLoader; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.spring.injection.annot.SpringComponentInjector; import org.apache.wicket.util.lang.Bytes; import org.projectforge.common.BeanHelper; import org.projectforge.common.ExceptionHelper; import org.projectforge.core.Configuration; import org.projectforge.core.ProjectForgeApp; import org.projectforge.database.MyDatabaseUpdateDao; import org.projectforge.plugins.core.PluginsRegistry; import org.projectforge.registry.Registry; import org.projectforge.user.Login; import org.projectforge.user.LoginDefaultHandler; import org.projectforge.user.LoginHandler; import org.projectforge.user.PFUserContext; import org.projectforge.user.UserDao; import org.projectforge.web.UserFilter; import org.projectforge.web.WebConfiguration; import org.projectforge.web.calendar.CalendarPage; import org.projectforge.web.registry.WebRegistry; import org.projectforge.web.wicket.converter.MyDateConverter; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.orm.hibernate3.LocalSessionFactoryBean; import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.context.support.XmlWebApplicationContext; import de.micromata.less.LessWicketApplicationInstantiator; import de.micromata.wicket.request.mapper.PageParameterAwareMountedMapper; /** * Application object for your web application. If you want to run this application without deploying, run the Start class. * * @see org.projectforge.web.AbstractStartHelper.demo.Start#main(String[]) */ public class WicketApplication extends WebApplication implements WicketApplicationInterface { // If you change this you have to change this also in PFApplication. This is used for updating the hsqldb. // private static final String SYSTEM_PROPERTY_HSQLDB_18_UPDATE = "hsqldb18Update"; private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(WicketApplication.class); public static final String RESOURCE_BUNDLE_NAME = "I18nResources"; static Class< ? extends WebPage> DEFAULT_PAGE = CalendarPage.class; private static Boolean developmentMode; private static Boolean stripWicketTags; private static String alertMessage; private static Map<Class< ? extends Page>, String> mountedPages = new HashMap<Class< ? extends Page>, String>(); @SpringBean(name = "wicketApplicationFilter") private WicketApplicationFilter wicketApplicationFilter; private ProjectForgeApp projectForgeApp; /** * At application start the flag developmentMode is perhaps not already set. If possible please use {@link #isDevelopmentSystem()} * instead.<br/> * Please use {@link WebConfiguration#isDevelopmentMode()}. */ public static Boolean internalIsDevelopmentMode() { return developmentMode; } /** * @return true if the application is running and is full available, false e. g. if ProjectForge runs in maintenance mode or is in * start-up phase. */ public static boolean isUpAndRunning() { return ProjectForgeApp.getInstance().isUpAndRunning(); } /** * This method should only be called in test cases! * @param upAndRunning the upAndRunning to set */ public static void internalSetUpAndRunning(final boolean upAndRunning) { ProjectForgeApp.getInstance().internalSetUpAndRunning(upAndRunning); } /** * Please don't use this method, use {@link WicketUtils#getDefaultPage()} instead. * @return */ public static Class< ? extends WebPage> internalGetDefaultPage() { return DEFAULT_PAGE; } /** * Use this method only if you want to change the default page (if no other is defined in config.xml). * @param defaultPage */ public static void setDefaultPage(final Class< ? extends WebPage> defaultPage) { DEFAULT_PAGE = defaultPage; } public static String getBookmarkableMountPath(final Class< ? extends Page> pageClass) { return mountedPages.get(pageClass); } public void setWicketApplicationFilter(final WicketApplicationFilter wicketApplicationFilter) { this.wicketApplicationFilter = wicketApplicationFilter; } /** * Returns the alert message, if exists. The alert message will be displayed on every screen (red on top) and is edit-able via * Administration -> System. */ public static String getAlertMessage() { if (UserFilter.isUpdateRequiredFirst() == true) { return "Maintenance mode: Please restart ProjectForge after finishing." + (alertMessage != null ? " " + alertMessage : ""); } else { return alertMessage; } } /** * @param alertMessage * @see #getAlertMessage() */ public static void setAlertMessage(final String alertMessage) { WicketApplication.alertMessage = alertMessage; } /** * Constructor */ public WicketApplication() { super(); } /** * Own solution: uses development parameter of servlet context init parameter (see context.xml or server.xml). * @return DEVELOPMENT, if development variable of servlet context is set to "true" otherwise DEPLOYMENT. * @see org.apache.wicket.protocol.http.WebApplication#getConfigurationType() */ @Override public RuntimeConfigurationType getConfigurationType() { if (isDevelopmentSystem() == true) { return RuntimeConfigurationType.DEVELOPMENT; } return RuntimeConfigurationType.DEPLOYMENT; } @Override protected void init() { super.init(); // CryptoMapper doesn't work with FullCalendar. // setRootRequestMapper(new CryptoMapper(getRootRequestMapper(), this)); final XmlWebApplicationContext webApplicationContext = (XmlWebApplicationContext) WebApplicationContextUtils .getWebApplicationContext(getServletContext()); final ConfigurableListableBeanFactory beanFactory = webApplicationContext.getBeanFactory(); beanFactory.autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false); final LocalSessionFactoryBean localSessionFactoryBean = (LocalSessionFactoryBean) beanFactory.getBean("&sessionFactory"); final org.hibernate.cfg.Configuration hibernateConfiguration = localSessionFactoryBean.getConfiguration(); final PluginsRegistry pluginsRegistry = PluginsRegistry.instance(); pluginsRegistry.set(getResourceSettings()); projectForgeApp = ProjectForgeApp.init(beanFactory, hibernateConfiguration); // Own error page for deployment mode and UserException and AccessException. getRequestCycleListeners().add(new AbstractRequestCycleListener() { /** * Log only non ProjectForge exceptions. * @see org.apache.wicket.request.cycle.AbstractRequestCycleListener#onException(org.apache.wicket.request.cycle.RequestCycle, * java.lang.Exception) */ @Override public IRequestHandler onException(final RequestCycle cycle, final Exception ex) { // in case of expired session, please redirect to home page if (ex instanceof PageExpiredException) { return super.onException(cycle, ex); } final Throwable rootCause = ExceptionHelper.getRootCause(ex); // log.error(rootCause.getMessage(), ex); // if (rootCause instanceof ProjectForgeException == false) { // return super.onException(cycle, ex); // } // return null; if (isDevelopmentSystem() == true) { log.error(ex.getMessage(), ex); if (rootCause instanceof SQLException) { SQLException next = (SQLException) rootCause; while ((next = next.getNextException()) != null) { log.error(next.getMessage(), next); } } return super.onException(cycle, ex); } else { // Show always this error page in production mode: return new RenderPageRequestHandler(new PageProvider(new ErrorPage(ex))); } } }); getApplicationSettings().setDefaultMaximumUploadSize(Bytes.megabytes(100)); getMarkupSettings().setDefaultMarkupEncoding("utf-8"); final MyAuthorizationStrategy authStrategy = new MyAuthorizationStrategy(); getSecuritySettings().setAuthorizationStrategy(authStrategy); getSecuritySettings().setUnauthorizedComponentInstantiationListener(authStrategy); // Prepend the resource bundle for overwriting some Wicket default localizations (such as StringValidator.*) getResourceSettings().getStringResourceLoaders().add(new BundleStringResourceLoader(RESOURCE_BUNDLE_NAME)); if (isDevelopmentSystem() == false) { getResourceSettings().setThrowExceptionOnMissingResource(false); // Don't throw MissingResourceException for // missing i18n keys in production mode. } getApplicationSettings().setPageExpiredErrorPage(PageExpiredPage.class); // Don't show expired page. // getSessionSettings().setMaxPageMaps(20); // Map up to 20 pages per session (default is 5). getComponentInstantiationListeners().add(new SpringComponentInjector(this)); getApplicationSettings().setInternalErrorPage(ErrorPage.class); // getRequestCycleSettings().setGatherExtendedBrowserInfo(true); // For getting browser width and height. // Select2: // final ApplicationSettings select2Settings = ApplicationSettings.get(); // select2Settings.setIncludeJavascript(false); // if ("true".equals(System.getProperty(SYSTEM_PROPERTY_HSQLDB_18_UPDATE)) == true) { // try { // log.info("Send SHUTDOWN COMPACT to upgrade data-base version:"); // final DataSource dataSource = (DataSource)beanFactory.getBean("dataSource"); // dataSource.getConnection().createStatement().execute("SHUTDOWN COMPACT"); // log.fatal("************ PLEASE RESTART APPLICATION NOW FOR PROPER INSTALLATION !!!!!!!!!!!!!! ************"); // return; // } catch (final SQLException ex) { // log.fatal("Data-base SHUTDOWN COMPACT failed: " + ex.getMessage()); // } // } // Javascript Resource settings getJavaScriptLibrarySettings().setJQueryReference(new PackageResourceReference(WicketApplication.class, "scripts/jquery.js")); final ServletContext servletContext = getServletContext(); final String configContextPath = projectForgeApp.getConfigXml().getServletContextPath(); String contextPath; if (StringUtils.isBlank(configContextPath) == true) { contextPath = servletContext.getContextPath(); projectForgeApp.getConfigXml().setServletContextPath(contextPath); } else { contextPath = configContextPath; } log.info("Using servlet context path: " + contextPath); if (this.wicketApplicationFilter != null) { this.wicketApplicationFilter.setApplication(this); } else { throw new RuntimeException("this.wicketApplicationFilter is null"); } WicketUtils.setContextPath(contextPath); UserFilter.initialize(Registry.instance().getDao(UserDao.class), contextPath); for (final Map.Entry<String, Class< ? extends WebPage>> mountPage : WebRegistry.instance().getMountPages().entrySet()) { final String path = mountPage.getKey(); final Class< ? extends WebPage> pageClass = mountPage.getValue(); mountPageWithPageParameterAwareness(path, pageClass); mountedPages.put(pageClass, path); } if (isDevelopmentSystem() == true) { if (isStripWicketTags() == true) { log.info("Strip Wicket tags also in development mode at default (see context.xml)."); Application.get().getMarkupSettings().setStripWicketTags(true); } getDebugSettings().setOutputMarkupContainerClassName(true); } try { PFUserContext.setUser(MyDatabaseUpdateDao.__internalGetSystemAdminPseudoUser()); // Logon admin user. if (projectForgeApp.getMyDatabaseUpdater().getSystemUpdater().isUpdated() == false) { // Force redirection to update page: UserFilter.setUpdateRequiredFirst(true); } } finally { PFUserContext.setUser(null); } LoginHandler loginHandler; if (StringUtils.isNotBlank(projectForgeApp.getConfigXml().getLoginHandlerClass()) == true) { loginHandler = (LoginHandler) BeanHelper.newInstance(projectForgeApp.getConfigXml().getLoginHandlerClass()); } else { loginHandler = new LoginDefaultHandler(); } // initialize styles compiler try { final LessWicketApplicationInstantiator lessInstantiator = new LessWicketApplicationInstantiator(this, "styles", "projectforge.less", "projectforge.css"); lessInstantiator.instantiate(); } catch (final Exception e) { log.error("Unable to instantiate wicket less compiler", e); } if (loginHandler == null) { log.error("Can't load login handler '" + projectForgeApp.getConfigXml().getLoginHandlerClass() + "'. No login will be possible!"); } else { loginHandler.initialize(); Login.getInstance().setLoginHandler(loginHandler); if (UserFilter.isUpdateRequiredFirst() == false) { projectForgeApp.finalizeInitialization(); } } getPageSettings().setRecreateMountedPagesAfterExpiry(false); } private void mountPageWithPageParameterAwareness(final String path, final Class< ? extends WebPage> pageClass) { mount(new PageParameterAwareMountedMapper(path, pageClass)); } @Override protected void onDestroy() { ProjectForgeApp.shutdown(); } /** * @return True if configured as servlet context param. */ public boolean isDevelopmentSystem() { if (developmentMode == null) { final String value = getServletContext().getInitParameter("development"); developmentMode = "true".equals(value); Configuration.getInstance().internalSetDevelopmentMode(developmentMode); } return developmentMode; } @Override public boolean isStripWicketTags() { if (stripWicketTags == null) { if (isDevelopmentSystem() == false) { stripWicketTags = true; } else { final String value = getServletContext().getInitParameter("stripWicketTags"); stripWicketTags = "true".equals(value); } } return stripWicketTags; } /** * @see org.apache.wicket.Application#getHomePage() */ @Override public Class< ? extends WebPage> getHomePage() { return WicketUtils.getDefaultPage(); } @Override public Session newSession(final Request request, final Response response) { final MySession mySession = new MySession(request); return mySession; } /** * From http://www.danwalmsley.com/2009/04/08/apache-wicket-on-google-app-engine-for-java/<br/> * Override the newSessionStore() method to return HttpSessionStore, because the default second level session store uses java.io.File, * which is sometimes not allowed. * @see org.apache.wicket.Application#newSessionStore() */ /* * @Override protected ISessionStore newSessionStore() { return new org.apache.wicket.protocol.http.HttpSessionStore(this); } */ /** * */ @Override protected IConverterLocator newConverterLocator() { final ConverterLocator converterLocator = new ConverterLocator(); converterLocator.set(java.util.Date.class, new MyDateConverter()); converterLocator.set(java.sql.Date.class, new MyDateConverter(java.sql.Date.class, "S-")); return converterLocator; } public static long getStartTime() { if (ProjectForgeApp.getInstance() == null) { // Should only occur in test cases. return 0; } return ProjectForgeApp.getInstance().getStartTime(); } }