001/*
002 * (C) Copyright 2015 Nuxeo SA (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.RuntimeServiceEvent;
057import org.nuxeo.runtime.RuntimeServiceListener;
058import org.nuxeo.runtime.api.Framework;
059import org.nuxeo.runtime.management.ServerLocator;
060import org.nuxeo.runtime.model.ComponentContext;
061import org.nuxeo.runtime.model.ComponentInstance;
062import org.nuxeo.runtime.model.DefaultComponent;
063
064/**
065 * Nuxeo component that provide an implementation of the {@link AutomationService} and handle extensions registrations.
066 *
067 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
068 * @author <a href="mailto:grenard@nuxeo.com">Guillaume Renard</a>
069 */
070public class AutomationComponent extends DefaultComponent {
071
072    private static final Log log = LogFactory.getLog(AutomationComponent.class);
073
074    public static final String XP_OPERATIONS = "operations";
075
076    public static final String XP_ADAPTERS = "adapters";
077
078    public static final String XP_CHAINS = "chains";
079
080    public static final String XP_EVENT_HANDLERS = "event-handlers";
081
082    public static final String XP_CHAIN_EXCEPTION = "chainException";
083
084    public static final String XP_AUTOMATION_FILTER = "automationFilter";
085
086    public static final String XP_CONTEXT_HELPER = "contextHelpers";
087
088    protected OperationServiceImpl service;
089
090    protected EventHandlerRegistry handlers;
091
092    protected TracerFactory tracerFactory;
093
094    public ContextHelperRegistry contextHelperRegistry;
095
096    protected ContextService contextService;
097
098    public static AutomationComponent self;
099
100    @Override
101    public void activate(ComponentContext context) {
102        service = new OperationServiceImpl();
103        tracerFactory = new TracerFactory();
104        handlers = new EventHandlerRegistry(service);
105        self = this;
106        contextService = new ContextServiceImpl();
107        contextHelperRegistry = new ContextHelperRegistry();
108    }
109
110    protected void bindManagement() throws JMException {
111        ObjectName objectName = new ObjectName("org.nuxeo.automation:name=tracerfactory");
112        MBeanServer mBeanServer = Framework.getLocalService(ServerLocator.class).lookupServer();
113        mBeanServer.registerMBean(tracerFactory, objectName);
114    }
115
116    protected void unBindManagement() throws MalformedObjectNameException, NotCompliantMBeanException,
117            InstanceAlreadyExistsException, MBeanRegistrationException, InstanceNotFoundException {
118        final ObjectName on = new ObjectName("org.nuxeo.automation:name=tracerfactory");
119        final ServerLocator locator = Framework.getLocalService(ServerLocator.class);
120        if (locator != null) {
121            MBeanServer mBeanServer = locator.lookupServer();
122            mBeanServer.unregisterMBean(on);
123        }
124    }
125
126    @Override
127    public void deactivate(ComponentContext context) {
128        service = null;
129        handlers = null;
130        tracerFactory = null;
131    }
132
133    @Override
134    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
135        if (XP_OPERATIONS.equals(extensionPoint)) {
136            OperationContribution opc = (OperationContribution) contribution;
137            List<WidgetDefinition> widgetDefinitionList = new ArrayList<WidgetDefinition>();
138            if (opc.widgets != null) {
139                for (WidgetDescriptor widgetDescriptor : opc.widgets) {
140                    widgetDefinitionList.add(widgetDescriptor.getWidgetDefinition());
141                }
142            }
143            try {
144                service.putOperation(opc.type, opc.replace, contributor.getName().toString(), widgetDefinitionList);
145            } catch (OperationException e) {
146                throw new RuntimeException(e);
147            }
148        } else if (XP_CHAINS.equals(extensionPoint)) {
149            OperationChainContribution occ = (OperationChainContribution) contribution;
150            // Register the chain
151            try {
152                OperationType docChainType = new ChainTypeImpl(service,
153                        occ.toOperationChain(contributor.getContext().getBundle()), occ);
154                service.putOperation(docChainType, occ.replace);
155            } catch (OperationException e) {
156                // TODO Auto-generated catch block
157                throw new RuntimeException(e);
158            }
159        } else if (XP_CHAIN_EXCEPTION.equals(extensionPoint)) {
160            ChainExceptionDescriptor chainExceptionDescriptor = (ChainExceptionDescriptor) contribution;
161            ChainException chainException = new ChainExceptionImpl(chainExceptionDescriptor);
162            service.putChainException(chainException);
163        } else if (XP_AUTOMATION_FILTER.equals(extensionPoint)) {
164            AutomationFilterDescriptor automationFilterDescriptor = (AutomationFilterDescriptor) contribution;
165            ChainExceptionFilter chainExceptionFilter = new ChainExceptionFilter(automationFilterDescriptor);
166            service.putAutomationFilter(chainExceptionFilter);
167        } else if (XP_ADAPTERS.equals(extensionPoint)) {
168            TypeAdapterContribution tac = (TypeAdapterContribution) contribution;
169            TypeAdapter adapter;
170            try {
171                adapter = tac.clazz.newInstance();
172            } catch (ReflectiveOperationException e) {
173                throw new RuntimeException(e);
174            }
175            service.putTypeAdapter(tac.accept, tac.produce, adapter);
176        } else if (XP_EVENT_HANDLERS.equals(extensionPoint)) {
177            EventHandler eh = (EventHandler) contribution;
178            if (eh.isPostCommit()) {
179                handlers.putPostCommitEventHandler(eh);
180            } else {
181                handlers.putEventHandler(eh);
182            }
183        } else if (XP_CONTEXT_HELPER.equals(extensionPoint)) {
184            contextHelperRegistry.addContribution((ContextHelperDescriptor)
185                    contribution);
186        }
187    }
188
189    @Override
190    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
191        if (XP_OPERATIONS.equals(extensionPoint)) {
192            service.removeOperation(((OperationContribution) contribution).type);
193        } else if (XP_CHAINS.equals(extensionPoint)) {
194            OperationChainContribution occ = (OperationChainContribution) contribution;
195            service.removeOperationChain(occ.getId());
196        } else if (XP_CHAIN_EXCEPTION.equals(extensionPoint)) {
197            ChainExceptionDescriptor chainExceptionDescriptor = (ChainExceptionDescriptor) contribution;
198            ChainException chainException = new ChainExceptionImpl(chainExceptionDescriptor);
199            service.removeExceptionChain(chainException);
200        } else if (XP_AUTOMATION_FILTER.equals(extensionPoint)) {
201            AutomationFilterDescriptor automationFilterDescriptor = (AutomationFilterDescriptor) contribution;
202            AutomationFilter automationFilter = new ChainExceptionFilter(automationFilterDescriptor);
203            service.removeAutomationFilter(automationFilter);
204        } else if (XP_ADAPTERS.equals(extensionPoint)) {
205            TypeAdapterContribution tac = (TypeAdapterContribution) contribution;
206            service.removeTypeAdapter(tac.accept, tac.produce);
207        } else if (XP_EVENT_HANDLERS.equals(extensionPoint)) {
208            EventHandler eh = (EventHandler) contribution;
209            if (eh.isPostCommit()) {
210                handlers.removePostCommitEventHandler(eh);
211            } else {
212                handlers.removeEventHandler(eh);
213            }
214        } else if (XP_CONTEXT_HELPER.equals(extensionPoint)) {
215            contextHelperRegistry.removeContribution(
216                    (ContextHelperDescriptor) contribution);
217        }
218    }
219
220    @Override
221    public <T> T getAdapter(Class<T> adapter) {
222        if (adapter == AutomationService.class || adapter == AutomationAdmin.class) {
223            return adapter.cast(service);
224        }
225        if (adapter == EventHandlerRegistry.class) {
226            return adapter.cast(handlers);
227        }
228        if (adapter == TracerFactory.class) {
229            return adapter.cast(tracerFactory);
230        }
231        if (adapter == ContextService.class) {
232            return adapter.cast(contextService);
233        }
234        return null;
235    }
236
237    @Override
238    public void applicationStarted(ComponentContext context) {
239        super.applicationStarted(context);
240        if (!tracerFactory.getRecordingState()) {
241            log.info("You can activate automation trace mode to get more informations on automation executions");
242        }
243        try {
244            bindManagement();
245        } catch (JMException e) {
246            throw new RuntimeException(e);
247        }
248        Framework.addListener(new RuntimeServiceListener() {
249
250            @Override
251            public void handleEvent(RuntimeServiceEvent event) {
252                if (event.id != RuntimeServiceEvent.RUNTIME_ABOUT_TO_STOP) {
253                    return;
254                }
255                Framework.removeListener(this);
256                try {
257                    unBindManagement();
258                } catch (MalformedObjectNameException | NotCompliantMBeanException | InstanceAlreadyExistsException
259                        | MBeanRegistrationException | InstanceNotFoundException cause) {
260                    log.error("Cannot unbind management", cause);
261                }
262            }
263        });
264    }
265}