/******************************************************************************* * Copyright (c) 2012-2013 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.internal.services; import java.lang.annotation.Annotation; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import org.osgi.service.useradmin.UserAdmin; import org.ripla.annotations.UseCaseController; import org.ripla.interfaces.IControllerConfiguration; import org.ripla.interfaces.IControllerSet; import org.ripla.interfaces.IMenuExtendible; import org.ripla.interfaces.IMenuItem; import org.ripla.services.IExtendibleMenuContribution; import org.ripla.web.interfaces.IMenuSet; import org.ripla.web.interfaces.IPluggable; import org.ripla.web.internal.menu.ContextMenuManager; import org.ripla.web.internal.menu.ExtendibleMenuHandler; import org.ripla.web.internal.menu.MenuFactory; import org.ripla.web.services.IUseCase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Singleton instance to register the use cases provided by usecase bundles. The * provided use cases are injected through the service consumer * <code>UseCaseComponent</code>. * * @author Luthiger * @see UseCaseComponent */ public enum UseCaseRegistry { INSTANCE; private static final Logger LOG = LoggerFactory.getLogger(UseCaseRegistry.class); private final transient List<IUseCase> useCases = Collections.synchronizedList(new ArrayList<IUseCase>()); private final transient Map<String, ExtendibleMenuHandler> extendibleMenus = Collections .synchronizedMap(new HashMap<String, ExtendibleMenuHandler>()); private static final String PREFIX_ROOT = "/"; //$NON-NLS-1$ private static final String PREFIX_BIN = "/bin/"; //$NON-NLS-1$ private static final String SUFFIX_CLASS = ".class"; //$NON-NLS-1$ private final transient PermissionHelper permissionHelper = new PermissionHelper(); private final transient ControllerManager controllerManager = new ControllerManager(); private final transient ContextMenuManager contextMenuManager = ContextMenuManager.createInstance(); private transient boolean automaticContextMenuRegistration = false; /** * @return {@link ControllerManager} the application's controller manager */ public ControllerManager getControllerManager() { return controllerManager; } /** * @return {@link ContextMenuManager} the application's context menu manager */ public ContextMenuManager getContextMenuManager() { return contextMenuManager; } /** * @param inUserAdmin */ public void setUserAdmin(final UserAdmin inUserAdmin) { controllerManager.setUserAdmin(inUserAdmin); permissionHelper.setUserAdmin(inUserAdmin); } /** * @return {@link UserAdmin} the user admin instance */ public UserAdmin getUserAdmin() { return controllerManager.getUserAdmin(); } /** * Add the use case. * * @param inUseCase * {@link IUseCase} */ public void addUseCase(final IUseCase inUseCase) { // register useCases.add(inUseCase); // register task classes controllerManager.addControllerSet(inUseCase.getControllerSet()); controllerManager.addControllerSet(lookupControllers(inUseCase.getControllerClasses(), inUseCase.getClass())); if (automaticContextMenuRegistration) { registerContextMenus(inUseCase); } LOG.debug("Added use case {}.", inUseCase.toString()); //$NON-NLS-1$ } public void registerContextMenus() { for (final IUseCase lUseCase : useCases) { registerContextMenus(lUseCase); } automaticContextMenuRegistration = true; } private void registerContextMenus(final IUseCase inUseCase) { for (final IMenuSet lMenuSet : inUseCase.getContextMenus()) { contextMenuManager.addContextMenuSet(lMenuSet); } } /** * Remove the use case. * * @param inUseCase * {@link IUseCase} */ public void removeUseCase(final IUseCase inUseCase) { useCases.remove(inUseCase); controllerManager.removeControllerSet(inUseCase.getControllerSet()); controllerManager .removeControllerSet(lookupControllers(inUseCase.getControllerClasses(), inUseCase.getClass())); for (final IMenuSet lMenuSet : inUseCase.getContextMenus()) { contextMenuManager.removeContextMenuSet(lMenuSet); } LOG.debug("Removed use case {}.", inUseCase.toString()); //$NON-NLS-1$ } // --- extendible menu contributions --- /** * Registers the contribution to the extendible menus. * * @param inContribution * {@link IExtendibleMenuContribution} */ public void registerMenuContribution(final IExtendibleMenuContribution inContribution) { final String lMenuID = inContribution.getExtendibleMenuID(); final ExtendibleMenuHandler lExtendibleMenu = extendibleMenus.get(lMenuID); if (lExtendibleMenu == null) { extendibleMenus.put(lMenuID, new ExtendibleMenuHandler(inContribution)); } else { lExtendibleMenu.addContribution(inContribution); } } /** * Unregisters the contribution from the extendible menus. * * @param inContribution * {@link IExtendibleMenuContribution} */ public void unregisterMenuContribution(final IExtendibleMenuContribution inContribution) { final ExtendibleMenuHandler lExtendibleMenu = extendibleMenus.get(inContribution.getExtendibleMenuID()); if (lExtendibleMenu != null) { lExtendibleMenu.removeContribution(inContribution); } } /** * Create the menu based on the registered use cases. * * @return Collection<MenuFactory> the collection of menus */ public Collection<MenuFactory> getMenus() { return getMenus(useCases); } private Collection<MenuFactory> getMenus(final List<IUseCase> inUseCases) { final List<MenuFactory> outFactories = new ArrayList<MenuFactory>(); for (final IUseCase lUseCase : inUseCases) { final IMenuItem lMenu = lUseCase.getMenu(); if (lMenu instanceof IMenuExtendible) { final String lMenuID = ((IMenuExtendible) lMenu).getMenuID(); final ExtendibleMenuHandler lExtendibleMenu = extendibleMenus.get(lMenuID); if (lExtendibleMenu != null) { outFactories.add(lExtendibleMenu.getMenuFactory((IMenuExtendible) lMenu)); } } else { outFactories.add(new MenuFactory(lUseCase.getMenu())); // NOPMD } } Collections.sort(outFactories); return outFactories; } private IControllerSet lookupControllers(final Package inControllerClasses, final Class<?> inClass) { if (inControllerClasses == null) { return new EmptyControllerSet(); } final String lPackagName = inControllerClasses.getName(); final String lPath = lPackagName.replace(".", "/"); //$NON-NLS-1$ //$NON-NLS-2$ final Bundle lBundle = FrameworkUtil.getBundle(inClass); Enumeration<?> lControllers = lBundle.findEntries(String.format("%s%s", PREFIX_BIN, lPath), "*" + SUFFIX_CLASS, //$NON-NLS-1$ //$NON-NLS-2$ false); if (lControllers != null) { return createControllerSet(lControllers, lBundle, PREFIX_BIN); } lControllers = lBundle.findEntries(String.format("%s%s", PREFIX_ROOT, lPath), "*" + SUFFIX_CLASS, false); //$NON-NLS-1$ //$NON-NLS-2$ if (lControllers != null) { return createControllerSet(lControllers, lBundle, PREFIX_ROOT); } return new EmptyControllerSet(); } private IControllerSet createControllerSet(final Enumeration<?> inControllers, final Bundle inBundle, final String inPrefix) { final Collection<IControllerConfiguration> lControllerConfigurations = new ArrayList<IControllerConfiguration>(); while (inControllers.hasMoreElements()) { final IControllerConfiguration lControllerConfiguration = createControllerConfiguration( (URL) inControllers.nextElement(), inBundle, inPrefix); if (lControllerConfiguration != null) { lControllerConfigurations.add(lControllerConfiguration); } } return new ControllerSet(lControllerConfigurations); } @SuppressWarnings("unchecked") private IControllerConfiguration createControllerConfiguration(final URL inController, final Bundle inBundle, final String inPrefix) { final String lPath = inController.getPath(); final String lClassName = lPath.substring(inPrefix.length(), lPath.length() - SUFFIX_CLASS.length()) .replace("/", "."); //$NON-NLS-1$ //$NON-NLS-2$ try { final Class<IPluggable> lClass = (Class<IPluggable>) inBundle.loadClass(lClassName); final Annotation lControllerAnnotations = lClass.getAnnotation(UseCaseController.class); if (lControllerAnnotations != null) { return new ControllerConfiguration(inBundle, lClassName); } } catch (ClassNotFoundException | NoClassDefFoundError exc) { // NOPMD // intentionally left empty } return null; } /** * @return {@link PermissionHelper} the <code>PermissionHelper</code> * instance */ public PermissionHelper getPermissionHelper() { return permissionHelper; } // --- private classes --- private static class ControllerSet implements IControllerSet { private final transient IControllerConfiguration[] controllers; ControllerSet(final Collection<IControllerConfiguration> inControllers) { controllers = new IControllerConfiguration[inControllers.size()]; final Iterator<IControllerConfiguration> lControllers = inControllers.iterator(); for (int i = 0; i < controllers.length; i++) { controllers[i] = lControllers.next(); } } @Override public IControllerConfiguration[] getControllerConfigurations() { return Arrays.copyOf(controllers, controllers.length); } } private static class EmptyControllerSet implements IControllerSet { @Override public IControllerConfiguration[] getControllerConfigurations() { return new IControllerConfiguration[] {}; } } private static class ControllerConfiguration implements IControllerConfiguration { private final transient Bundle bundle; private final transient String controllerName; ControllerConfiguration(final Bundle inBundle, final String inControllerName) { bundle = inBundle; controllerName = inControllerName; } @Override public Bundle getBundle() { return bundle; } @Override public String getControllerName() { return controllerName; } } }