001/*
002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Florent Guillaume
016 */
017package org.nuxeo.ecm.core.management.jtajca.internal;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import javax.management.InstanceAlreadyExistsException;
023import javax.management.InstanceNotFoundException;
024import javax.management.JMException;
025import javax.management.MBeanRegistrationException;
026import javax.management.MBeanServer;
027import javax.management.MalformedObjectNameException;
028import javax.management.NotCompliantMBeanException;
029import javax.management.ObjectInstance;
030import javax.management.ObjectName;
031import javax.security.auth.login.LoginContext;
032import javax.security.auth.login.LoginException;
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035import org.apache.geronimo.connector.outbound.AbstractConnectionManager;
036import org.nuxeo.ecm.core.api.CoreInstance;
037import org.nuxeo.ecm.core.api.CoreSession;
038import org.nuxeo.ecm.core.api.NuxeoException;
039import org.nuxeo.ecm.core.management.jtajca.CoreSessionMonitor;
040import org.nuxeo.ecm.core.management.jtajca.Defaults;
041import org.nuxeo.ecm.core.management.jtajca.ConnectionPoolMonitor;
042import org.nuxeo.ecm.core.management.jtajca.TransactionMonitor;
043import org.nuxeo.ecm.core.repository.RepositoryService;
044import org.nuxeo.runtime.api.Framework;
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.metrics.MetricsServiceImpl;
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, AbstractConnectionManager 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, AbstractConnectionManager cm) {
072            DefaultConnectionPoolMonitor monitor = (DefaultConnectionPoolMonitor) poolConnectionMonitors.get(name);
073            monitor.handleNewConnectionManager(cm);
074        }
075
076        @Override
077        public void handleConnectionManagerDispose(String name, AbstractConnectionManager 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<String, ConnectionPoolMonitor>();
091
092    // don't use activate, it would be too early
093    @Override
094    public void applicationStarted(ComponentContext context) {
095        uninstall();
096        install();
097    }
098
099    @Override
100    public int getApplicationStartedOrder() {
101        // should deploy after metrics service
102        return ((MetricsServiceImpl) Framework.getRuntime().getComponent(MetricsService.class.getName())).getApplicationStartedOrder() + 1;
103    }
104
105    @Override
106    public void deactivate(ComponentContext context) {
107        uninstall();
108        super.deactivate(context);
109    }
110
111    protected boolean installed;
112
113    protected void install() {
114        installed = true;
115
116        coreSessionMonitor = new DefaultCoreSessionMonitor();
117        coreSessionMonitor.install();
118
119        transactionMonitor = new DefaultTransactionMonitor();
120        transactionMonitor.install();
121
122        try {
123            installPoolMonitors();
124        } catch (LoginException cause) {
125            log.warn("Cannot install storage monitors", cause);
126        }
127
128    }
129
130    protected void installPoolMonitors() throws LoginException {
131        NuxeoContainer.addListener(cmUpdater);
132        NuxeoException errors = new NuxeoException("Cannot install pool monitors");
133        LoginContext loginContext = Framework.login();
134        try {
135            for (String name : Framework.getLocalService(RepositoryService.class).getRepositoryNames()) {
136                try (CoreSession session = CoreInstance.openCoreSession(name)) {;
137                } catch (NuxeoException cause) {
138                    errors.addSuppressed(cause);
139                }
140            }
141        } finally {
142            loginContext.logout();
143        }
144        if (errors.getSuppressed().length > 0) {
145            throw errors;
146        }
147    }
148
149    /**
150     * Make sure we open the repository, to initialize its connection manager.
151     */
152    protected void activateRepository(String repositoryName) {
153        try (CoreSession session = CoreInstance.openCoreSessionSystem(repositoryName)) {
154            // do nothing, just open and close
155        }
156    }
157
158    protected void uninstall() {
159        if (!installed) {
160            return;
161        }
162        installed = false;
163        NuxeoContainer.removeListener(cmUpdater);
164        for (ConnectionPoolMonitor storage : poolConnectionMonitors.values()) {
165            storage.uninstall();
166        }
167        coreSessionMonitor.uninstall();
168        transactionMonitor.uninstall();
169        poolConnectionMonitors.clear();
170        coreSessionMonitor = null;
171        transactionMonitor = null;
172    }
173
174    protected static ObjectInstance bind(Object managed) {
175        return bind(managed, "default");
176    }
177
178    protected static ObjectInstance bind(Class<?> itf, Object managed) {
179        return bind(itf, managed, "default");
180    }
181
182    protected static ObjectInstance bind(Object managed, String name) {
183        return bind(managed.getClass().getInterfaces()[0], managed, name);
184    }
185
186    protected static ObjectInstance bind(Class<?> itf, Object managed, String name) {
187        MBeanServer mbs = Framework.getLocalService(ServerLocator.class).lookupServer();
188        name = Defaults.instance.name(itf, name);
189        try {
190            return mbs.registerMBean(managed, new ObjectName(name));
191        } catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException
192                | MalformedObjectNameException e) {
193            throw new UnsupportedOperationException("Cannot bind " + managed + " on " + name, e);
194        }
195    }
196
197    protected static void unbind(ObjectInstance instance) {
198        MBeanServer mbs = Framework.getLocalService(ServerLocator.class).lookupServer();
199        try {
200            mbs.unregisterMBean(instance.getObjectName());
201        } catch (MBeanRegistrationException | InstanceNotFoundException e) {
202            throw new UnsupportedOperationException("Cannot unbind " + instance, e);
203        }
204    }
205
206}