/** * This file Copyright (c) 2005-2008 Aptana, Inc. This program is * dual-licensed under both the Aptana Public License and the GNU General * Public license. You may elect to use one or the other of these licenses. * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by whichever of * the GPL or APL you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or modify this * program under the terms of the GNU General Public License, * Version 3, as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain other free and open source software ("FOSS") code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * 2. For the Aptana Public License (APL), this program and the * accompanying materials are made available under the terms of the APL * v1.0 which accompanies this distribution, and is available at * http://www.aptana.com/legal/apl/. * * You may view the GPL, Aptana's exception and additional terms, and the * APL in the file titled license.html at the root of the corresponding * plugin containing this source file. * * Any modifications to this file must keep this entire header intact. */ package com.aptana.ide.server.core.impl.servers; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IStatusHandler; import com.aptana.ide.core.IdeLog; import com.aptana.ide.core.StringUtils; import com.aptana.ide.server.ServerCore; import com.aptana.ide.server.core.IAbstractConfiguration; import com.aptana.ide.server.core.IModule; import com.aptana.ide.server.core.IModuleType; import com.aptana.ide.server.core.IPublishOperation; import com.aptana.ide.server.core.IServer; import com.aptana.ide.server.core.IServerLocator; import com.aptana.ide.server.core.IServerManager; import com.aptana.ide.server.core.IServerManagerListener; import com.aptana.ide.server.core.IServerType; import com.aptana.ide.server.core.ServerManagerEvent; import com.aptana.ide.server.core.impl.Configuration; import com.aptana.ide.server.core.impl.PreferencesConfiguration; import com.aptana.ide.server.core.impl.RegistryLazyObject; import com.aptana.ide.server.core.impl.RegistryObjectCollection; import com.aptana.ide.server.core.model.IServerProviderDelegate; /** * @author Pavel Petrochenko */ public final class ServerManager implements IServerManager { private static final int EXCEPTION_WHILE_LOADING_SERVERS_STATUS_CODE = 333; private static final String SERVER_COUNT = "serverCount"; //$NON-NLS-1$ private static final String KEY_SERVERS = "servers"; //$NON-NLS-1$ static ServerManager instance; private ArrayList<IServerManagerListener> listeners = new ArrayList<IServerManagerListener>(); private ArrayList<IServer> servers = new ArrayList<IServer>(); private ArrayList<IServer> externalservers = new ArrayList<IServer>(); private HashMap<IServer, IServerProviderDelegate> fSrvToProvider = new HashMap<IServer, IServerProviderDelegate>(); private ArrayList<RegistryLazyObject> serverTypes = new ArrayList<RegistryLazyObject>(); private ArrayList<RegistryLazyObject> moduleTypes = new ArrayList<RegistryLazyObject>(); private ArrayList<RegistryLazyObject> serverLocators = new ArrayList<RegistryLazyObject>(); private ArrayList<RegistryLazyObject> publishOperations = new ArrayList<RegistryLazyObject>(); static PreferencesConfiguration config = new PreferencesConfiguration(ServerCore.getDefault() .getPluginPreferences(), "serverManager"); //$NON-NLS-1$ /** * */ private ServerManager() { } /** * loads server info from preferences */ void load() { serverTypes.addAll(Arrays.asList(ServerTypeRegistry.getServerTypeRegistry().getAll())); moduleTypes.addAll(Arrays.asList(ServerTypeRegistry.getServerTypeRegistry().getAll())); serverLocators.addAll(Arrays.asList(ServerTypeRegistry.getServerTypeRegistry().getAll())); publishOperations.addAll(Arrays.asList(ServerTypeRegistry.getServerTypeRegistry().getAll())); loadExternals(); loadServers(); } private void loadExternals() { RegistryLazyObject[] all = new RegistryObjectCollection("com.aptana.ide.server.serverProvider") { //$NON-NLS-1$ @Override protected RegistryLazyObject createObject(IConfigurationElement configurationElement) { return new ServerProvider(configurationElement); } }.getAll(); List<RegistryLazyObject> objects = new ArrayList<RegistryLazyObject>(); for (RegistryLazyObject o : all) { objects.add(o); } Collections.sort(objects, new Comparator<RegistryLazyObject>() { public int compare(RegistryLazyObject o1, RegistryLazyObject o2) { return o1.getId().compareTo(o2.getId()); } }); all = objects.toArray(new RegistryLazyObject[0]); for (RegistryLazyObject o : all) { try { final IServerProviderDelegate pd = ((ServerProvider) o).getDelegate(); if (pd != null) { pd.addServerChangeListener(new IServerManagerListener() { public void serversChanged(ServerManagerEvent event) { IServer server = event.getServer(); if (event.getKind() == ServerManagerEvent.KIND_ADDED) { externalservers.add(server); fSrvToProvider.put(server, pd); } if (event.getKind() == ServerManagerEvent.KIND_REMOVED) { externalservers.remove(server); fSrvToProvider.remove(server); } ServerManager.this.fireChange(event); } }); IServer[] servers2 = pd.getServers(); for (IServer s : servers2) { this.externalservers.add(s); fSrvToProvider.put(s, pd); } } } catch (Throwable e) { handleError(e, o); } } } /** * reloads server manager */ public void reload() { servers.clear(); loadServers(); } private void loadServers() { String[] stringArrayAttribute = config.getStringArrayAttribute(KEY_SERVERS); for (int a = 0; a < stringArrayAttribute.length; a++) { try { IAbstractConfiguration subConfiguration = config.getSubConfiguration(stringArrayAttribute[a]); try { String stringAttribute = subConfiguration.getStringAttribute(IServer.KEY_TYPE); IServerType object = (IServerType) ServerTypeRegistry.getServerTypeRegistry().getObject( stringAttribute); if (object != null) { IServer create = object.create(subConfiguration); servers.add(create); } else { IdeLog.logError(ServerCore.getDefault(), StringUtils.format( "Server with id {0} not found", stringAttribute)); //$NON-NLS-1$ } } catch (Throwable e) { internalHandleError(e, subConfiguration.getStringAttribute(IServer.KEY_NAME)); } } catch (Throwable e) { internalHandleError(e, "Server Storage"); //$NON-NLS-1$ } } } private void handleError(Throwable e, RegistryLazyObject o) { internalHandleError(e, o.getName()); } private void internalHandleError(Throwable e, String name) { if (e instanceof CoreException) { CoreException ee = (CoreException) e; IStatusHandler statusHandler = DebugPlugin.getDefault().getStatusHandler(ee.getStatus()); if (statusHandler != null) { try { statusHandler.handleStatus(ee.getStatus(), name); } catch (Throwable e1) { IdeLog.logError(ServerCore.getDefault(), e.getMessage(), e1); } } } Status ee = new Status(IStatus.ERROR, ServerCore.PLUGIN_ID, EXCEPTION_WHILE_LOADING_SERVERS_STATUS_CODE, "Exception while loading servers", e); //$NON-NLS-1$ IStatusHandler statusHandler = DebugPlugin.getDefault().getStatusHandler(ee); try { statusHandler.handleStatus(ee, name); } catch (Throwable e1) { IdeLog.logError(ServerCore.getDefault(), e.getMessage(), e1); } IdeLog.logError(ServerCore.getDefault(), e.getMessage(), e); } /** * stores servers * * @param allInfo */ void storeServers(boolean allInfo) { try { String[] ids = new String[servers.size()]; List<String> savedIds = new ArrayList<String>(); for (int a = 0; a < ids.length; a++) { IServer server = ((IServer) servers.get(a)); if (!server.isTransient()) { ids[a] = server.getId(); savedIds.add(ids[a]); if (allInfo) { IAbstractConfiguration configuration = getConfiguration(server); server.storeConfiguration(configuration); configuration.setStringAttribute(IServer.KEY_TYPE, server.getServerType().getId()); } } } config.setStringArrayAttribute(KEY_SERVERS, savedIds.toArray(new String[0])); ServerCore.getDefault().savePluginPreferences(); } catch (Throwable e) { IdeLog.log(ServerCore.getDefault(), 0, "exception while storing servers", e); //$NON-NLS-1$ } } /** * @param srv * @return returns new configuration for the server */ IAbstractConfiguration getConfiguration(IServer srv) { IAbstractConfiguration subConfiguration = config.getSubConfiguration(srv.getId()); return subConfiguration; } /** * callback on server change * * @param server */ void serverChanged(IServer server) { if (server != null) { if (!servers.contains(server)) { fireChange(new ServerManagerEvent(server, ServerManagerEvent.KIND_CHANGED)); return; } IAbstractConfiguration configuration = getConfiguration(server); server.storeConfiguration(configuration); ServerCore default1 = ServerCore.getDefault(); if (default1 != null) { default1.savePluginPreferences(); } fireChange(new ServerManagerEvent(server, ServerManagerEvent.KIND_CHANGED)); } } /** * @param server */ public void addServer(IServer server) { if (server == null) { throw new IllegalArgumentException("server should not be null"); //$NON-NLS-1$ } if (servers.contains(server)) { throw new IllegalArgumentException("servers should be unique"); //$NON-NLS-1$ } servers.add(server); IAbstractConfiguration configuration = getConfiguration(server); server.storeConfiguration(configuration); configuration.setStringAttribute(IServer.KEY_TYPE, server.getServerType().getId()); storeServers(false); fireChange(new ServerManagerEvent(server, ServerManagerEvent.KIND_ADDED)); } /** * @param server */ public void removeServer(IServer server) { IServerProviderDelegate serverProviderDelegate = fSrvToProvider.get(server); if (serverProviderDelegate != null) { serverProviderDelegate.removeServer(server); } IAbstractConfiguration configuration = getConfiguration(server); String[] propertyNames = configuration.propertyNames(); for (int a = 0; a < propertyNames.length; a++) { configuration.removeAttribute(propertyNames[a]); } boolean remove = servers.remove(server); storeServers(false); if (remove) { fireChange(new ServerManagerEvent(server, ServerManagerEvent.KIND_REMOVED)); } } /** * @see com.aptana.ide.server.core.IServerManager#addServerManagerListener(com.aptana.ide.server.core.IServerManagerListener) */ public synchronized void addServerManagerListener(IServerManagerListener listener) { if (listener == null) { throw new IllegalArgumentException("listener should not be null"); //$NON-NLS-1$ } if (listeners.contains(listener)) { return; } listeners.add(listener); } /** * @see com.aptana.ide.server.core.IServerManager#getProjects(com.aptana.ide.server.core.IServer) */ public synchronized IProject[] getProjects(IServer server) { HashSet<IProject> resultSet = new HashSet<IProject>(); IModule[] modules = server.getModules(); for (int b = 0; b < modules.length; b++) { resultSet.add(modules[b].getProject()); } IProject[] result = new IProject[resultSet.size()]; resultSet.toArray(result); return result; } /** * @see com.aptana.ide.server.core.IServerManager#getServerTypes() */ public synchronized IServerType[] getServerTypes() { IServerType[] result = new IServerType[serverTypes.size()]; serverTypes.toArray(result); return result; } /** * @see com.aptana.ide.server.core.IServerManager#getServers() */ public synchronized IServer[] getServers() { ArrayList<IServer> combined = new ArrayList<IServer>(); combined.addAll(servers); combined.addAll(externalservers); IServer[] result = new IServer[combined.size()]; combined.toArray(result); return result; } /** * @see IServerManager#findServer(String) */ public synchronized IServer findServer(String id) { IServer[] servers = ServerCore.getServerManager().getServers(); for (int i = 0; i < servers.length; i++) { IServer server = servers[i]; if (server.getId().equals(id)) { return server; } } return null; } /** * @see com.aptana.ide.server.core.IServerManager#getServers(org.eclipse.core.resources.IProject) */ public synchronized IServer[] getServers(IProject project) { ArrayList<IServer> resultList = new ArrayList<IServer>(); l2: for (int a = 0; a < servers.size(); a++) { IServer srv = (IServer) servers.get(a); IModule[] modules = srv.getModules(); for (int b = 0; b < modules.length; b++) { if (modules[b].getProject().equals(project)) { resultList.add(srv); continue l2; } } } IServer[] result = new IServer[resultList.size()]; resultList.toArray(result); return result; } /** * fires event to listeners * * @param event */ protected void fireChange(ServerManagerEvent event) { for (int a = 0; a < listeners.size(); a++) { IServerManagerListener listener = (IServerManagerListener) listeners.get(a); try { listener.serversChanged(event); } catch (Exception e) { IdeLog.logError(ServerCore.getDefault(), StringUtils.format( "error while notifying listener {0}", listener.toString()), e); //$NON-NLS-1$ } } } /** * @see com.aptana.ide.server.core.IServerManager#removeServerManagerListener(com.aptana.ide.server.core.IServerManagerListener) */ public synchronized void removeServerManagerListener(IServerManagerListener listener) { listeners.remove(listener); } /** * @see com.aptana.ide.server.core.IServerManager#getModules(org.eclipse.core.resources.IProject) */ public synchronized IModule[] getModules(IProject project) { ArrayList<IModule> resultList = new ArrayList<IModule>(); for (int a = 0; a < servers.size(); a++) { IServer srv = (IServer) servers.get(a); IModule[] modules = srv.getModules(); for (int b = 0; b < modules.length; b++) { if (modules[b].getProject().equals(project)) { resultList.add(modules[b]); } } } IModule[] result = new IModule[resultList.size()]; resultList.toArray(result); return result; } /** * @see com.aptana.ide.server.core.IServerManager#exists(com.aptana.ide.server.core.IServer) */ public synchronized boolean exists(IServer server) { return servers.contains(server); } /** * @see com.aptana.ide.server.core.IServerManager#getModuleTypes() */ public IModuleType[] getModuleTypes() { IModuleType[] result = new IModuleType[moduleTypes.size()]; moduleTypes.toArray(result); return result; } /** * @see com.aptana.ide.server.core.IServerManager#getServerLocators() */ public synchronized IServerLocator[] getServerLocators() { IServerLocator[] result = new IServerLocator[serverLocators.size()]; serverLocators.toArray(result); return result; } /** * @see com.aptana.ide.server.core.IServerManager#getPublishOperation(com.aptana.ide.server.core.IServerType, * com.aptana.ide.server.core.IModuleType, java.lang.String) */ public synchronized IPublishOperation getPublishOperation(IServerType server, IModuleType type, String id) { for (int a = 0; a < publishOperations.size(); a++) { IPublishOperation op = (IPublishOperation) publishOperations.get(a); if (op.supports(server, type)) { return op; } } return null; } /** * @see com.aptana.ide.server.core.IServerManager#getPublishOperations() */ public synchronized IPublishOperation[] getPublishOperations() { IPublishOperation[] result = new IPublishOperation[publishOperations.size()]; publishOperations.toArray(result); return result; } /** * @return - free id */ public static String getFreeId() { int intAttribute = config.getIntAttribute(SERVER_COUNT); DecimalFormat dFormat = new DecimalFormat("000000000"); // force leading zeros as padding //$NON-NLS-1$ String format = StringUtils.format("server{0}", dFormat.format(intAttribute)); //$NON-NLS-1$ config.setIntAttribute(SERVER_COUNT, ++intAttribute); ServerCore.getDefault().savePluginPreferences(); return format; } /** * @see com.aptana.ide.server.core.IServerManager#addServer(com.aptana.ide.server.core.IAbstractConfiguration) */ public IServer addServer(IAbstractConfiguration configuration) throws CoreException { try { String stringAttribute = configuration.getStringAttribute(IServer.KEY_TYPE); IServerType tpe = (IServerType) ServerTypeRegistry.getServerTypeRegistry().getObject(stringAttribute); IServer create = tpe.create(configuration); addServer(create); return create; } catch (CoreException e) { throw e; } catch (Throwable e) { throw new CoreException(new Status(IStatus.ERROR, ServerCore.PLUGIN_ID, IStatus.ERROR, "exception while adding server", e)); //$NON-NLS-1$ } } /** * removes all servers */ public void clearAll() { servers.clear(); config.setStringArrayAttribute(KEY_SERVERS, new String[] {}); } /** * @see com.aptana.ide.server.core.IServerManager#getInitialServerConfiguration(java.lang.String) */ public IAbstractConfiguration getInitialServerConfiguration(String serverTypeId) { Configuration config = new Configuration(); InitializerLazyObject initializer = InitializerRegistry.getInstance().getInitializer(serverTypeId); config.setStringAttribute(IServer.KEY_ID, ServerManager.getFreeId()); config.setStringAttribute(IServer.KEY_TYPE, serverTypeId); if (initializer != null) { initializer.initializeConfiguration(config); } return config; } /** * @see com.aptana.ide.server.core.IServerManager#getServerType(java.lang.String) */ public IServerType getServerType(String id) { return (IServerType) ServerTypeRegistry.getServerTypeRegistry().getObject(id); } /** * @return instance */ public static ServerManager getInstance() { if (instance == null) { instance = new ServerManager(); instance.load(); } return instance; } /** * Finds a free port in this range, inclusively on both ends of the range. * * @param portRange * @return - port number or -1 for no available in this range */ public static int findFreePort(int[] portRange) { return findFreePort(portRange[0], portRange[1]); } /** * Finds a free port in this range, inclusively on both ends of the range. * * @param startRange * @param endRange * @return - port number or -1 for no available in this range */ public static int findFreePort(int startRange, int endRange) { return findFreePort(startRange, endRange, null); } /** * Finds a free port in this range, inclusively on both ends of the range. Also allow optional array of ports to * avoid checking in the range * * @param startRange * @param endRange * @param excluding - * ports to exclude in range * @return - port number or -1 for no available in this range */ public static int findFreePort(int startRange, int endRange, int[] excluding) { int port = -1; ServerSocket socket = null; for (int i = startRange; i <= endRange; i++) { boolean valid = true; if (excluding != null && excluding.length > 0) { for (int e = 0; e < excluding.length; e++) { if (i == excluding[e]) { valid = false; break; } } } if (valid) { try { socket = new ServerSocket(i); socket.close(); socket = new ServerSocket(); socket.setReuseAddress(false); socket.bind(new InetSocketAddress("127.0.0.1", i)); //$NON-NLS-1$ socket.close(); port = i; break; } catch (IOException e) { // Proceed to next port if opening the server socket or closing the server socket fails } } } return port; } }