/******************************************************************************* * Copyright (c) 2012 RelationWare, Benno Luthiger * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * RelationWare, Benno Luthiger ******************************************************************************/ package org.ripla.web; // NOPMD by Luthiger on 09.09.12 00:42 import java.util.Locale; import org.lunifera.runtime.web.vaadin.osgi.common.OSGiUI; import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.prefs.PreferencesService; import org.osgi.service.useradmin.User; import org.osgi.service.useradmin.UserAdmin; import org.ripla.interfaces.IAppConfiguration; import org.ripla.interfaces.IAuthenticator; import org.ripla.interfaces.IRiplaEventDispatcher; import org.ripla.interfaces.IWorkflowListener; import org.ripla.util.PreferencesHelper; import org.ripla.web.controllers.RiplaBody; import org.ripla.web.interfaces.IBodyComponent; import org.ripla.web.internal.services.ConfigManager; import org.ripla.web.internal.services.RiplaEventDispatcher; import org.ripla.web.internal.services.SkinRegistry; import org.ripla.web.internal.services.UseCaseRegistry; import org.ripla.web.internal.views.RiplaLogin; import org.ripla.web.services.ISkin; import org.ripla.web.util.RiplaRequestHandler; import org.ripla.web.util.RiplaRequestHandler.IRequestParameter; import org.ripla.web.util.ToolbarItemFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinSession; import com.vaadin.ui.Component; import com.vaadin.ui.Layout; import com.vaadin.ui.VerticalLayout; /** * <p> * The base class of all web applications using the Ripla platform. * </p> * <p> * Subclasses may override the following methods:<br /> * <ul> * <li>{@link #beforeInitializeLayout()}</li> * <li>{@link #getAppConfiguration()}</li> * <li>{@link #initBodyView(ISkin)}</li> * <li>{@link #createPreferencesHelper()}</li> * <li>{@link #beforeLogin()}</li> * <li>{@link #workflowExit()}</li> * <li>{@link #initializePermissions()}</li> * <li>{@link #showAfterLogin(User)}</li> * </ul> * </p> * * @author Luthiger */ @SuppressWarnings("serial") public class RiplaApplication extends OSGiUI implements IWorkflowListener { // NOPMD private static final Logger LOG = LoggerFactory .getLogger(RiplaApplication.class); private static final String APP_NAME = "Ripla"; private final PreferencesHelper preferences = createPreferencesHelper(); private final ConfigManager configManager = new ConfigManager(); // private final PermissionHelper permissionHelper = new PermissionHelper(); private RiplaEventDispatcher eventDispatcher; private ToolbarItemFactory toolbarItemFactory; private Layout bodyView; private boolean initialized = false; @Override public final void init(final VaadinRequest inRequest) { initialized = true; toolbarItemFactory = new ToolbarItemFactory(preferences, configManager, null); // synchronize language settings setSessionLocale(preferences.getLocale(getLocale())); setSessionPreferences(preferences); eventDispatcher = new RiplaEventDispatcher(); try { VaadinSession.getCurrent().getLockInstance().lock(); VaadinSession.getCurrent().setAttribute( IRiplaEventDispatcher.class, eventDispatcher); } finally { VaadinSession.getCurrent().getLockInstance().unlock(); } UseCaseRegistry.INSTANCE.registerContextMenus(); SkinRegistry.INSTANCE.setPreferences(preferences); beforeInitializeLayout(); if (!initializeLayout(getAppConfiguration())) { return; } } /** * Hook for subclasses.<br /> * This method is called before the application's layout is initialized. */ protected void beforeInitializeLayout() { // doing nothing } private void setSessionPreferences(final PreferencesHelper inPreferences) { try { VaadinSession.getCurrent().getLockInstance().lock(); VaadinSession.getCurrent().setAttribute(PreferencesHelper.class, inPreferences); } finally { VaadinSession.getCurrent().getLockInstance().unlock(); } } private void setSessionLocale(final Locale inLocale) { try { VaadinSession.getCurrent().getLockInstance().lock(); VaadinSession.getCurrent().setLocale(inLocale); } finally { VaadinSession.getCurrent().getLockInstance().unlock(); } } private void setSessionUser(final User inUser) { try { VaadinSession.getCurrent().getLockInstance().lock(); VaadinSession.getCurrent().setAttribute(User.class, inUser); } finally { VaadinSession.getCurrent().getLockInstance().unlock(); } } /** * Subclasses may override to provide their own * <code>PreferencesHelper</code>. * * @return PreferencesHelper */ protected PreferencesHelper createPreferencesHelper() { return new PreferencesHelper(); } /** * Returns the configuration object to configure the application.<br /> * Subclasses may override. * * @return {@link IAppConfiguration} the configuration object. */ protected IAppConfiguration getAppConfiguration() { return new IAppConfiguration() { @Override public String getWelcome() { return null; } @Override public String getDftSkinID() { return Constants.DFT_SKIN_ID; } @Override public IAuthenticator getLoginAuthenticator() { return null; } @Override public String getAppName() { return APP_NAME; } @Override public String getMenuTagFilter() { return null; } }; } /** * @return String the application's name, as configured in * <code>IAppConfiguration.getAppName()</code> */ public String getAppName() { return getAppConfiguration().getAppName(); } private boolean initializeLayout(final IAppConfiguration inConfiguration) { setStyleName("ripla-window"); //$NON-NLS-1$ SkinRegistry.INSTANCE.setDefaultSkin(inConfiguration.getDftSkinID()); final ISkin lSkin = SkinRegistry.INSTANCE.getActiveSkin(); final VerticalLayout lLayout = new VerticalLayout(); lLayout.setSizeFull(); lLayout.setStyleName("ripla-main"); setContent(lLayout); bodyView = createBody(); lLayout.addComponent(bodyView); lLayout.setExpandRatio(bodyView, 1); if (!beforeLogin(this, bodyView, inConfiguration)) { return false; } if (inConfiguration.getLoginAuthenticator() == null) { bodyView.addComponent(initBodyView(lSkin)); } else { bodyView.addComponent(createLoginView(inConfiguration, lSkin)); } if (lSkin.hasFooter()) { final Component lFooter = lSkin.getFooter(); lLayout.addComponent(lFooter); lLayout.setExpandRatio(lFooter, 0); } return true; } /** * Callback method to display the application's views after the user has * successfully logged in. * * @param inUser * {@link User} the user instance */ public void showAfterLogin(final User inUser) { toolbarItemFactory.setUser(inUser); setSessionUser(inUser); setSessionLocale(preferences.getLocale(inUser, getLocale())); refreshBody(); } /** * Hook for application configuration.<br /> * Subclasses may override to plug in a configuration workflow.<br /> * Use <code>inBodyView.addComponent(getDftView(inConfiguration))</code> to * set the application's layout before starting the workflow. * * @param inWorkflowListener * {@link IWorkflowListener} the listener of the application * workflow configuration * @param inBodyView * Layout the body of the application's main window * @param inConfiguration * IAppConfiguration the application's configuration object * @return boolean <code>true</code> in case there's no need of application * configuration and, therefore, the startup process can continue, * <code>false</code> if the startup is handed over to the * application configuration workflow. */ protected boolean beforeLogin(final IWorkflowListener inWorkflowListener, final Layout inBodyView, final IAppConfiguration inConfiguration) { return true; } private Layout createBody() { final Layout outBody = new VerticalLayout(); outBody.setStyleName("ripla-body"); outBody.setSizeFull(); return outBody; } /** * Refreshes the body component. */ public final void refreshBody() { bodyView.removeAllComponents(); bodyView.addComponent(initBodyView(SkinRegistry.INSTANCE .getActiveSkin())); } /** * Refreshes the whole UI. */ public final void refreshUI() { bodyView.removeAllComponents(); final ISkin lSkin = SkinRegistry.INSTANCE.getActiveSkin(); final IAppConfiguration lConfiguration = getAppConfiguration(); if (lConfiguration.getLoginAuthenticator() == null) { bodyView.addComponent(initBodyView(lSkin)); } else { bodyView.addComponent(createLoginView(lConfiguration, lSkin)); } } /** * Changes the skin used for the UI. * * @param inSkinID * String the ID of the new skin to be used. */ public final void changeSkin(final String inSkinID) { SkinRegistry.INSTANCE.changeSkin(inSkinID); } /** * This implementation creates an instance of {@link RiplaBody}, notifies * the event dispatcher about the new body component, calls the request * handler for that a requested view can be displayed in the main view and * then passes the new view back to the application. * * @param inSkin * {@link ISkin} the actual application skin * @return {@link Component} the application's body view */ private Component initBodyView(final ISkin inSkin) { final IBodyComponent out = createBodyView(inSkin); eventDispatcher.setBodyComponent(out, this); final IRequestParameter requestParameter = RiplaRequestHandler .getParameterFromSession(); if (requestParameter == null) { out.showDefault(); } else { if (!requestParameter.process(out)) { out.showDefault(); } } return (Component) out; } /** * Creates the application's body view.<br /> * Subclasses may override to provide their own body views. * * @param inSkin * {@link ISkin} the actual application skin * @return {@link IBodyComponent} the application's body view */ protected IBodyComponent createBodyView(final ISkin inSkin) { return RiplaBody.createInstance(inSkin, this, getAppConfiguration() .getMenuTagFilter()); } /** * Creates the application's login view.<br /> * Subclasses may override. * * @param inConfiguration * {@link IAppConfiguration} the application's configuration * object * @param inSkin * {@link ISkin} the actual application skin * @return {@link Component} the application's login view */ private Component createLoginView(final IAppConfiguration inConfiguration, final ISkin inSkin) { final VerticalLayout out = new VerticalLayout(); out.setStyleName("ripla-body"); out.setSizeFull(); if (inSkin.hasHeader()) { final Component lHeader = inSkin.getHeader(inConfiguration .getAppName()); out.addComponent(lHeader); out.setExpandRatio(lHeader, 0); } final RiplaLogin lLogin = new RiplaLogin(inConfiguration, this, UseCaseRegistry.INSTANCE.getUserAdmin()); out.addComponent(lLogin); out.setExpandRatio(lLogin, 1); return out; } /** * Creates the application's default view to be added to the body view.<br /> * Usage: <code>bodyView.addComponent(getDftView(inConfiguration))</code> * * @param inConfiguration * {@link IAppConfiguration} * @return {@link Component} the component to add to the body view for that * the application's layout can be displayed without content */ protected Component getDftView(final IAppConfiguration inConfiguration) { final VerticalLayout out = new VerticalLayout(); out.setStyleName("ripla-body"); out.setSizeFull(); final ISkin lSkin = SkinRegistry.INSTANCE.getActiveSkin(); if (lSkin.hasHeader()) { final Component lHeader = lSkin.getHeader(inConfiguration .getAppName()); out.addComponent(lHeader); out.setExpandRatio(lHeader, 0); } if (lSkin.hasFooter()) { final Component lFooter = lSkin.getFooter(); out.addComponent(lFooter); out.setExpandRatio(lFooter, 0); } return out; } /** * We want to save the locale to the preferences store. * * @see com.vaadin.Application#setLocale(java.util.Locale) */ @Override public void setLocale(final Locale inLocale) { if (initialized) { User lUser = null; try { VaadinSession.getCurrent().getLockInstance().lock(); lUser = VaadinSession.getCurrent().getAttribute(User.class); } finally { VaadinSession.getCurrent().getLockInstance().unlock(); } if (lUser == null) { preferences.setLocale(inLocale); } else { preferences.setLocale(inLocale, lUser); } } super.setLocale(inLocale); } /** * Subclasses may override. * * @see org.ripla.web.interfaces.IWorkflowListener#workflowExit(int, * java.lang.String) */ @Override public void workflowExit(final int inReturnCode, final String inMessage) { // intentionally left empty } /** * Allows access to the application's preferences. * * @return {@link PreferencesHelper} */ public PreferencesHelper getPreferences() { return preferences; } /** * Allows access to the application's skin registry. * * @return {@link SkinRegistry} */ public SkinRegistry getSkinRegistry() { return SkinRegistry.INSTANCE; } /** * <p> * Subclasses that want to make use of the OSGi user admin service * functionality may trigger initialization of registered permissions. * </p> * * <p> * This will create a permission group for each registered * <code>IPermissionEntry</code> and add the members defined in those * permission instances. * </p> * * <p> * This requires that such member instances exist already. Thus, subclasses * have to prepare groups (i.e. roles) that can act as members for the * registered permission groups before the registered permission instances * are processed. * </p> * <p> * This method is best called in the subclass's * <code>beforeInitializeLayout()</code> method: * * <pre> * private void beforeInitializeLayout() { * UserAdmin userAdmin = getUserAdmin(); * Group administrators = (Group) userAdmin.createRole("ripla.admin", * Role.GROUP); * initializePermissions(); * } * </pre> * * </p> */ protected void initializePermissions() { UseCaseRegistry.INSTANCE.getPermissionHelper().initializePermissions(); } /** * Makes the OSGi <code>UserAdmin</code> available for subclasses. * * @return {@link UserAdmin} */ protected UserAdmin getUserAdmin() { return UseCaseRegistry.INSTANCE.getUserAdmin(); } /** * The factory method to create a toolbar component instance. <br /> * Toolbar items created with this factory must have a constructor with the * following parameters: * <ul> * <li>org.ripla.web.util.PreferencesHelper</li> * <li>org.ripla.web.internal.services.ConfigManager</li> * <li>org.osgi.service.useradmin.User</li> * </ul> * * @param inClass * Class the toolbar component class * @return {@link Component} the created toolbar component instance or * <code>null</code> in case of an error */ public <T extends Component> T createToolbarItem(final Class<T> inClass) { try { return toolbarItemFactory.createToolbarComponent(inClass); } catch (final Exception exc) { LOG.error("Error encountered while creating the toolbar item!", exc); } return null; } // --- OSGi DS bind and unbind methods --- public void setPreferences(final PreferencesService inPreferences) { preferences.setPreferences(inPreferences); LOG.debug("The OSGi preferences service is made available."); } public void unsetPreferences(final PreferencesService inPreferences) { preferences.dispose(); LOG.debug("Removed the OSGi preferences service."); } public void setConfiAdmin(final ConfigurationAdmin inConfigAdmin) { configManager.setConfigAdmin(inConfigAdmin); } public void unsetConfiAdmin(final ConfigurationAdmin inConfigAdmin) { configManager.clearConfigAdmin(); } }