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    protected ContextHelperRegistry contextHelperRegistry;
093
094    protected ContextService contextService;
095
096    @Override
097    public void activate(ComponentContext context) {
098        service = new OperationServiceImpl();
099        tracerFactory = new TracerFactory();
100        handlers = new EventHandlerRegistry(service);
101        contextHelperRegistry = new ContextHelperRegistry();
102        contextService = new ContextServiceImpl(contextHelperRegistry);
103    }
104
105    protected void bindManagement() throws JMException {
106        ObjectName objectName = new ObjectName("org.nuxeo.automation:name=tracerfactory");
107        MBeanServer mBeanServer = Framework.getService(ServerLocator.class).lookupServer();
108        mBeanServer.registerMBean(tracerFactory, objectName);
109    }
110
111    protected void unBindManagement() throws MalformedObjectNameException, NotCompliantMBeanException,
112            InstanceAlreadyExistsException, MBeanRegistrationException, InstanceNotFoundException {
113        final ObjectName on = new ObjectName("org.nuxeo.automation:name=tracerfactory");
114        final ServerLocator locator = Framework.getService(ServerLocator.class);
115        if (locator != null) {
116            MBeanServer mBeanServer = locator.lookupServer();
117            mBeanServer.unregisterMBean(on);
118        }
119    }
120
121    @Override
122    public void deactivate(ComponentContext context) {
123        service = null;
124        handlers = null;
125        tracerFactory = null;
126    }
127
128    @Override
129    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
130        if (XP_OPERATIONS.equals(extensionPoint)) {
131            OperationContribution opc = (OperationContribution) contribution;
132            List<WidgetDefinition> widgetDefinitionList = new ArrayList<>();
133            if (opc.widgets != null) {
134                for (WidgetDescriptor widgetDescriptor : opc.widgets) {
135                    widgetDefinitionList.add(widgetDescriptor.getWidgetDefinition());
136                }
137            }
138            try {
139                service.putOperation(opc.type, opc.replace, contributor.getName().toString(), widgetDefinitionList);
140            } catch (OperationException e) {
141                throw new RuntimeException(e);
142            }
143        } else if (XP_CHAINS.equals(extensionPoint)) {
144            OperationChainContribution occ = (OperationChainContribution) contribution;
145            // Register the chain
146            try {
147                OperationType docChainType = new ChainTypeImpl(service,
148                        occ.toOperationChain(contributor.getContext().getBundle()), occ);
149                service.putOperation(docChainType, occ.replace);
150            } catch (OperationException e) {
151                // TODO Auto-generated catch block
152                throw new RuntimeException(e);
153            }
154        } else if (XP_CHAIN_EXCEPTION.equals(extensionPoint)) {
155            ChainExceptionDescriptor chainExceptionDescriptor = (ChainExceptionDescriptor) contribution;
156            ChainException chainException = new ChainExceptionImpl(chainExceptionDescriptor);
157            service.putChainException(chainException);
158        } else if (XP_AUTOMATION_FILTER.equals(extensionPoint)) {
159            AutomationFilterDescriptor automationFilterDescriptor = (AutomationFilterDescriptor) contribution;
160            ChainExceptionFilter chainExceptionFilter = new ChainExceptionFilter(automationFilterDescriptor);
161            service.putAutomationFilter(chainExceptionFilter);
162        } else if (XP_ADAPTERS.equals(extensionPoint)) {
163            TypeAdapterContribution tac = (TypeAdapterContribution) contribution;
164            TypeAdapter adapter;
165            try {
166                adapter = tac.clazz.newInstance();
167            } catch (ReflectiveOperationException e) {
168                throw new RuntimeException(e);
169            }
170            service.putTypeAdapter(tac.accept, tac.produce, adapter);
171        } else if (XP_EVENT_HANDLERS.equals(extensionPoint)) {
172            EventHandler eh = (EventHandler) contribution;
173            if (eh.isPostCommit()) {
174                handlers.putPostCommitEventHandler(eh);
175            } else {
176                handlers.putEventHandler(eh);
177            }
178        } else if (XP_CONTEXT_HELPER.equals(extensionPoint)) {
179            contextHelperRegistry.addContribution((ContextHelperDescriptor)
180                    contribution);
181        }
182    }
183
184    @Override
185    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
186        if (XP_OPERATIONS.equals(extensionPoint)) {
187            service.removeOperation(((OperationContribution) contribution).type);
188        } else if (XP_CHAINS.equals(extensionPoint)) {
189            OperationChainContribution occ = (OperationChainContribution) contribution;
190            service.removeOperationChain(occ.getId());
191        } else if (XP_CHAIN_EXCEPTION.equals(extensionPoint)) {
192            ChainExceptionDescriptor chainExceptionDescriptor = (ChainExceptionDescriptor) contribution;
193            ChainException chainException = new ChainExceptionImpl(chainExceptionDescriptor);
194            service.removeExceptionChain(chainException);
195        } else if (XP_AUTOMATION_FILTER.equals(extensionPoint)) {
196            AutomationFilterDescriptor automationFilterDescriptor = (AutomationFilterDescriptor) contribution;
197            AutomationFilter automationFilter = new ChainExceptionFilter(automationFilterDescriptor);
198            service.removeAutomationFilter(automationFilter);
199        } else if (XP_ADAPTERS.equals(extensionPoint)) {
200            TypeAdapterContribution tac = (TypeAdapterContribution) contribution;
201            service.removeTypeAdapter(tac.accept, tac.produce);
202        } else if (XP_EVENT_HANDLERS.equals(extensionPoint)) {
203            EventHandler eh = (EventHandler) contribution;
204            if (eh.isPostCommit()) {
205                handlers.removePostCommitEventHandler(eh);
206            } else {
207                handlers.removeEventHandler(eh);
208            }
209        } else if (XP_CONTEXT_HELPER.equals(extensionPoint)) {
210            contextHelperRegistry.removeContribution(
211                    (ContextHelperDescriptor) contribution);
212        }
213    }
214
215    @Override
216    public <T> T getAdapter(Class<T> adapter) {
217        if (adapter == AutomationService.class || adapter == AutomationAdmin.class) {
218            return adapter.cast(service);
219        }
220        if (adapter == EventHandlerRegistry.class) {
221            return adapter.cast(handlers);
222        }
223        if (adapter == TracerFactory.class) {
224            return adapter.cast(tracerFactory);
225        }
226        if (adapter == ContextService.class) {
227            return adapter.cast(contextService);
228        }
229        return null;
230    }
231
232    @Override
233    public void start(ComponentContext context) {
234        if (!tracerFactory.getRecordingState()) {
235            log.info("You can activate automation trace mode to get more informations on automation executions");
236        }
237        try {
238            bindManagement();
239        } catch (JMException e) {
240            throw new RuntimeException("Cannot bind management", e);
241        }
242    }
243
244    @Override
245    public void stop(ComponentContext context) {
246        service.flushCompiledChains();
247        try {
248            unBindManagement();
249        } catch (JMException e) {
250            throw new RuntimeException("Cannot unbind management", e);
251        }
252    }
253}