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