/******************************************************************************* * Copyright (c) 2017 Alex Xu and others. * 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 * * Contributors: * Alex Xu - initial API and implementation *******************************************************************************/ package org.eclipse.php.internal.server.core.builtin; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.commons.lang3.StringUtils; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IDebugEventSetListener; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IProcess; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.IProjectFragment; import org.eclipse.dltk.core.IScriptProject; import org.eclipse.osgi.util.NLS; import org.eclipse.php.debug.core.debugger.parameters.IDebugParametersKeys; import org.eclipse.php.internal.debug.core.IPHPDebugConstants; import org.eclipse.php.internal.debug.core.PHPDebugPlugin; import org.eclipse.php.internal.debug.core.launching.DebugSessionIdGenerator; import org.eclipse.php.internal.debug.core.pathmapper.PathEntry.Type; import org.eclipse.php.internal.debug.core.pathmapper.PathMapper; import org.eclipse.php.internal.debug.core.pathmapper.PathMapper.Mapping; import org.eclipse.php.internal.debug.core.pathmapper.PathMapper.Mapping.MappingSource; import org.eclipse.php.internal.debug.core.pathmapper.PathMapperRegistry; import org.eclipse.php.internal.debug.core.pathmapper.VirtualPath; import org.eclipse.php.internal.debug.core.preferences.PHPProjectPreferences; import org.eclipse.php.internal.debug.core.preferences.PHPexeItem; import org.eclipse.php.internal.debug.core.zend.communication.DebuggerCommunicationDaemon; import org.eclipse.php.internal.debug.core.zend.debugger.PHPSessionLaunchMapper; import org.eclipse.php.internal.server.core.Server; import org.eclipse.php.internal.server.core.manager.ServersManager; import org.eclipse.wst.server.core.IModule; import org.eclipse.wst.server.core.IServer; import org.eclipse.wst.server.core.ServerPort; import org.eclipse.wst.server.core.internal.IModulePublishHelper; import org.eclipse.wst.server.core.model.IModuleResource; import org.eclipse.wst.server.core.model.IModuleResourceDelta; import org.eclipse.wst.server.core.model.ServerBehaviourDelegate; import org.eclipse.wst.server.core.util.PublishHelper; import org.eclipse.wst.server.core.util.SocketUtil; @SuppressWarnings("restriction") public class PHPServerBehaviour extends ServerBehaviourDelegate implements IPHPServerBehaviour, IModulePublishHelper { private static final String ATTR_STOP = "stop-server"; //$NON-NLS-1$ protected transient PingThread ping = null; protected transient IDebugEventSetListener processListener; private ILaunch fLaunch; private int fSessionId; private IPHPServerRunner fPHPServerRunner; public PHPServerBehaviour() { } @Override public void stop(boolean force) { terminate(); } @Override public void setupLaunchConfiguration(ILaunchConfigurationWorkingCopy workingCopy, IProgressMonitor monitor) throws CoreException { PHPexeItem item = getPHPRuntime().getExecutableInstall(); workingCopy.setAttribute(IPHPDebugConstants.ATTR_EXECUTABLE_LOCATION, item.getExecutable().getAbsolutePath()); if (item.getINILocation() != null) { workingCopy.setAttribute(IPHPDebugConstants.ATTR_INI_LOCATION, item.getINILocation().getAbsolutePath()); } else { workingCopy.setAttribute(IPHPDebugConstants.ATTR_INI_LOCATION, StringUtils.EMPTY); } workingCopy.setAttribute(Server.NAME, getServer().getName()); workingCopy.setAttribute(Server.BASE_URL, getPHPServer().getRootUrl().toString()); workingCopy.setAttribute(IDebugParametersKeys.TRANSFER_ENCODING, PHPProjectPreferences.getTransferEncoding(null)); workingCopy.setAttribute(IDebugParametersKeys.OUTPUT_ENCODING, PHPProjectPreferences.getOutputEncoding(null)); workingCopy.setAttribute(IDebugParametersKeys.PHP_DEBUG_TYPE, IDebugParametersKeys.PHP_WEB_PAGE_DEBUG); } @Override protected IModuleResource[] getResources(IModule[] module) { return super.getResources(module); } @Override protected IModuleResourceDelta[] getPublishedResourceDelta(IModule[] module) { return super.getPublishedResourceDelta(module); } /** * Cleans the entire work directory for this server. This involves deleting * all subdirectories of the server's work directory. * * @param monitor * a progress monitor * @return results of the clean operation * @throws CoreException */ public IStatus cleanServerWorkDir(IProgressMonitor monitor) throws CoreException { IStatus result; IPath basePath = getRuntimeBaseDirectory(); IPath workPath = getPHPServerConfiguration().getServerWorkDirectory(basePath); if (workPath != null) { File workDir = workPath.toFile(); result = Status.OK_STATUS; if (workDir.exists() && workDir.isDirectory()) { // Delete subdirectories of the server's work dir File[] files = workDir.listFiles(); if (files != null && files.length > 0) { MultiStatus ms = new MultiStatus(PHPServerPlugin.PLUGIN_ID, 0, "Problem occurred deleting work directory for module.", null); //$NON-NLS-1$ int size = files.length; monitor = ProgressUtil.getMonitorFor(monitor); monitor.beginTask( NLS.bind("Cleaning Server Work Directory", new String[] { workDir.getAbsolutePath() }), //$NON-NLS-1$ size * 10); for (int i = 0; i < size; i++) { File current = files[i]; if (current.isDirectory()) { IStatus[] results = PublishHelper.deleteDirectory(current, ProgressUtil.getSubMonitorFor(monitor, 10)); if (results != null && results.length > 0) { for (int j = 0; j < results.length; j++) { ms.add(results[j]); } } } } monitor.done(); result = ms; } } } else { result = new Status(IStatus.ERROR, PHPServerPlugin.PLUGIN_ID, 0, "Could not determine work directory for module", null); //$NON-NLS-1$ } return result; } protected void addProcessListener(final IProcess newProcess) { if (processListener != null || newProcess == null) return; processListener = new IDebugEventSetListener() { @Override public void handleDebugEvents(DebugEvent[] events) { if (events != null) { int size = events.length; for (int i = 0; i < size; i++) { if (newProcess != null && newProcess.equals(events[i].getSource()) && events[i].getKind() == DebugEvent.TERMINATE) { stopImpl(); } } } } }; DebugPlugin.getDefault().addDebugEventListener(processListener); } protected void setServerStarted() { setServerState(IServer.STATE_STARTED); } protected void stopImpl() { if (ping != null) { ping.stop(); ping = null; } if (processListener != null) { DebugPlugin.getDefault().removeDebugEventListener(processListener); processListener = null; } if (fPHPServerRunner != null) { fPHPServerRunner.stop(); fPHPServerRunner = null; } setServerState(IServer.STATE_STOPPED); } /** * Setup for starting the server. * * @param launch * ILaunch * @param launchMode * String * @param monitor * IProgressMonitor * @throws CoreException * if anything goes wrong */ @Override public void setupLaunch(ILaunch launch, String launchMode, IProgressMonitor monitor) throws CoreException { if ("true".equals(launch.getLaunchConfiguration().getAttribute(ATTR_STOP, "false"))) { //$NON-NLS-1$ //$NON-NLS-2$ launch.terminate(); stopImpl(); return; } Server server = ServersManager.getServer(getServer().getName()); PathMapper pathMapper = PathMapperRegistry.getByServer(server); pathMapper.setMapping(getPHPServerConfiguration().getPathMappings()); fLaunch = launch; IStatus status = getPHPRuntime().validate(); if (status != null && status.getSeverity() == IStatus.ERROR) throw new CoreException(status); // // // setRestartNeeded(false); PHPServerConfiguration configuration = getPHPServerConfiguration(); // check that ports are free Iterator<ServerPort> iterator = configuration.getServerPorts().iterator(); List<ServerPort> usedPorts = new ArrayList<ServerPort>(); while (iterator.hasNext()) { ServerPort sp = iterator.next(); if (sp.getPort() < 0) throw new CoreException( new Status(IStatus.ERROR, PHPServerPlugin.PLUGIN_ID, 0, Messages.errorPortInvalid, null)); if (SocketUtil.isPortInUse(sp.getPort(), 5)) { usedPorts.add(sp); } } if (usedPorts.size() == 1) { ServerPort port = usedPorts.get(0); throw new CoreException(new Status(IStatus.ERROR, PHPServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorPortInUse, new String[] { port.getPort() + "", getServer().getName() }), //$NON-NLS-1$ null)); } else if (usedPorts.size() > 1) { String portStr = ""; //$NON-NLS-1$ iterator = usedPorts.iterator(); boolean first = true; while (iterator.hasNext()) { if (!first) portStr += ", "; //$NON-NLS-1$ first = false; ServerPort sp = (ServerPort) iterator.next(); portStr += "" + sp.getPort(); //$NON-NLS-1$ } throw new CoreException(new Status(IStatus.ERROR, PHPServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorPortsInUse, new String[] { portStr, getServer().getName() }), null)); } setServerRestartState(false); setServerState(IServer.STATE_STARTING); setMode(launchMode); if (ILaunchManager.DEBUG_MODE.equals(launchMode)) { fSessionId = DebugSessionIdGenerator.generateSessionID(); int debugPort = PHPDebugPlugin.getDebugPort(DebuggerCommunicationDaemon.ZEND_DEBUGGER_ID); PHPSessionLaunchMapper.put(fSessionId, launch); launch.setAttribute(IDebugParametersKeys.SESSION_ID, String.valueOf(fSessionId)); launch.setAttribute(IDebugParametersKeys.BUILTIN_SERVER_DEBUGGER, "true"); //$NON-NLS-1$ launch.setAttribute(IDebugParametersKeys.PORT, String.valueOf(debugPort)); } // ping server to check for startup try { String url = "http://" + getServer().getHost(); //$NON-NLS-1$ int port = configuration.getMainPort().getPort(); if (port != 80) url += ":" + port; //$NON-NLS-1$ ping = new PingThread(getServer(), url, -1, this); } catch (Exception e) { Trace.trace(Trace.SEVERE, "Can't ping for PHP Server startup."); //$NON-NLS-1$ } } /** * Terminates the server. */ protected void terminate() { if (getServer().getServerState() == IServer.STATE_STOPPED) return; try { setServerState(IServer.STATE_STOPPING); if (Trace.isTraceEnabled()) Trace.trace(Trace.FINER, "Killing the PHP Server process"); //$NON-NLS-1$ if (fLaunch != null) { fLaunch.terminate(); stopImpl(); } } catch (Exception e) { Trace.trace(Trace.SEVERE, "Error killing the process", e); //$NON-NLS-1$ } } /** * Returns the runtime base path for relative paths in the server * configuration. * * @return the base path */ public IPath getRuntimeBaseDirectory() { return getPHPServer().getRuntimeBaseDirectory(); } public PHPServer getPHPServer() { return (PHPServer) getServer().loadAdapter(PHPServer.class, null); } public PHPServerConfiguration getPHPServerConfiguration() throws CoreException { return getPHPServer().getPHPServerConfiguration(); } @Override public IPath getTempDirectory() { return super.getTempDirectory(false); } @Override public IPath getPublishDirectory(IModule[] module) { if (module == null || module.length != 1) return null; return getModuleDeployDirectory(module[0]); } /** * Gets the directory to which to deploy a module's web application. * * @param module * a module * @return full path to deployment directory for the module */ public IPath getModuleDeployDirectory(IModule module) { return getServerDeployDirectory().append(module.getName()); } public void setModulePublishState2(IModule[] module, int state) { setModulePublishState(module, state); } public Properties loadModulePublishLocations() { Properties p = new Properties(); IPath path = getTempDirectory().append("publish.txt"); //$NON-NLS-1$ FileInputStream fin = null; try { fin = new FileInputStream(path.toFile()); p.load(fin); } catch (Exception e) { // ignore } finally { try { fin.close(); } catch (Exception ex) { // ignore } } return p; } public void saveModulePublishLocations(Properties p) { IPath path = getTempDirectory().append("publish.txt"); //$NON-NLS-1$ FileOutputStream fout = null; try { fout = new FileOutputStream(path.toFile()); p.store(fout, "PHP Server publish data"); //$NON-NLS-1$ } catch (Exception e) { // ignore } finally { try { fout.close(); } catch (Exception ex) { // ignore } } } public void setPHPServerPublishState(int state) { setServerPublishState(state); } public void setPHPServerRestartState(boolean state) { setServerRestartState(state); } @Override protected void publishServer(int kind, IProgressMonitor monitor) throws CoreException { if (getServer().getRuntime() == null) return; IPath installDir = getServer().getRuntime().getLocation(); IPath confDir = null; confDir = installDir; IStatus status = PHPServerHelper.createDeploymentDirectory(getServerDeployDirectory()); if (status != null && !status.isOK()) throw new CoreException(status); monitor = ProgressUtil.getMonitorFor(monitor); monitor.beginTask(Messages.publishServerTask, 600); status = getPHPServerConfiguration().cleanupServer(confDir, installDir, false, ProgressUtil.getSubMonitorFor(monitor, 100)); if (status != null && !status.isOK()) throw new CoreException(status); status = getPHPServerConfiguration().backupAndPublish(confDir, false, ProgressUtil.getSubMonitorFor(monitor, 400)); if (status != null && !status.isOK()) throw new CoreException(status); status = getPHPServerConfiguration().localizeConfiguration(confDir, getServerDeployDirectory(), getPHPServer(), ProgressUtil.getSubMonitorFor(monitor, 100)); if (status != null && !status.isOK()) throw new CoreException(status); monitor.done(); setServerPublishState(IServer.PUBLISH_STATE_NONE); } /* * Publishes the given module to the server. */ @Override protected void publishModule(int kind, int deltaKind, IModule[] moduleTree, IProgressMonitor monitor) throws CoreException { Properties p = loadModulePublishLocations(); PublishHelper helper = new PublishHelper(getRuntimeBaseDirectory().append("temp").toFile()); //$NON-NLS-1$ // If parent web module if (moduleTree.length == 1) { publishDir(deltaKind, p, moduleTree, helper, monitor); } // // Else a child module // else { // // Try to determine the URI for the child module // IWebModule webModule = (IWebModule) // moduleTree[0].loadAdapter(IWebModule.class, monitor); // String childURI = null; // if (webModule != null) { // childURI = webModule.getURI(moduleTree[1]); // } // // Try to determine if child is binary // IJ2EEModule childModule = (IJ2EEModule) // moduleTree[1].loadAdapter(IJ2EEModule.class, monitor); // boolean isBinary = false; // if (childModule != null) { // isBinary = childModule.isBinary(); // } // // if (isBinary) { // publishArchiveModule(childURI, kind, deltaKind, p, moduleTree, // helper, monitor); // } else { // publishJar(childURI, kind, deltaKind, p, moduleTree, helper, // monitor); // } // } setModulePublishState(moduleTree, IServer.PUBLISH_STATE_NONE); saveModulePublishLocations(p); } /** * Publish a web module. * * @param deltaKind * @param p * @param module * @param monitor * @throws CoreException */ private void publishDir(int deltaKind, Properties p, IModule module[], PublishHelper helper, IProgressMonitor monitor) throws CoreException { List<IStatus> status = new ArrayList<IStatus>(); // Remove if requested or if previously published and are now serving // without publishing if (deltaKind == REMOVED) { String publishPath = (String) p.get(module[0].getId()); if (publishPath != null) { try { File f = new File(publishPath); if (f.exists()) { IStatus[] stat = PublishHelper.deleteDirectory(f, monitor); PublishOperation2.addArrayToList(status, stat); } } catch (Exception e) { throw new CoreException(new Status(IStatus.WARNING, PHPServerPlugin.PLUGIN_ID, 0, NLS.bind(Messages.errorPublishCouldNotRemoveModule, module[0].getName()), e)); } p.remove(module[0].getId()); } } else { IPath path = getModuleDeployDirectory(module[0]); IModuleResource[] mr = getResources(module); IPath[] jarPaths = null; // IWebModule webModule = // (IWebModule)module[0].loadAdapter(IWebModule.class, monitor); // IModule [] childModules = getServer().getChildModules(module, // monitor); // if (childModules != null && childModules.length > 0) { // jarPaths = new IPath[childModules.length]; // for (int i = 0; i < childModules.length; i++) { // if (webModule != null) { // jarPaths[i] = new Path(webModule.getURI(childModules[i])); // } // else { // IJ2EEModule childModule = // (IJ2EEModule)childModules[i].loadAdapter(IJ2EEModule.class, // monitor); // if (childModule != null && childModule.isBinary()) { // jarPaths[i] = new // Path("WEB-INF/lib").append(childModules[i].getName()); // } // else { // jarPaths[i] = new // Path("WEB-INF/lib").append(childModules[i].getName() + ".jar"); // } // } // } // } IStatus[] stat = helper.publishSmart(mr, path, jarPaths, monitor); PublishOperation2.addArrayToList(status, stat); p.put(module[0].getId(), path.toOSString()); } try { processPathMapping(module, deltaKind, monitor); } catch (MalformedURLException e) { throw new CoreException(new Status(IStatus.ERROR, PHPServerPlugin.PLUGIN_ID, 0, e.getMessage(), e)); } PublishOperation2.throwException(status); } /** * Gets the directory to which modules should be deployed for this server. * * @return full path to deployment directory for the server */ public IPath getServerDeployDirectory() { return getPHPServer().getServerDeployDirectory(); } public PHPRuntime getPHPRuntime() { if (getServer().getRuntime() == null) return null; return (PHPRuntime) getServer().getRuntime().loadAdapter(PHPRuntime.class, null); } private void processPathMapping(IModule[] module, int deltaKind, IProgressMonitor monitor) throws MalformedURLException, CoreException { Server server = ServersManager.getServer(getServer().getName()); PathMapper pathMapper = PathMapperRegistry.getByServer(server); Map<String, Mapping> mappings = new HashMap<>(); for (IModule m : module) { String moduleName = m.getName(); if (deltaKind == REMOVED) { getPHPServerConfiguration().setPathMapping(moduleName, new Mapping[0]); getPHPServer().saveConfiguration(monitor); pathMapper.setMapping(getPHPServerConfiguration().getPathMappings()); } else { IProject project = (IProject) ResourcesPlugin.getWorkspace().getRoot().findMember(moduleName); IScriptProject scriptProject = DLTKCore.create(project); VirtualPath remotePath = new VirtualPath(getModuleDeployDirectory(m).toOSString()); for (IProjectFragment fragment : scriptProject.getProjectFragments()) { if (!fragment.isExternal()) { VirtualPath localPath = new VirtualPath(fragment.getResource().getFullPath().toString()); Mapping mapping = new Mapping(localPath, remotePath, Type.WORKSPACE, MappingSource.ENVIRONMENT); mappings.put(localPath.toString(), mapping); } } boolean isChanged = false; Mapping[] storedMappings = getPHPServerConfiguration().getPathMappings(moduleName); if (storedMappings.length != mappings.size()) { isChanged = true; } else { for (int i = 0; i < storedMappings.length; i++) { Mapping mapping = storedMappings[i]; String local = mapping.localPath.toString(); if (mappings.containsKey(local)) { if (!mappings.get(local).equals(mapping)) { isChanged = true; break; } } } } if (isChanged) { getPHPServerConfiguration().setPathMapping(moduleName, mappings.values().toArray(new Mapping[mappings.size()])); getPHPServer().saveConfiguration(monitor); pathMapper.setMapping(getPHPServerConfiguration().getPathMappings()); } } } } public void setPHPServerRunner(IPHPServerRunner runner) { fPHPServerRunner = runner; } }