/*
* (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* bstefanescu
* Vladimir Pasquier <vpasquier@nuxeo.com>
*/
package org.nuxeo.ecm.automation.core;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.JMException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.automation.AutomationAdmin;
import org.nuxeo.ecm.automation.AutomationFilter;
import org.nuxeo.ecm.automation.AutomationService;
import org.nuxeo.ecm.automation.ChainException;
import org.nuxeo.ecm.automation.OperationException;
import org.nuxeo.ecm.automation.OperationType;
import org.nuxeo.ecm.automation.TypeAdapter;
import org.nuxeo.ecm.automation.context.ContextHelperDescriptor;
import org.nuxeo.ecm.automation.context.ContextHelperRegistry;
import org.nuxeo.ecm.automation.context.ContextService;
import org.nuxeo.ecm.automation.context.ContextServiceImpl;
import org.nuxeo.ecm.automation.core.events.EventHandler;
import org.nuxeo.ecm.automation.core.events.EventHandlerRegistry;
import org.nuxeo.ecm.automation.core.exception.ChainExceptionFilter;
import org.nuxeo.ecm.automation.core.exception.ChainExceptionImpl;
import org.nuxeo.ecm.automation.core.impl.ChainTypeImpl;
import org.nuxeo.ecm.automation.core.impl.OperationServiceImpl;
import org.nuxeo.ecm.automation.core.trace.TracerFactory;
import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition;
import org.nuxeo.ecm.platform.forms.layout.descriptors.WidgetDescriptor;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.management.ServerLocator;
import org.nuxeo.runtime.model.ComponentContext;
import org.nuxeo.runtime.model.ComponentInstance;
import org.nuxeo.runtime.model.DefaultComponent;
/**
* Nuxeo component that provide an implementation of the {@link AutomationService} and handle extensions registrations.
*
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
* @author <a href="mailto:grenard@nuxeo.com">Guillaume Renard</a>
*/
public class AutomationComponent extends DefaultComponent {
private static final Log log = LogFactory.getLog(AutomationComponent.class);
public static final String XP_OPERATIONS = "operations";
public static final String XP_ADAPTERS = "adapters";
public static final String XP_CHAINS = "chains";
public static final String XP_EVENT_HANDLERS = "event-handlers";
public static final String XP_CHAIN_EXCEPTION = "chainException";
public static final String XP_AUTOMATION_FILTER = "automationFilter";
public static final String XP_CONTEXT_HELPER = "contextHelpers";
protected OperationServiceImpl service;
protected EventHandlerRegistry handlers;
protected TracerFactory tracerFactory;
public ContextHelperRegistry contextHelperRegistry;
protected ContextService contextService;
public static AutomationComponent self;
@Override
public void activate(ComponentContext context) {
service = new OperationServiceImpl();
tracerFactory = new TracerFactory();
handlers = new EventHandlerRegistry(service);
self = this;
contextService = new ContextServiceImpl();
contextHelperRegistry = new ContextHelperRegistry();
}
protected void bindManagement() throws JMException {
ObjectName objectName = new ObjectName("org.nuxeo.automation:name=tracerfactory");
MBeanServer mBeanServer = Framework.getLocalService(ServerLocator.class).lookupServer();
mBeanServer.registerMBean(tracerFactory, objectName);
}
protected void unBindManagement() throws MalformedObjectNameException, NotCompliantMBeanException,
InstanceAlreadyExistsException, MBeanRegistrationException, InstanceNotFoundException {
final ObjectName on = new ObjectName("org.nuxeo.automation:name=tracerfactory");
final ServerLocator locator = Framework.getLocalService(ServerLocator.class);
if (locator != null) {
MBeanServer mBeanServer = locator.lookupServer();
mBeanServer.unregisterMBean(on);
}
}
@Override
public void deactivate(ComponentContext context) {
service = null;
handlers = null;
tracerFactory = null;
}
@Override
public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
if (XP_OPERATIONS.equals(extensionPoint)) {
OperationContribution opc = (OperationContribution) contribution;
List<WidgetDefinition> widgetDefinitionList = new ArrayList<WidgetDefinition>();
if (opc.widgets != null) {
for (WidgetDescriptor widgetDescriptor : opc.widgets) {
widgetDefinitionList.add(widgetDescriptor.getWidgetDefinition());
}
}
try {
service.putOperation(opc.type, opc.replace, contributor.getName().toString(), widgetDefinitionList);
} catch (OperationException e) {
throw new RuntimeException(e);
}
} else if (XP_CHAINS.equals(extensionPoint)) {
OperationChainContribution occ = (OperationChainContribution) contribution;
// Register the chain
try {
OperationType docChainType = new ChainTypeImpl(service,
occ.toOperationChain(contributor.getContext().getBundle()), occ);
service.putOperation(docChainType, occ.replace);
} catch (OperationException e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
}
} else if (XP_CHAIN_EXCEPTION.equals(extensionPoint)) {
ChainExceptionDescriptor chainExceptionDescriptor = (ChainExceptionDescriptor) contribution;
ChainException chainException = new ChainExceptionImpl(chainExceptionDescriptor);
service.putChainException(chainException);
} else if (XP_AUTOMATION_FILTER.equals(extensionPoint)) {
AutomationFilterDescriptor automationFilterDescriptor = (AutomationFilterDescriptor) contribution;
ChainExceptionFilter chainExceptionFilter = new ChainExceptionFilter(automationFilterDescriptor);
service.putAutomationFilter(chainExceptionFilter);
} else if (XP_ADAPTERS.equals(extensionPoint)) {
TypeAdapterContribution tac = (TypeAdapterContribution) contribution;
TypeAdapter adapter;
try {
adapter = tac.clazz.newInstance();
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
service.putTypeAdapter(tac.accept, tac.produce, adapter);
} else if (XP_EVENT_HANDLERS.equals(extensionPoint)) {
EventHandler eh = (EventHandler) contribution;
if (eh.isPostCommit()) {
handlers.putPostCommitEventHandler(eh);
} else {
handlers.putEventHandler(eh);
}
} else if (XP_CONTEXT_HELPER.equals(extensionPoint)) {
contextHelperRegistry.addContribution((ContextHelperDescriptor)
contribution);
}
}
@Override
public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
if (XP_OPERATIONS.equals(extensionPoint)) {
service.removeOperation(((OperationContribution) contribution).type);
} else if (XP_CHAINS.equals(extensionPoint)) {
OperationChainContribution occ = (OperationChainContribution) contribution;
service.removeOperationChain(occ.getId());
} else if (XP_CHAIN_EXCEPTION.equals(extensionPoint)) {
ChainExceptionDescriptor chainExceptionDescriptor = (ChainExceptionDescriptor) contribution;
ChainException chainException = new ChainExceptionImpl(chainExceptionDescriptor);
service.removeExceptionChain(chainException);
} else if (XP_AUTOMATION_FILTER.equals(extensionPoint)) {
AutomationFilterDescriptor automationFilterDescriptor = (AutomationFilterDescriptor) contribution;
AutomationFilter automationFilter = new ChainExceptionFilter(automationFilterDescriptor);
service.removeAutomationFilter(automationFilter);
} else if (XP_ADAPTERS.equals(extensionPoint)) {
TypeAdapterContribution tac = (TypeAdapterContribution) contribution;
service.removeTypeAdapter(tac.accept, tac.produce);
} else if (XP_EVENT_HANDLERS.equals(extensionPoint)) {
EventHandler eh = (EventHandler) contribution;
if (eh.isPostCommit()) {
handlers.removePostCommitEventHandler(eh);
} else {
handlers.removeEventHandler(eh);
}
} else if (XP_CONTEXT_HELPER.equals(extensionPoint)) {
contextHelperRegistry.removeContribution(
(ContextHelperDescriptor) contribution);
}
}
@Override
public <T> T getAdapter(Class<T> adapter) {
if (adapter == AutomationService.class || adapter == AutomationAdmin.class) {
return adapter.cast(service);
}
if (adapter == EventHandlerRegistry.class) {
return adapter.cast(handlers);
}
if (adapter == TracerFactory.class) {
return adapter.cast(tracerFactory);
}
if (adapter == ContextService.class) {
return adapter.cast(contextService);
}
return null;
}
@Override
public void applicationStarted(ComponentContext context) {
if (!tracerFactory.getRecordingState()) {
log.info("You can activate automation trace mode to get more informations on automation executions");
}
try {
bindManagement();
} catch (JMException e) {
throw new RuntimeException("Cannot bind management", e);
}
}
@Override
public void applicationStopped(ComponentContext context, Instant deadline) {
service.flushCompiledChains();
try {
unBindManagement();
} catch (JMException e) {
throw new RuntimeException("Cannot unbind management", e);
}
}
}