001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     bstefanescu
016 *     Vladimir Pasquier <vpasquier@nuxeo.com>
017 */
018package org.nuxeo.ecm.automation.core;
019
020import java.util.ArrayList;
021import java.util.List;
022
023import javax.management.InstanceAlreadyExistsException;
024import javax.management.InstanceNotFoundException;
025import javax.management.JMException;
026import javax.management.MBeanRegistrationException;
027import javax.management.MBeanServer;
028import javax.management.MalformedObjectNameException;
029import javax.management.NotCompliantMBeanException;
030import javax.management.ObjectName;
031
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.nuxeo.ecm.automation.AutomationAdmin;
035import org.nuxeo.ecm.automation.AutomationFilter;
036import org.nuxeo.ecm.automation.AutomationService;
037import org.nuxeo.ecm.automation.ChainException;
038import org.nuxeo.ecm.automation.OperationException;
039import org.nuxeo.ecm.automation.OperationType;
040import org.nuxeo.ecm.automation.TypeAdapter;
041import org.nuxeo.ecm.automation.context.ContextHelperDescriptor;
042import org.nuxeo.ecm.automation.context.ContextHelperRegistry;
043import org.nuxeo.ecm.automation.context.ContextService;
044import org.nuxeo.ecm.automation.context.ContextServiceImpl;
045import org.nuxeo.ecm.automation.core.events.EventHandler;
046import org.nuxeo.ecm.automation.core.events.EventHandlerRegistry;
047import org.nuxeo.ecm.automation.core.exception.ChainExceptionFilter;
048import org.nuxeo.ecm.automation.core.exception.ChainExceptionImpl;
049import org.nuxeo.ecm.automation.core.impl.ChainTypeImpl;
050import org.nuxeo.ecm.automation.core.impl.OperationServiceImpl;
051import org.nuxeo.ecm.automation.core.trace.TracerFactory;
052import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition;
053import org.nuxeo.ecm.platform.forms.layout.descriptors.WidgetDescriptor;
054import org.nuxeo.runtime.RuntimeServiceEvent;
055import org.nuxeo.runtime.RuntimeServiceListener;
056import org.nuxeo.runtime.api.Framework;
057import org.nuxeo.runtime.management.ServerLocator;
058import org.nuxeo.runtime.model.ComponentContext;
059import org.nuxeo.runtime.model.ComponentInstance;
060import org.nuxeo.runtime.model.DefaultComponent;
061
062/**
063 * Nuxeo component that provide an implementation of the {@link AutomationService} and handle extensions registrations.
064 *
065 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
066 * @author <a href="mailto:grenard@nuxeo.com">Guillaume Renard</a>
067 */
068public class AutomationComponent extends DefaultComponent {
069
070    private static final Log log = LogFactory.getLog(AutomationComponent.class);
071
072    public static final String XP_OPERATIONS = "operations";
073
074    public static final String XP_ADAPTERS = "adapters";
075
076    public static final String XP_CHAINS = "chains";
077
078    public static final String XP_EVENT_HANDLERS = "event-handlers";
079
080    public static final String XP_CHAIN_EXCEPTION = "chainException";
081
082    public static final String XP_AUTOMATION_FILTER = "automationFilter";
083
084    public static final String XP_CONTEXT_HELPER = "contextHelpers";
085
086    protected OperationServiceImpl service;
087
088    protected EventHandlerRegistry handlers;
089
090    protected TracerFactory tracerFactory;
091
092    public ContextHelperRegistry contextHelperRegistry;
093
094    protected ContextService contextService;
095
096    public static AutomationComponent self;
097
098    @Override
099    public void activate(ComponentContext context) {
100        service = new OperationServiceImpl();
101        tracerFactory = new TracerFactory();
102        handlers = new EventHandlerRegistry(service);
103        self = this;
104        contextService = new ContextServiceImpl();
105        contextHelperRegistry = new ContextHelperRegistry();
106    }
107
108    protected void bindManagement() throws JMException {
109        ObjectName objectName = new ObjectName("org.nuxeo.automation:name=tracerfactory");
110        MBeanServer mBeanServer = Framework.getLocalService(ServerLocator.class).lookupServer();
111        mBeanServer.registerMBean(tracerFactory, objectName);
112    }
113
114    protected void unBindManagement() throws MalformedObjectNameException, NotCompliantMBeanException,
115            InstanceAlreadyExistsException, MBeanRegistrationException, InstanceNotFoundException {
116        final ObjectName on = new ObjectName("org.nuxeo.automation:name=tracerfactory");
117        final ServerLocator locator = Framework.getLocalService(ServerLocator.class);
118        if (locator != null) {
119            MBeanServer mBeanServer = locator.lookupServer();
120            mBeanServer.unregisterMBean(on);
121        }
122    }
123
124    @Override
125    public void deactivate(ComponentContext context) {
126        service = null;
127        handlers = null;
128        tracerFactory = null;
129    }
130
131    @Override
132    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
133        if (XP_OPERATIONS.equals(extensionPoint)) {
134            OperationContribution opc = (OperationContribution) contribution;
135            List<WidgetDefinition> widgetDefinitionList = new ArrayList<WidgetDefinition>();
136            if (opc.widgets != null) {
137                for (WidgetDescriptor widgetDescriptor : opc.widgets) {
138                    widgetDefinitionList.add(widgetDescriptor.getWidgetDefinition());
139                }
140            }
141            try {
142                service.putOperation(opc.type, opc.replace, contributor.getName().toString(), widgetDefinitionList);
143            } catch (OperationException e) {
144                throw new RuntimeException(e);
145            }
146        } else if (XP_CHAINS.equals(extensionPoint)) {
147            OperationChainContribution occ = (OperationChainContribution) contribution;
148            // Register the chain
149            try {
150                OperationType docChainType = new ChainTypeImpl(service,
151                        occ.toOperationChain(contributor.getContext().getBundle()), occ);
152                service.putOperation(docChainType, occ.replace);
153            } catch (OperationException e) {
154                // TODO Auto-generated catch block
155                throw new RuntimeException(e);
156            }
157        } else if (XP_CHAIN_EXCEPTION.equals(extensionPoint)) {
158            ChainExceptionDescriptor chainExceptionDescriptor = (ChainExceptionDescriptor) contribution;
159            ChainException chainException = new ChainExceptionImpl(chainExceptionDescriptor);
160            service.putChainException(chainException);
161        } else if (XP_AUTOMATION_FILTER.equals(extensionPoint)) {
162            AutomationFilterDescriptor automationFilterDescriptor = (AutomationFilterDescriptor) contribution;
163            ChainExceptionFilter chainExceptionFilter = new ChainExceptionFilter(automationFilterDescriptor);
164            service.putAutomationFilter(chainExceptionFilter);
165        } else if (XP_ADAPTERS.equals(extensionPoint)) {
166            TypeAdapterContribution tac = (TypeAdapterContribution) contribution;
167            TypeAdapter adapter;
168            try {
169                adapter = tac.clazz.newInstance();
170            } catch (ReflectiveOperationException e) {
171                throw new RuntimeException(e);
172            }
173            service.putTypeAdapter(tac.accept, tac.produce, adapter);
174        } else if (XP_EVENT_HANDLERS.equals(extensionPoint)) {
175            EventHandler eh = (EventHandler) contribution;
176            if (eh.isPostCommit()) {
177                handlers.putPostCommitEventHandler(eh);
178            } else {
179                handlers.putEventHandler(eh);
180            }
181        } else if (XP_CONTEXT_HELPER.equals(extensionPoint)) {
182            contextHelperRegistry.addContribution((ContextHelperDescriptor)
183                    contribution);
184        }
185    }
186
187    @Override
188    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
189        if (XP_OPERATIONS.equals(extensionPoint)) {
190            service.removeOperation(((OperationContribution) contribution).type);
191        } else if (XP_CHAINS.equals(extensionPoint)) {
192            OperationChainContribution occ = (OperationChainContribution) contribution;
193            service.removeOperationChain(occ.getId());
194        } else if (XP_CHAIN_EXCEPTION.equals(extensionPoint)) {
195            ChainExceptionDescriptor chainExceptionDescriptor = (ChainExceptionDescriptor) contribution;
196            ChainException chainException = new ChainExceptionImpl(chainExceptionDescriptor);
197            service.removeExceptionChain(chainException);
198        } else if (XP_AUTOMATION_FILTER.equals(extensionPoint)) {
199            AutomationFilterDescriptor automationFilterDescriptor = (AutomationFilterDescriptor) contribution;
200            AutomationFilter automationFilter = new ChainExceptionFilter(automationFilterDescriptor);
201            service.removeAutomationFilter(automationFilter);
202        } else if (XP_ADAPTERS.equals(extensionPoint)) {
203            TypeAdapterContribution tac = (TypeAdapterContribution) contribution;
204            service.removeTypeAdapter(tac.accept, tac.produce);
205        } else if (XP_EVENT_HANDLERS.equals(extensionPoint)) {
206            EventHandler eh = (EventHandler) contribution;
207            if (eh.isPostCommit()) {
208                handlers.removePostCommitEventHandler(eh);
209            } else {
210                handlers.removeEventHandler(eh);
211            }
212        } else if (XP_CONTEXT_HELPER.equals(extensionPoint)) {
213            contextHelperRegistry.removeContribution(
214                    (ContextHelperDescriptor) contribution);
215        }
216    }
217
218    @Override
219    public <T> T getAdapter(Class<T> adapter) {
220        if (adapter == AutomationService.class || adapter == AutomationAdmin.class) {
221            return adapter.cast(service);
222        }
223        if (adapter == EventHandlerRegistry.class) {
224            return adapter.cast(handlers);
225        }
226        if (adapter == TracerFactory.class) {
227            return adapter.cast(tracerFactory);
228        }
229        if (adapter == ContextService.class) {
230            return adapter.cast(contextService);
231        }
232        return null;
233    }
234
235    @Override
236    public void applicationStarted(ComponentContext context) {
237        super.applicationStarted(context);
238        if (!tracerFactory.getRecordingState()) {
239            log.info("You can activate automation trace mode to get more informations on automation executions");
240        }
241        try {
242            bindManagement();
243        } catch (JMException e) {
244            throw new RuntimeException(e);
245        }
246        Framework.addListener(new RuntimeServiceListener() {
247
248            @Override
249            public void handleEvent(RuntimeServiceEvent event) {
250                if (event.id != RuntimeServiceEvent.RUNTIME_ABOUT_TO_STOP) {
251                    return;
252                }
253                Framework.removeListener(this);
254                try {
255                    unBindManagement();
256                } catch (MalformedObjectNameException | NotCompliantMBeanException | InstanceAlreadyExistsException
257                        | MBeanRegistrationException | InstanceNotFoundException cause) {
258                    log.error("Cannot unbind management", cause);
259                }
260            }
261        });
262    }
263}