/******************************************************************************* * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library 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 Lesser General Public License for more * details. * *******************************************************************************/ package com.liferay.ide.server.core.portal; import aQute.remote.api.Agent; import com.liferay.ide.core.LiferayRuntimeClasspathEntry; import com.liferay.ide.core.util.CoreUtil; import com.liferay.ide.core.util.FileUtil; import com.liferay.ide.server.core.ILiferayServerBehavior; import com.liferay.ide.server.core.LiferayServerCore; import com.liferay.ide.server.util.PingThread; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; 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.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IProcess; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.wst.server.core.IModule; import org.eclipse.wst.server.core.IServer; import org.eclipse.wst.server.core.internal.Server; import org.eclipse.wst.server.core.model.ServerBehaviourDelegate; /** * @author Gregory Amerson * @author Simon Jiang */ @SuppressWarnings( {"restriction","rawtypes"} ) public class PortalServerBehavior extends ServerBehaviourDelegate implements ILiferayServerBehavior, IJavaLaunchConfigurationConstants { public static final String ATTR_STOP = "stop-server"; private static final String[] JMX_EXCLUDE_ARGS = new String [] { "-Dcom.sun.management.jmxremote", "-Dcom.sun.management.jmxremote.port=", "-Dcom.sun.management.jmxremote.ssl=", "-Dcom.sun.management.jmxremote.authenticate=" }; private IAdaptable info; private transient PingThread ping = null; private transient IDebugEventSetListener processListener; private BundleSupervisor _bundleSupervisor; public PortalServerBehavior() { super(); } public void addProcessListener( final IProcess newProcess ) { if( processListener != null || newProcess == null ) { return; } processListener = new IDebugEventSetListener() { public void handleDebugEvents( DebugEvent[] events ) { if( events != null ) { for( DebugEvent event : events ) { if( newProcess != null && newProcess.equals( event.getSource() ) && event.getKind() == DebugEvent.TERMINATE ) { cleanup(); } } } } }; DebugPlugin.getDefault().addDebugEventListener( processListener ); } public void cleanup() { if( ping != null ) { ping.stop(); ping = null; } if( processListener != null ) { DebugPlugin.getDefault().removeDebugEventListener( processListener ); processListener = null; } setServerState( IServer.STATE_STOPPED ); } public String getClassToLaunch() { return getPortalRuntime().getPortalBundle().getMainClass(); } @Override public IPath getDeployedPath( IModule[] module ) { return null; } public IAdaptable getInfo() { return this.info; } private int getNextToken( String s, int start ) { int i = start; int length = s.length(); char lookFor = ' '; while( i < length ) { char c = s.charAt( i ); if( lookFor == c ) { if( lookFor == '"' ) return i + 1; return i; } if( c == '"' ) { lookFor = '"'; } i++; } return -1; } private PortalRuntime getPortalRuntime() { PortalRuntime retval = null; if( getServer().getRuntime() != null ) { retval = (PortalRuntime) getServer().getRuntime().loadAdapter( PortalRuntime.class, null ); } return retval; } private PortalServer getPortalServer() { PortalServer retval = null; if( getServer() != null ) { retval = (PortalServer) getServer().loadAdapter( PortalServer.class, null ); } return retval; } private String[] getRuntimeStartProgArgs() { return getPortalRuntime().getPortalBundle().getRuntimeStartProgArgs(); } private String[] getRuntimeStartVMArguments() { final List<String> retval = new ArrayList<String>(); Collections.addAll( retval, getPortalServer().getMemoryArgs() ); Collections.addAll( retval, getPortalRuntime().getPortalBundle().getRuntimeStartVMArgs() ); int agentPort = getServer().getAttribute( AGENT_PORT, Agent.DEFAULT_PORT ); retval.add( "-D" + Agent.AGENT_SERVER_PORT_KEY + "=" + agentPort ); return retval.toArray( new String[0] ); } private String[] getRuntimeStopProgArgs() { return getPortalRuntime().getPortalBundle().getRuntimeStopProgArgs(); } private String[] getRuntimeStopVMArguments() { final List<String> retval = new ArrayList<String>(); Collections.addAll( retval, getPortalServer().getMemoryArgs() ); Collections.addAll( retval, getPortalRuntime().getPortalBundle().getRuntimeStopVMArgs() ); return retval.toArray( new String[0] ); } public void launchServer( ILaunch launch, String mode, IProgressMonitor monitor ) throws CoreException { if( "true".equals( launch.getLaunchConfiguration().getAttribute( ATTR_STOP, "false" ) ) ) { return; } final IStatus status = getPortalRuntime().validate(); if( status != null && status.getSeverity() == IStatus.ERROR ) throw new CoreException( status ); setServerRestartState( false ); setServerState( IServer.STATE_STARTING ); setMode( mode ); try { String url = "http://" + getServer().getHost(); final int port = Integer.parseInt( getPortalRuntime().getPortalBundle().getHttpPort() ); if( port != 80 ) { url += ":" + port; } ping = new PingThread( getServer(), url, -1, this ); } catch( Exception e ) { LiferayServerCore.logError( "Can't ping for portal startup." ); } } private String mergeArguments( final String orgArgsString, final String[] newArgs, final String[] excludeArgs, boolean keepActionLast ) { String retval = null; if( CoreUtil.isNullOrEmpty( newArgs ) && CoreUtil.isNullOrEmpty( excludeArgs ) ) { retval = orgArgsString; } else { retval = orgArgsString == null ? "" : orgArgsString; String xbootClasspath = ""; // replace and null out all newArgs that already exist final int size = newArgs.length; for( int i = 0; i < size; i++ ) { if( newArgs[i].startsWith( "-Xbootclasspath" ) ) { xbootClasspath = xbootClasspath + newArgs[i] + " "; newArgs[i] = null; continue; } final int ind = newArgs[i].indexOf( " " ); final int ind2 = newArgs[i].indexOf( "=" ); if( ind >= 0 && ( ind2 == -1 || ind < ind2 ) ) { // -a bc style int index = retval.indexOf( newArgs[i].substring( 0, ind + 1 ) ); if( index == 0 || ( index > 0 && Character.isWhitespace( retval.charAt( index - 1 ) ) ) ) { // replace String s = retval.substring( 0, index ); int index2 = getNextToken( retval, index + ind + 1 ); if( index2 >= 0 ) { retval = s + newArgs[i] + retval.substring( index2 ); } else { retval = s + newArgs[i]; } newArgs[i] = null; } } else if( ind2 >= 0 ) { // a=b style int index = retval.indexOf( newArgs[i].substring( 0, ind2 + 1 ) ); if( index == 0 || ( index > 0 && Character.isWhitespace( retval.charAt( index - 1 ) ) ) ) { // replace String s = retval.substring( 0, index ); int index2 = getNextToken( retval, index ); if( index2 >= 0 ) { retval = s + newArgs[i] + retval.substring( index2 ); } else { retval = s + newArgs[i]; } newArgs[i] = null; } } else { // abc style int index = retval.indexOf( newArgs[i] ); if( index == 0 || ( index > 0 && Character.isWhitespace( retval.charAt( index - 1 ) ) ) ) { // replace String s = retval.substring( 0, index ); int index2 = getNextToken( retval, index ); if( !keepActionLast || i < ( size - 1 ) ) { if( index2 >= 0 ) { retval = s + newArgs[i] + retval.substring( index2 ); } else { retval = s + newArgs[i]; } newArgs[i] = null; } else { // The last VM argument needs to remain last, // remove original arg and append the vmArg later if( index2 >= 0 ) { retval = s + retval.substring( index2 ); } else { retval = s; } } } } } // remove excluded arguments if( excludeArgs != null && excludeArgs.length > 0 ) { for( int i = 0; i < excludeArgs.length; i++ ) { int ind = excludeArgs[i].indexOf( " " ); int ind2 = excludeArgs[i].indexOf( "=" ); if( ind >= 0 && ( ind2 == -1 || ind < ind2 ) ) { // -a bc style int index = retval.indexOf( excludeArgs[i].substring( 0, ind + 1 ) ); if( index == 0 || ( index > 0 && Character.isWhitespace( retval.charAt( index - 1 ) ) ) ) { // remove String s = retval.substring( 0, index ); int index2 = getNextToken( retval, index + ind + 1 ); if( index2 >= 0 ) { // If remainder will become the first argument, remove leading blanks while( index2 < retval.length() && Character.isWhitespace( retval.charAt( index2 ) ) ) index2 += 1; retval = s + retval.substring( index2 ); } else retval = s; } } else if( ind2 >= 0 ) { // a=b style int index = retval.indexOf( excludeArgs[i].substring( 0, ind2 + 1 ) ); if( index == 0 || ( index > 0 && Character.isWhitespace( retval.charAt( index - 1 ) ) ) ) { // remove String s = retval.substring( 0, index ); int index2 = getNextToken( retval, index ); if( index2 >= 0 ) { // If remainder will become the first argument, remove leading blanks while( index2 < retval.length() && Character.isWhitespace( retval.charAt( index2 ) ) ) index2 += 1; retval = s + retval.substring( index2 ); } else retval = s; } } else { // abc style int index = retval.indexOf( excludeArgs[i] ); if( index == 0 || ( index > 0 && Character.isWhitespace( retval.charAt( index - 1 ) ) ) ) { // remove String s = retval.substring( 0, index ); int index2 = getNextToken( retval, index ); if( index2 >= 0 ) { // Remove leading blanks while( index2 < retval.length() && Character.isWhitespace( retval.charAt( index2 ) ) ) index2 += 1; retval = s + retval.substring( index2 ); } else retval = s; } } } } // add remaining vmargs to the end for( int i = 0; i < size; i++ ) { if( newArgs[i] != null ) { if( retval.length() > 0 && !retval.endsWith( " " ) ) { retval += " "; } retval += newArgs[i]; } } if( !CoreUtil.isNullOrEmpty( xbootClasspath ) ) { // delete xbootclasspath int xbootIndex = retval.lastIndexOf( "-Xbootclasspath" ); while( xbootIndex != -1 ) { String head = retval.substring( 0, xbootIndex ); int tailIndex = getNextToken( retval, xbootIndex ); String tail = retval.substring( tailIndex == retval.length() ? retval.length() : tailIndex + 1 ); retval = head + tail; xbootIndex = retval.lastIndexOf( "-Xbootclasspath" ); } retval = retval + " " + xbootClasspath; } } return retval; } private void mergeClasspath( List<IRuntimeClasspathEntry> oldCpEntries, IRuntimeClasspathEntry cpEntry ) { for( IRuntimeClasspathEntry oldCpEntry : oldCpEntries ) { if( oldCpEntry.getPath().equals( cpEntry.getPath() ) ) { return; } } oldCpEntries.add( cpEntry ); } @Override public void publish( int kind, List<IModule[]> modules, IProgressMonitor monitor, IAdaptable info ) throws CoreException { this.info = info;// save info super.publish( kind, modules, monitor, info ); this.info = null; } @Override protected void publishModule( final int kind, final int deltaKind, final IModule[] modules, final IProgressMonitor monitor ) throws CoreException { // publishing is done by PortalPublishTask return; } @Override protected void publishServer( int kind, IProgressMonitor monitor ) throws CoreException { setServerPublishState(IServer.PUBLISH_STATE_NONE); } @Override public void redeployModule( final IModule[] module ) throws CoreException { setModulePublishState( module, IServer.PUBLISH_STATE_FULL ); IAdaptable info = new IAdaptable() { @SuppressWarnings( "unchecked" ) public Object getAdapter( Class adapter ) { if( String.class.equals( adapter ) ) { return "user"; //$NON-NLS-1$ } else if( IModule.class.equals( adapter ) ) { return module[0]; } return null; } }; final List<IModule[]> modules = new ArrayList<IModule[]>(); modules.add( module ); publish( IServer.PUBLISH_FULL, modules, null, info ); } private void replaceJREConatiner( List<IRuntimeClasspathEntry> oldCp, IRuntimeClasspathEntry newJRECp ) { int size = oldCp.size(); for( int i = 0; i < size; i++ ) { final IRuntimeClasspathEntry entry2 = oldCp.get( i ); if( entry2.getPath().uptoSegment( 2 ).isPrefixOf( newJRECp.getPath() ) ) { oldCp.set( i, newJRECp ); return; } } oldCp.add( 0, newJRECp ); } public void setModulePublishState2( IModule[] module, int state ) { super.setModulePublishState( module, state ); } public void setServerStarted() { try { startBundleSupervisor(); setServerState( IServer.STATE_STARTED ); } catch( Exception e ) { LiferayServerCore.logError( "Error starting bundle supervisor", e ); } } @Override public void setupLaunchConfiguration( ILaunchConfigurationWorkingCopy launch, IProgressMonitor monitor ) throws CoreException { final String existingProgArgs = launch.getAttribute( ATTR_PROGRAM_ARGUMENTS, (String) null ); launch.setAttribute( ATTR_PROGRAM_ARGUMENTS, mergeArguments( existingProgArgs, getRuntimeStartProgArgs(), null, true ) ); final String existingVMArgs = launch.getAttribute( ATTR_VM_ARGUMENTS, (String) null ); final String[] configVMArgs = getRuntimeStartVMArguments(); launch.setAttribute( ATTR_VM_ARGUMENTS, mergeArguments( existingVMArgs, configVMArgs, null, false ) ); final PortalRuntime portalRuntime = getPortalRuntime(); final IVMInstall vmInstall = portalRuntime.getVMInstall(); if( vmInstall != null ) { launch.setAttribute( ATTR_JRE_CONTAINER_PATH, JavaRuntime.newJREContainerPath( vmInstall ).toPortableString() ); } final IRuntimeClasspathEntry[] orgClasspath = JavaRuntime.computeUnresolvedRuntimeClasspath( launch ); final int orgClasspathSize = orgClasspath.length; final List<IRuntimeClasspathEntry> oldCp = new ArrayList<IRuntimeClasspathEntry>( orgClasspathSize ); Collections.addAll( oldCp, orgClasspath ); final List<IRuntimeClasspathEntry> runCpEntries = portalRuntime.getRuntimeClasspathEntries(); for( IRuntimeClasspathEntry cpEntry : runCpEntries ) { mergeClasspath( oldCp, cpEntry ); } if( vmInstall != null ) { try { final String typeId = vmInstall.getVMInstallType().getId(); final IRuntimeClasspathEntry newJRECp = JavaRuntime.newRuntimeContainerClasspathEntry( new Path( JavaRuntime.JRE_CONTAINER ).append( typeId ).append( vmInstall.getName() ), IRuntimeClasspathEntry.BOOTSTRAP_CLASSES ); replaceJREConatiner( oldCp, newJRECp ); } catch( Exception e ) { // ignore } final IPath jrePath = new Path( vmInstall.getInstallLocation().getAbsolutePath() ); if( jrePath != null ) { final IPath toolsPath = jrePath.append( "lib/tools.jar" ); if( toolsPath.toFile().exists() ) { final IRuntimeClasspathEntry toolsJar = JavaRuntime.newArchiveRuntimeClasspathEntry( toolsPath ); // Search for index to any existing tools.jar entry int toolsIndex; for( toolsIndex = 0; toolsIndex < oldCp.size(); toolsIndex++ ) { final IRuntimeClasspathEntry entry = oldCp.get( toolsIndex ); if( entry.getType() == IRuntimeClasspathEntry.ARCHIVE && entry.getPath().lastSegment().equals( "tools.jar" ) ) { break; } } // If existing tools.jar found, replace in case it's different. Otherwise add. if( toolsIndex < oldCp.size() ) { oldCp.set( toolsIndex, toolsJar ); } else { mergeClasspath( oldCp, toolsJar ); } } } } final List<String> cp = new ArrayList<String>(); for( IRuntimeClasspathEntry entry : oldCp ) { try { if ( entry.getClasspathEntry().getEntryKind() != IClasspathEntry.CPE_CONTAINER) { entry = new LiferayRuntimeClasspathEntry(entry.getClasspathEntry()); } cp.add( entry.getMemento() ); } catch( Exception e ) { LiferayServerCore.logError( "Could not resolve cp entry " + entry, e ); } } launch.setAttribute( ATTR_CLASSPATH, cp ); launch.setAttribute( ATTR_DEFAULT_CLASSPATH, false ); setupAgent(); } private void setupAgent() { // make sure that agent is either installed or will be installed final IPath modulesPath = getPortalRuntime().getPortalBundle().getLiferayHome().append( "osgi/modules" ); final IPath agentInstalledPath = modulesPath.append( "biz.aQute.remote.agent.jar" ); File modulesDir = modulesPath.toFile(); if( !modulesDir.exists() ) { modulesDir.mkdirs(); } if( !agentInstalledPath.toFile().exists() ) { try { final File file = new File ( FileLocator.toFileURL( LiferayServerCore.getDefault().getBundle().getEntry( "bundles/biz.aQute.remote.agent-3.3.0.jar" ) ).getFile() ); FileUtil.copyFile( file, modulesPath.append( "biz.aQute.remote.agent.jar" ).toFile() ); } catch( IOException e ) { } } } @Override public void stop( boolean force ) { try { stopBundleSupervisor(); } catch( IOException e1 ) { } if( force ) { terminate(); return; } int state = getServer().getServerState(); // If stopped or stopping, no need to run stop command again if (state == IServer.STATE_STOPPED || state == IServer.STATE_STOPPING) { return; } else if (state == IServer.STATE_STARTING) { terminate(); return; } try { if( state != IServer.STATE_STOPPED ) { setServerState( IServer.STATE_STOPPING ); } final ILaunchConfiguration launchConfig = ( (Server) getServer() ).getLaunchConfiguration( false, null ); final ILaunchConfigurationWorkingCopy wc = launchConfig.getWorkingCopy(); final String args = renderCommandLine( getRuntimeStopProgArgs(), " " ); // Remove JMX arguments if present final String existingVMArgs = wc.getAttribute( IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, (String) null ); if( existingVMArgs.indexOf( JMX_EXCLUDE_ARGS[0] ) >= 0 ) { wc.setAttribute( IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, mergeArguments( existingVMArgs, getRuntimeStopVMArguments(), JMX_EXCLUDE_ARGS, false ) ); } else { wc.setAttribute( IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, mergeArguments( existingVMArgs, getRuntimeStopVMArguments(), null, true ) ); } wc.setAttribute( IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, args ); wc.setAttribute( "org.eclipse.debug.ui.private", true ); wc.setAttribute( ATTR_STOP, "true" ); wc.launch( ILaunchManager.RUN_MODE, new NullProgressMonitor() ); } catch( Exception e ) { LiferayServerCore.logError( "Error stopping portal", e ); } } protected void terminate() { if( getServer().getServerState() == IServer.STATE_STOPPED ) { return; } try { setServerState( IServer.STATE_STOPPING ); ILaunch launch = getServer().getLaunch(); if( launch != null ) { launch.terminate(); cleanup(); } } catch( Exception e ) { LiferayServerCore.logError( "Error killing the process", e ); } } protected static String renderCommandLine( String[] commandLine, String separator ) { if( commandLine == null || commandLine.length < 1 ) { return ""; } StringBuffer buf = new StringBuffer( commandLine[0] ); for( int i = 1; i < commandLine.length; i++ ) { buf.append( separator ); buf.append( commandLine[i] ); } return buf.toString(); } public BundleSupervisor getBundleSupervisor() { return _bundleSupervisor; } public void startBundleSupervisor() throws Exception { _bundleSupervisor = new BundleSupervisor(); int agentPort = getServer().getAttribute( AGENT_PORT, Agent.DEFAULT_PORT ); _bundleSupervisor.connect( getServer().getHost(), agentPort ); } public void stopBundleSupervisor() throws IOException { if( _bundleSupervisor != null ) { try { _bundleSupervisor.close(); } catch( Exception e ) { LiferayServerCore.logError( "Unable to close bundle supervisor", e ); } } } }