Java tutorial
/******************************************************************************* * Copyright 2011 Google Inc. All Rights Reserved. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.google.gwt.eclipse.oophm.model; import com.google.gwt.eclipse.oophm.devmode.DevModeServiceClientManager; import org.eclipse.debug.core.ILaunch; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * The model for the OOPHM data. At the top level, the model consists of a list of * {@link LaunchConfiguration}. * * This class is thread-safe, except for the initializeModel() method. TODO: initializeModel() * method gone, determine impact on thread safety. */ public class WebAppDebugModel implements IModelNode { private static WebAppDebugModel INSTANCE = new WebAppDebugModel(); /** * Get the global model instance. */ public static WebAppDebugModel getInstance() { return INSTANCE; } private final int id; private final List<LaunchConfiguration> launchConfigurations = new ArrayList<LaunchConfiguration>(); private final AtomicInteger nextModelNodeId = new AtomicInteger(); private final Object privateInstanceLock = new Object(); private final List<IWebAppDebugModelListener> webAppDebugModelListeners = new ArrayList<IWebAppDebugModelListener>(); WebAppDebugModel() { // Not instantiable id = getModelNodeNextId(); } /** * Adds the launch configuration to the model, or returns the existing launch configuration. Fires * an event to all listeners on the model if the launch configuration was added. * * @param clientId optional if newLaunch is non-null * @param factory if non-null, this will be used to instantiate a {@link LaunchConfiguration} to * be added */ public LaunchConfiguration addOrReturnExistingLaunchConfiguration(ILaunch newLaunch, String clientId, ILaunchConfigurationFactory factory) { LaunchConfiguration launchConfiguration; synchronized (privateInstanceLock) { for (LaunchConfiguration lc : launchConfigurations) { if (lc.getLaunch().equals(newLaunch)) { return lc; } } String name = LaunchConfiguration.computeNamePrefix(newLaunch, clientId); if (factory != null) { launchConfiguration = factory.newLaunchConfiguration(newLaunch, name, this); } else { launchConfiguration = new LaunchConfiguration(newLaunch, name, this); } launchConfigurations.add(launchConfiguration); } // Only fire events when we're not holding any locks. Otherwise, deadlock // may happen. WebAppDebugModelEvent<LaunchConfiguration> launchedEvent = new WebAppDebugModelEvent<LaunchConfiguration>( launchConfiguration); fireLaunchConfigurationLaunched(launchedEvent); // Clean up terminated launch configurations with the same name as the added // launch configuration removeAllAssociatedTerminatedLaunchConfigsExceptMostRecent(launchConfiguration); return launchConfiguration; } /** * TODO extract possibly? * * @param launchConfiguration */ public void addLaunchConfiguration(LaunchConfiguration launchConfiguration) { synchronized (privateInstanceLock) { for (LaunchConfiguration lc : launchConfigurations) { if (lc.equals(launchConfiguration)) { return; } } launchConfigurations.add(launchConfiguration); } // Only fire events when we're not holding any locks. Otherwise, deadlock // may happen. WebAppDebugModelEvent<LaunchConfiguration> launchedEvent = new WebAppDebugModelEvent<LaunchConfiguration>( launchConfiguration); fireLaunchConfigurationLaunched(launchedEvent); // Clean up terminated launch configurations with the same name as the added // launch configuration removeAllAssociatedTerminatedLaunchConfigsExceptMostRecent(launchConfiguration); } /** * Add a listener for changes to the model. */ public void addWebAppDebugModelListener(IWebAppDebugModelListener listener) { synchronized (privateInstanceLock) { webAppDebugModelListeners.add(listener); } } @Override public List<? extends IModelNode> getChildren() { return getLaunchConfigurations(); } @Override public int getId() { return id; } /** * Returns the most-recently created launch configuration that has not been terminated as yet, or * <code>null</code> if no such launch configuration can be found. */ public LaunchConfiguration getLatestActiveLaunchConfiguration() { synchronized (privateInstanceLock) { for (int i = launchConfigurations.size() - 1; i > -1; i--) { LaunchConfiguration lc = launchConfigurations.get(i); if (!lc.isTerminated()) { return lc; } } return null; } } /** * Get the launch configurations. */ public List<LaunchConfiguration> getLaunchConfigurations() { synchronized (privateInstanceLock) { return new ArrayList<LaunchConfiguration>(launchConfigurations); } } @Override public String getName() { // The WebAppDebugModel doesn't have a name; return the empty string return ""; } @Override public String getNeedsAttentionLevel() { return null; } @Override public IModelNode getParent() { return null; } /** * Returns the terminated launch configurations that are part of the model. */ public List<LaunchConfiguration> getTerminatedLaunchConfigurations() { List<LaunchConfiguration> launchConfigurations = getLaunchConfigurations(); Iterator<LaunchConfiguration> iter = launchConfigurations.iterator(); while (iter.hasNext()) { LaunchConfiguration next = iter.next(); if (!next.isTerminated()) { iter.remove(); } } return launchConfigurations; } /** * Returns a list of the {@link IWebAppDebugModelListener} instances that are registered with the * model. */ public List<IWebAppDebugModelListener> getWebAppDebugModelListeners() { synchronized (privateInstanceLock) { return new ArrayList<IWebAppDebugModelListener>(webAppDebugModelListeners); } } @Override public boolean isTerminated() { return false; } /** * Given a {@link ILaunch} of a terminated launch, find the associated launch configuration in the * model, and mark it as terminated. Fires an event to all listeners on the * {@link WebAppDebugModel}. * * This method does nothing if no such launch configuration can be found. */ public void launchTerminated(ILaunch launch) { LaunchConfiguration launchConfiguration = null; synchronized (privateInstanceLock) { for (LaunchConfiguration lc : launchConfigurations) { if (lc.getLaunch() == launch) { launchConfiguration = lc; break; } } } if (launchConfiguration != null) { launchConfiguration.setTerminated(); } } /** * Remove the launch configuration. Fires an event for the launch configuration that was removed. * * @return true if the launch configuration was removed successfully */ public boolean removeLaunchConfiguration(LaunchConfiguration lc) { boolean wasRemoved = false; synchronized (privateInstanceLock) { // TODO: Mark launch configuration as removed, and force the removal of // all of its browser tabs and servers wasRemoved = launchConfigurations.remove(lc); } if (wasRemoved) { DevModeServiceClientManager.getInstance().removeClient(lc); // Only fire events when we're not holding any locks. Otherwise, deadlock // may happen. WebAppDebugModelEvent<LaunchConfiguration> removedEvent = new WebAppDebugModelEvent<LaunchConfiguration>( lc); fireLaunchConfigurationRemoved(removedEvent); } return wasRemoved; } /** * Removes all launches that have been terminated from the model. Fires an event for each launch * configuration that was removed. * * {@link #removeLaunchConfiguration(LaunchConfiguration)} */ public void removeTerminatedLaunchesFromModel() { List<LaunchConfiguration> terminatedLaunchConfigs = new ArrayList<LaunchConfiguration>(); synchronized (privateInstanceLock) { for (LaunchConfiguration lc : launchConfigurations) { if (lc.isTerminated()) { terminatedLaunchConfigs.add(lc); } } } for (LaunchConfiguration lc : terminatedLaunchConfigs) { removeLaunchConfiguration(lc); } } /** * Remove a listener registered to receive notifications about model changes. * * @return true if the listener was removed successfully */ public boolean removeWebAppDebugModelListener(IWebAppDebugModelListener listener) { synchronized (privateInstanceLock) { return webAppDebugModelListeners.remove(listener); } } int getModelNodeNextId() { return nextModelNodeId.getAndIncrement(); } private void fireLaunchConfigurationLaunched(WebAppDebugModelEvent<LaunchConfiguration> launchedEvent) { for (IWebAppDebugModelListener webAppDebugModelListener : getWebAppDebugModelListeners()) { webAppDebugModelListener.launchConfigurationLaunched(launchedEvent); } } private void fireLaunchConfigurationRemoved(WebAppDebugModelEvent<LaunchConfiguration> removedEvent) { for (IWebAppDebugModelListener webAppDebugModelListener : getWebAppDebugModelListeners()) { webAppDebugModelListener.launchConfigurationRemoved(removedEvent); } } private void removeAllAssociatedTerminatedLaunchConfigsExceptMostRecent( LaunchConfiguration addedLaunchConfiguration) { List<LaunchConfiguration> terminatedAssociatedLaunchConfigs = new ArrayList<LaunchConfiguration>(); synchronized (privateInstanceLock) { for (LaunchConfiguration lc : launchConfigurations) { if (lc.isTerminated() && lc.getName().equals(addedLaunchConfiguration.getName())) { terminatedAssociatedLaunchConfigs.add(lc); } } } int numTerminatedLaunchesToRemove = terminatedAssociatedLaunchConfigs.size() - 1; for (int i = 0; i < numTerminatedLaunchesToRemove; i++) { removeLaunchConfiguration(terminatedAssociatedLaunchConfigs.get(i)); } } }