001/*
002 * (C) Copyright 2012 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 *     Florent Guillaume
018 */
019package org.nuxeo.ecm.core.management.jtajca.internal;
020
021import java.util.HashMap;
022import java.util.Map;
023
024import javax.management.InstanceAlreadyExistsException;
025import javax.management.InstanceNotFoundException;
026import javax.management.MBeanRegistrationException;
027import javax.management.MBeanServer;
028import javax.management.MalformedObjectNameException;
029import javax.management.NotCompliantMBeanException;
030import javax.management.ObjectInstance;
031import javax.management.ObjectName;
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.nuxeo.ecm.core.api.CoreInstance;
035import org.nuxeo.ecm.core.api.CoreSession;
036import org.nuxeo.ecm.core.management.jtajca.CoreSessionMonitor;
037import org.nuxeo.ecm.core.management.jtajca.Defaults;
038import org.nuxeo.ecm.core.management.jtajca.ConnectionPoolMonitor;
039import org.nuxeo.ecm.core.management.jtajca.TransactionMonitor;
040import org.nuxeo.runtime.api.Framework;
041import org.nuxeo.runtime.jtajca.NuxeoConnectionManager;
042import org.nuxeo.runtime.jtajca.NuxeoContainer;
043import org.nuxeo.runtime.jtajca.NuxeoContainerListener;
044import org.nuxeo.runtime.management.ServerLocator;
045import org.nuxeo.runtime.model.ComponentContext;
046import org.nuxeo.runtime.model.DefaultComponent;
047
048/**
049 * Component used to install/uninstall the monitors (transaction and connections).
050 *
051 * @since 5.6
052 */
053public class DefaultMonitorComponent extends DefaultComponent {
054
055    private final ConnectionManagerUpdater cmUpdater = new ConnectionManagerUpdater();
056
057    private class ConnectionManagerUpdater implements NuxeoContainerListener {
058        @Override
059        public void handleNewConnectionManager(String name, NuxeoConnectionManager cm) {
060            ConnectionPoolMonitor monitor = new DefaultConnectionPoolMonitor(name, cm);
061            monitor.install();
062            poolConnectionMonitors.put(name, monitor);
063        }
064
065        @Override
066        public void handleConnectionManagerReset(String name, NuxeoConnectionManager cm) {
067            DefaultConnectionPoolMonitor monitor = (DefaultConnectionPoolMonitor) poolConnectionMonitors.get(name);
068            monitor.handleNewConnectionManager(cm);
069        }
070
071        @Override
072        public void handleConnectionManagerDispose(String name, NuxeoConnectionManager mgr) {
073            ConnectionPoolMonitor monitor = poolConnectionMonitors.remove(name);
074            monitor.uninstall();
075        }
076
077    }
078
079    protected final Log log = LogFactory.getLog(DefaultMonitorComponent.class);
080
081    protected CoreSessionMonitor coreSessionMonitor;
082
083    protected TransactionMonitor transactionMonitor;
084
085    protected Map<String, ConnectionPoolMonitor> poolConnectionMonitors = new HashMap<>();
086
087    @Override
088    public void activate(ComponentContext context) {
089        super.activate(context);
090        install();
091    }
092
093    @Override
094    public void deactivate(ComponentContext context) {
095        uninstall();
096        super.deactivate(context);
097    }
098
099    protected boolean installed;
100
101    protected void install() {
102        installed = true;
103
104        coreSessionMonitor = new DefaultCoreSessionMonitor();
105        coreSessionMonitor.install();
106
107        transactionMonitor = new DefaultTransactionMonitor();
108        transactionMonitor.install();
109
110        NuxeoContainer.addListener(cmUpdater);
111    }
112
113    /**
114     * Make sure we open the repository, to initialize its connection manager.
115     */
116    protected void activateRepository(String repositoryName) {
117        try (CoreSession session = CoreInstance.openCoreSessionSystem(repositoryName)) {
118            // do nothing, just open and close
119        }
120    }
121
122    protected void uninstall() {
123        if (!installed) {
124            return;
125        }
126        // temporary log to help diagnostics
127        log.warn("Total commits during server life: " + transactionMonitor.getTotalCommits());
128        installed = false;
129        NuxeoContainer.removeListener(cmUpdater);
130        for (ConnectionPoolMonitor storage : poolConnectionMonitors.values()) {
131            storage.uninstall();
132        }
133        coreSessionMonitor.uninstall();
134        transactionMonitor.uninstall();
135        poolConnectionMonitors.clear();
136        coreSessionMonitor = null;
137        transactionMonitor = null;
138    }
139
140    public static class ServerInstance {
141        public final MBeanServer server;
142        public final ObjectName name;
143
144        ServerInstance(MBeanServer server, ObjectName name) {
145            this.server = server;
146            this.name = name;
147        }
148    }
149
150    protected static ServerInstance bind(Object managed) {
151        return bind(managed, "default");
152    }
153
154    protected static ServerInstance bind(Class<?> itf, Object managed) {
155        return bind(itf, managed, "default");
156    }
157
158    protected static ServerInstance bind(Object managed, String name) {
159        return bind(managed.getClass().getInterfaces()[0], managed, name);
160    }
161
162    protected static ServerInstance bind(Class<?> itf, Object managed, String name) {
163        MBeanServer mbs = Framework.getLocalService(ServerLocator.class).lookupServer();
164        name = Defaults.instance.name(itf, name);
165        try {
166            ObjectInstance oi = mbs.registerMBean(managed, new ObjectName(name));
167            return new ServerInstance(mbs, oi.getObjectName());
168        } catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException
169                | MalformedObjectNameException e) {
170            throw new UnsupportedOperationException("Cannot bind " + managed + " on " + name, e);
171        }
172    }
173
174    protected static void unbind(ServerInstance instance) {
175        try {
176            instance.server.unregisterMBean(instance.name);
177        } catch (MBeanRegistrationException | InstanceNotFoundException e) {
178            LogFactory.getLog(DefaultMonitorComponent.class).error("Cannot unbind " + instance, e);
179        }
180    }
181
182}