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