001/*
002 * (C) Copyright 2015-2017 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     bstefanescu
018 *     Vladimir Pasquier <vpasquier@nuxeo.com>
019 */
020package org.nuxeo.ecm.automation.core;
021
022import java.util.ArrayList;
023import java.util.List;
024
025import javax.management.InstanceAlreadyExistsException;
026import javax.management.InstanceNotFoundException;
027import javax.management.JMException;
028import javax.management.MBeanRegistrationException;
029import javax.management.MBeanServer;
030import javax.management.MalformedObjectNameException;
031import javax.management.NotCompliantMBeanException;
032import javax.management.ObjectName;
033
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036import org.nuxeo.ecm.automation.AutomationAdmin;
037import org.nuxeo.ecm.automation.AutomationFilter;
038import org.nuxeo.ecm.automation.AutomationService;
039import org.nuxeo.ecm.automation.ChainException;
040import org.nuxeo.ecm.automation.OperationException;
041import org.nuxeo.ecm.automation.OperationType;
042import org.nuxeo.ecm.automation.TypeAdapter;
043import org.nuxeo.ecm.automation.context.ContextHelperDescriptor;
044import org.nuxeo.ecm.automation.context.ContextHelperRegistry;
045import org.nuxeo.ecm.automation.context.ContextService;
046import org.nuxeo.ecm.automation.context.ContextServiceImpl;
047import org.nuxeo.ecm.automation.core.events.EventHandler;
048import org.nuxeo.ecm.automation.core.events.EventHandlerRegistry;
049import org.nuxeo.ecm.automation.core.exception.ChainExceptionFilter;
050import org.nuxeo.ecm.automation.core.exception.ChainExceptionImpl;
051import org.nuxeo.ecm.automation.core.impl.ChainTypeImpl;
052import org.nuxeo.ecm.automation.core.impl.OperationServiceImpl;
053import org.nuxeo.ecm.automation.core.trace.TracerFactory;
054import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition;
055import org.nuxeo.ecm.platform.forms.layout.descriptors.WidgetDescriptor;
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.getService(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.getService(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<>();
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 start(ComponentContext context) {
237        if (!tracerFactory.getRecordingState()) {
238            log.info("You can activate automation trace mode to get more informations on automation executions");
239        }
240        try {
241            bindManagement();
242        } catch (JMException e) {
243            throw new RuntimeException("Cannot bind management", e);
244        }
245    }
246
247    @Override
248    public void stop(ComponentContext context) {
249        service.flushCompiledChains();
250        try {
251            unBindManagement();
252        } catch (JMException e) {
253            throw new RuntimeException("Cannot unbind management", e);
254        }
255    }
256}