/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package net.java.visualvm.modules.glassfish.datasource; import com.sun.appserv.management.DomainRoot; import com.sun.appserv.management.config.WebModuleConfig; import com.sun.appserv.management.j2ee.J2EETypes; import com.sun.appserv.management.monitor.ServerRootMonitor; import com.sun.appserv.management.monitor.WebModuleVirtualServerMonitor; import com.sun.tools.visualvm.core.datasource.DataSource; import com.sun.tools.visualvm.core.datasource.DataSourceRepository; import com.sun.tools.visualvm.core.datasource.descriptor.DataSourceDescriptor; import com.sun.tools.visualvm.core.datasupport.DataChangeEvent; import com.sun.tools.visualvm.core.datasupport.DataChangeListener; import com.sun.tools.visualvm.core.datasupport.DataRemovedListener; import com.sun.tools.visualvm.core.explorer.ExplorerExpansionListener; import com.sun.tools.visualvm.core.explorer.ExplorerSupport; import com.sun.tools.visualvm.core.scheduler.Quantum; import com.sun.tools.visualvm.core.scheduler.ScheduledTask; import com.sun.tools.visualvm.core.scheduler.Scheduler; import com.sun.tools.visualvm.core.scheduler.SchedulerTask; import com.sun.tools.visualvm.tools.jmx.JmxModel; import com.sun.tools.visualvm.tools.jmx.JmxModelFactory; import java.io.IOException; import java.lang.reflect.UndeclaredThrowableException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import net.java.visualvm.modules.glassfish.jmx.AMXUtil; import net.java.visualvm.modules.glassfish.jmx.JMXUtil; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; /** * * @author Jaroslav Bachorik */ public class GlassFishApplicationProvider implements DataChangeListener<GlassFishModel>, DataRemovedListener<GlassFishModel>, ExplorerExpansionListener { private static final GlassFishApplicationProvider INSTANCE = new GlassFishApplicationProvider(); private final Map<GlassFishModel, ScheduledTask> taskMap = new HashMap<GlassFishModel, ScheduledTask>(); private static class LazyLoadingSource extends GlassFishDataSource { private String message; private GlassFishModel parent; public LazyLoadingSource(String message, GlassFishModel parent) { this.message = message; this.parent = parent; } @Override public DataSourceDescriptor getDescriptor() { return new DataSourceDescriptor(this) { @Override public int getAutoExpansionPolicy() { return EXPAND_NEVER; } @Override public String getName() { return message; } }; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final LazyLoadingSource other = (LazyLoadingSource) obj; if (this.message != other.message && (this.message == null || !this.message.equals(other.message))) { return false; } if (this.parent != other.parent && (this.parent == null || !this.parent.equals(other.parent))) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 19 * hash + (this.message != null ? this.message.hashCode() : 0); hash = 19 * hash + (this.parent != null ? this.parent.hashCode() : 0); return hash; } } private class DiscoveryTask implements SchedulerTask { final private AtomicBoolean isProcessing = new AtomicBoolean(false); final private AtomicBoolean beenNotified = new AtomicBoolean(false); private GlassFishModel model; public DiscoveryTask(GlassFishModel model) { this.model = model; } public void onSchedule(long timeStamp) { if (!isProcessing.compareAndSet(false, true)) { return; } try { JmxModel jmx = JmxModelFactory.getJmxModelFor(model.getApplication()); if ((jmx == null || jmx.getConnectionState() == JmxModel.ConnectionState.DISCONNECTED)){ if (beenNotified.compareAndSet(false, true)) { NotifyDescriptor nd = new NotifyDescriptor.Message("Cannot establish JMX connection", NotifyDescriptor.ERROR_MESSAGE); DialogDisplayer.getDefault().notifyLater(nd); model.setVisible(false); } return; } if (jmx.getConnectionState() != JmxModel.ConnectionState.CONNECTED) { model.setVisible(true); return; } DomainRoot dr = AMXUtil.getDomainRoot(jmx); if (dr == null || !dr.getAMXReady()) { return; } String serverName = JMXUtil.getServerName(jmx); if (serverName == null) { return; } ServerRootMonitor srm = dr.getMonitoringRoot().getServerRootMonitorMap().get(serverName); Map<String, WebModuleConfig> map = dr.getDomainConfig().getWebModuleConfigMap(); Map<String, String> contextRootMap = new HashMap<String, String>(); for (Map.Entry<String, WebModuleConfig> cfgEntry : map.entrySet()) { String contextRoot = cfgEntry.getValue().getContextRoot(); if (!contextRoot.startsWith("/")) { contextRoot = "/" + contextRoot; } contextRootMap.put(contextRoot, cfgEntry.getKey()); } Set<GlassFishApplication> currentApps = new HashSet<GlassFishApplication>(); for (Map.Entry<String, WebModuleVirtualServerMonitor> virtMonitorEntry : srm.getWebModuleVirtualServerMonitorMap().entrySet()) { String objectName = JMXUtil.getObjectName(J2EETypes.WEB_MODULE, virtMonitorEntry.getKey(), jmx); String moduleName = JMXUtil.getWebModuleName(objectName, jmx, contextRootMap); String appName = JMXUtil.getJ2EEAppName(objectName); if (moduleName == null || moduleName.length() == 0) { continue; } GlassFishWebModule webModule = new GlassFishWebModule(appName != null ? (moduleName + " (in " + appName + ")") : moduleName, objectName, virtMonitorEntry.getValue(), model); currentApps.add(webModule); } Set<GlassFishDataSource> toRemoveApps = new HashSet<GlassFishDataSource>(model.getRepository().getDataSources(GlassFishDataSource.class)); Set<GlassFishDataSource> toAdd = new HashSet<GlassFishDataSource>(currentApps); toRemoveApps.removeAll(currentApps); toAdd.removeAll(model.getRepository().getDataSources()); Set<LazyLoadingSource> lazy = model.getRepository().getDataSources(LazyLoadingSource.class); Set<GlassFishDataSource> toRemove = new HashSet<GlassFishDataSource>(toRemoveApps); toRemove.addAll(lazy); if (currentApps.size() == 0) { LazyLoadingSource unavailable = new LazyLoadingSource("Unavailable", model); toAdd.add(unavailable); toRemove.remove(unavailable); } toAdd.removeAll(lazy); if (toAdd.size() > 0 || toRemove.size() > 0) { model.getRepository().addDataSources(toAdd); model.getRepository().removeDataSources(toRemove); // model.getRepository().updateDataSources(toAdd, toRemove); } } catch (UndeclaredThrowableException e) { // this is caused by disappearing of the underlying JMX connection // just ignore it } finally { isProcessing.set(false); } } } public void dataChanged(DataChangeEvent<GlassFishModel> event) { if (event.getAdded().isEmpty() && event.getRemoved().isEmpty()) { addModels(event.getCurrent()); } else { addModels(event.getAdded()); removeModels(event.getRemoved()); } } private void addModels(Set<GlassFishModel> models) { for (GlassFishModel model : models) { GlassFishDataSource lazyDS = new LazyLoadingSource("Please wait", model); model.getRepository().addDataSource(lazyDS); ScheduledTask task = Scheduler.sharedInstance().schedule(new DiscoveryTask(model), Quantum.SUSPENDED); taskMap.put(model, task); } } private void removeModels(Set<GlassFishModel> models) { for (GlassFishModel model : models) { // removing the reference to the ScheduledTask practically unschedules the task Scheduler.sharedInstance().unschedule(taskMap.remove(model)); Set<GlassFishApplication> roots = model.getRepository().getDataSources(GlassFishApplication.class); model.getRepository().removeDataSources(roots); } } public void dataRemoved(GlassFishModel model) { // removing the reference to the ScheduledTask practically unschedules the task Scheduler.sharedInstance().unschedule(taskMap.remove(model)); Set<GlassFishApplication> roots = model.getRepository().getDataSources(GlassFishApplication.class); model.getRepository().removeDataSources(roots); } public static void initialize() { DataSourceRepository.sharedInstance().addDataChangeListener(INSTANCE, GlassFishModel.class); ExplorerSupport.sharedInstance().addExpansionListener(INSTANCE); } public static void shutdown() { DataSourceRepository.sharedInstance().removeDataChangeListener(INSTANCE); ExplorerSupport.sharedInstance().removeExpansionListener(INSTANCE); } public void dataSourceCollapsed(DataSource source) { // do nothing } public void dataSourceExpanded(DataSource source) { if (source instanceof GlassFishModel) { if (taskMap.containsKey(source)) { taskMap.get(source).setInterval(Quantum.seconds(3)); } } } }