/*******************************************************************************
* 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.remote;
import com.liferay.ide.core.ILiferayConstants;
import com.liferay.ide.core.ILiferayProject;
import com.liferay.ide.core.IWebProject;
import com.liferay.ide.core.LiferayCore;
import com.liferay.ide.core.remote.APIException;
import com.liferay.ide.core.util.CoreUtil;
import com.liferay.ide.server.core.ILiferayServerBehavior;
import com.liferay.ide.server.core.LiferayServerCore;
import com.liferay.ide.server.util.LiferayPublishHelper;
import com.liferay.ide.server.util.ServerUtil;
import com.liferay.ide.server.util.SocketUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
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.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.common.componentcore.internal.util.ComponentUtilities;
import org.eclipse.wst.server.core.IModule;
import org.eclipse.wst.server.core.IServer;
import org.eclipse.wst.server.core.IServerLifecycleListener;
import org.eclipse.wst.server.core.ServerCore;
import org.eclipse.wst.server.core.model.IModuleResource;
import org.eclipse.wst.server.core.model.IModuleResourceDelta;
import org.eclipse.wst.server.core.model.ServerBehaviourDelegate;
/**
* @author Greg Amerson
* @author Tao Tao
* @author Simon Jiang
*/
@SuppressWarnings( "restriction" )
public class RemoteServerBehavior extends ServerBehaviourDelegate
implements ILiferayServerBehavior, IServerLifecycleListener
{
protected ILaunch currentLaunch;
private IServerManagerConnection remoteConnection;
private List<IModule[]> redeployModules;
protected Job remoteServerUpdateJob;
public RemoteServerBehavior()
{
super();
}
public boolean canConnect()
{
IStatus status = SocketUtil.canConnect( getServer().getHost(), getRemoteServer().getHTTPPort() );
if( status != null && status.isOK() )
{
return true;
}
else
{
status = SocketUtil.canConnectProxy( getServer().getHost(), getRemoteServer().getHTTPPort() );
if( status != null && status.isOK() )
{
return true;
}
}
return false;
}
@Override
public IStatus canPublish()
{
if( currentLaunch != null )
{
return Status.OK_STATUS;
}
else
{
return Status.CANCEL_STATUS;
}
}
@Override
public IStatus canRestart( String mode )
{
return LiferayServerCore.createWarningStatus( Msgs.restartingRemoteServerNotSupported );
}
@Override
public boolean canRestartModule( IModule[] module )
{
IStatus status = getServer().canModifyModules( module, null, null );
return status.isOK();
}
@Override
public IStatus canStart( String launchMode )
{
return LiferayServerCore.error( Msgs.liferayServerInstanceRemote );
}
@Override
public IStatus canStop()
{
return LiferayServerCore.createWarningStatus( Msgs.stoppingRemoteLiferayServerNotSupported );
}
protected Job checkRemoteServerState( IProgressMonitor monitor )
{
if( monitor == null )
{
monitor = new NullProgressMonitor();
}
// make sure the server has not been deleted
if( !( getServer().equals( ServerCore.findServer( getServer().getId() ) ) ) )
{
remoteServerUpdateJob = null;
return null;
}
final int state = getServer().getServerState();
Job updateServerJob = null;
switch( state )
{
case IServer.STATE_UNKNOWN:
if( canConnect() )
{
updateServerJob = new Job( "Updating status for " + getServer().getName() ) //$NON-NLS-1$
{
@Override
protected IStatus run( IProgressMonitor monitor )
{
try
{
return updateServerState( state, monitor );
}
catch( Exception e )
{
LiferayServerCore.logError( e );
}
return Status.OK_STATUS;
}
};
}
break;
case IServer.STATE_STOPPED:
if( canConnect() )
{
updateServerJob = new Job( "Connecting to " + getServer().getName() ) //$NON-NLS-1$
{
@Override
protected IStatus run( IProgressMonitor monitor )
{
int wasState = getRemoteServerState( state, monitor );
if( wasState == IServer.STATE_STARTED )
{
setServerState( IServer.STATE_STARTED );
launchServer( monitor );
}
return Status.OK_STATUS;
}
};
}
break;
case IServer.STATE_STARTED:
boolean isAlive = false;
try
{
isAlive = getServerManagerConnection().isAlive();
}
catch( Exception ex )
{
// no error, this could because server is down
}
if( isAlive && ( this.currentLaunch == null || this.currentLaunch.isTerminated() ) )
{
updateServerJob = new Job( "Connecting to server: " + getServer().getName() ) //$NON-NLS-1$
{
@Override
protected IStatus run( IProgressMonitor monitor )
{
launchServer( monitor );
return Status.OK_STATUS;
}
};
}
else
{
// check on the current launch to make sure it is still valid
if( !isAlive )
{
terminateLaunch();
setServerState( IServer.STATE_STOPPED );
}
}
break;
case IServer.STATE_STOPPING:
case IServer.STATE_STARTING:
// do nothing since server state will get updated automatically after start/stop
break;
default:
break;
}
return updateServerJob;
}
protected Job createRemoteServerUpdateJob()
{
return new Job( "Remote server update." ) //$NON-NLS-1$
{
@Override
protected IStatus run( IProgressMonitor monitor )
{
Job updateServerJob = checkRemoteServerState( monitor );
if( updateServerJob != null )
{
updateServerJob.schedule();
try
{
updateServerJob.join();
}
catch( InterruptedException e )
{
}
}
if( remoteServerUpdateJob != null )
{
remoteServerUpdateJob.schedule( getRemoteServerUpdateDelay() );
}
return Status.OK_STATUS;
}
};
}
@Override
public void dispose()
{
super.dispose();
remoteServerUpdateJob.cancel();
remoteServerUpdateJob = null;
}
@Override
protected MultiStatus executePublishers(
int kind, List<IModule[]> modules, List<Integer> deltaKinds, IProgressMonitor monitor, IAdaptable info )
throws CoreException
{
return super.executePublishers(
kind, ( redeployModules == null ) ? modules : redeployModules, deltaKinds, monitor, info );
}
public IPath getDeployedPath( IModule[] module )
{
return null;
}
protected IRemoteServer getRemoteServer()
{
return RemoteUtil.getRemoteServer( getServer() );
}
public int getRemoteServerState( int currentServerState, IProgressMonitor monitor )
{
try
{
if( currentServerState == IServer.STATE_STOPPED )
{
monitor.beginTask( NLS.bind( Msgs.updatingServerState, getServer().getName() ), 100 );
}
Object retval = null;
IServerManagerConnection remoteConnection = getServerManagerConnection();
try
{
retval = remoteConnection.getServerState();
}
catch( Exception e )
{
e.printStackTrace();
}
if( retval == null )
{
return IServer.STATE_UNKNOWN;
}
String serverState = retval.toString();
if( "STARTED".equals( serverState ) ) //$NON-NLS-1$
{
return IServer.STATE_STARTED;
}
else if( "STOPPED".equals( serverState ) ) //$NON-NLS-1$
{
return IServer.STATE_STOPPED;
}
}
catch( Exception e )
{
LiferayServerCore.logError( "Could not get server state.", e ); //$NON-NLS-1$
return IServer.STATE_UNKNOWN;
}
return IServer.STATE_UNKNOWN;
}
protected long getRemoteServerUpdateDelay()
{
return 5000;
}
protected IServerManagerConnection getServerManagerConnection()
{
if( remoteConnection == null )
{
remoteConnection = LiferayServerCore.getRemoteConnection( getRemoteServer() );
}
return remoteConnection;
}
@Override
protected void initialize( IProgressMonitor monitor )
{
ServerCore.addServerLifecycleListener( this );
remoteServerUpdateJob = createRemoteServerUpdateJob();
remoteServerUpdateJob.setSystem( true );
remoteServerUpdateJob.schedule();
}
protected boolean isModuleInstalled( IModule[] module )
{
for( IModule m : module )
{
final String appName = ComponentUtilities.getServerContextRoot( m.getProject() );
try
{
if( getServerManagerConnection().isAppInstalled( appName ) )
{
return true;
}
}
catch( APIException e )
{
}
}
return false;
}
@SuppressWarnings( { "unchecked", "rawtypes" } )
protected void launchServer( IProgressMonitor monitor )
{
if( currentLaunch != null && ( !currentLaunch.isTerminated() ) )
{
terminateLaunch();
}
ILaunchConfigurationWorkingCopy config = null;
String launchMode = null;
try
{
config = getServer().getLaunchConfiguration( true, null ).getWorkingCopy();
IServerManagerConnection remoteConnection = getServerManagerConnection();
Integer debugPort = remoteConnection.getDebugPort();
if( debugPort > 0 )
{
Map connectMap = config.getAttribute( IJavaLaunchConfigurationConstants.ATTR_CONNECT_MAP, (Map) null );
if( connectMap == null )
{
connectMap = new HashMap();
config.setAttribute( IJavaLaunchConfigurationConstants.ATTR_CONNECT_MAP, connectMap );
}
connectMap.put( "hostname", getServer().getHost() ); //$NON-NLS-1$
connectMap.put( "port", debugPort.toString() ); //$NON-NLS-1$
launchMode = ILaunchManager.DEBUG_MODE;
}
else
{
launchMode = ILaunchManager.RUN_MODE;
}
try
{
currentLaunch = config.launch( launchMode, null );
}
catch( CoreException ce )
{
if( debugPort > 0 )
{
// if debug launch failed just try to launch one in run mode
ServerUtil.terminateLaunchesForConfig( config );
}
try
{
currentLaunch = config.launch( ILaunchManager.RUN_MODE, null );
}
catch( CoreException ce1 )
{
ServerUtil.terminateLaunchesForConfig( config );
}
}
}
catch( Exception e )
{
LiferayServerCore.logError( "Could not create new server launch configuration.", e ); //$NON-NLS-1$
}
}
@Override
protected void publishFinish( IProgressMonitor monitor ) throws CoreException
{
super.publishFinish( monitor );
setServerPublishState( IServer.PUBLISH_STATE_NONE );
this.redeployModules = null;
}
@Override
protected void publishModule( int kind, int deltaKind, IModule[] module, IProgressMonitor monitor )
throws CoreException
{
boolean shouldPublishModule =
LiferayPublishHelper.prePublishModule(
this, kind, deltaKind, module, getPublishedResourceDelta( module ), monitor );
if( !shouldPublishModule )
{
return;
}
int modulePublishState = -1;
if( kind == IServer.PUBLISH_FULL && ( deltaKind == ADDED || deltaKind == CHANGED ) )
{
modulePublishState = publishModuleFull( module, deltaKind, monitor );
}
else if( kind == IServer.PUBLISH_FULL && deltaKind == NO_CHANGE )
{
// first check to see if this module is even in the list of applications, if it is then we don't need to
// actually update it
modulePublishState = publishModuleFull( module, deltaKind, monitor );
}
else if( kind == IServer.PUBLISH_FULL && deltaKind == REMOVED )
{
modulePublishState = removeModule( module, monitor );
}
else if( ( kind == IServer.PUBLISH_AUTO || kind == IServer.PUBLISH_INCREMENTAL ) && deltaKind == CHANGED )
{
modulePublishState = publishModuleDelta( module, monitor );
}
if( modulePublishState == -1 )
{
modulePublishState = IServer.PUBLISH_STATE_UNKNOWN;
}
// by default, assume the module has published successfully.
// this will update the publish state and delta correctly
setModulePublishState( module, modulePublishState );
}
protected int publishModuleDelta( IModule[] module, IProgressMonitor monitor ) throws CoreException
{
if( monitor == null )
{
monitor = new NullProgressMonitor();
}
IProject moduleProject = module[0].getProject();
IModuleResourceDelta[] delta = getPublishedResourceDelta( module );
if( shouldPublishModuleFull( delta ) )
{
return publishModuleFull( module, CHANGED, monitor );
}
final String appName = ComponentUtilities.getServerContextRoot( moduleProject );
monitor.subTask( "Creating partial " + moduleProject.getName() + " update archive..." ); //$NON-NLS-1$ //$NON-NLS-2$
final ILiferayProject liferayProject = LiferayCore.create( moduleProject );
final IRemoteServerPublisher publisher = liferayProject.adapt( IRemoteServerPublisher.class );
if ( publisher == null )
{
setModuleStatus( module, null );
throw new CoreException( LiferayServerCore.error( Msgs.publishingModuleProject ) );
}
final IPath warPath = publisher.publishModuleDelta( appName + ".war", delta, "liferay", true );
monitor.worked( 25 );
if( monitor != null && monitor.isCanceled() )
{
return IServer.PUBLISH_STATE_UNKNOWN;
}
monitor.subTask( Msgs.gettingLiferayConnection );
IServerManagerConnection connection = getServerManagerConnection();
monitor.subTask( NLS.bind( Msgs.updatingModuleProject, moduleProject.getName() ) );
Object error = null;
try
{
error = connection.updateApplication( appName, warPath.toOSString(), monitor );
}
catch( APIException e )
{
error = e.getMessage();
}
monitor.worked( 90 );
if( error != null )
{
throw new CoreException( LiferayServerCore.error( error.toString() ) );
}
monitor.done();
return IServer.PUBLISH_STATE_NONE;
}
protected int publishModuleFull( IModule[] module, int deltaKind, IProgressMonitor monitor ) throws CoreException
{
if( module == null )
{
throw new CoreException( LiferayServerCore.error( "Cannot publish module with length " + //$NON-NLS-1$
( module != null ? module.length : 0 ) ) );
}
if( monitor == null )
{
monitor = new NullProgressMonitor();
}
final IModule publishModule = module[0];
final IProject moduleProject = publishModule.getProject();
final IProgressMonitor submon = CoreUtil.newSubMonitor( monitor, 100 );
submon.subTask( "Deploying " + moduleProject.getName() + " to Liferay..." ); //$NON-NLS-1$ //$NON-NLS-2$
final ILiferayProject liferayProject = LiferayCore.create( moduleProject );
final IRemoteServerPublisher publisher = liferayProject.adapt( IRemoteServerPublisher.class );
if ( publisher == null )
{
setModuleStatus( module, null );
throw new CoreException( LiferayServerCore.error( Msgs.publishingModuleProject ) );
}
final IPath warPath = publisher.publishModuleFull( monitor );
submon.worked( 25 ); // 25% complete
if( monitor.isCanceled() )
{
return IServer.PUBLISH_STATE_FULL;
}
final String appName = ComponentUtilities.getServerContextRoot( moduleProject );
IServerManagerConnection remoteConnection = getServerManagerConnection();
setModuleStatus( module, LiferayServerCore.info( Msgs.installing ) );
submon.worked( 25 ); // 50% complete
if( monitor.isCanceled() )
{
return IServer.PUBLISH_STATE_FULL;
}
submon.subTask( NLS.bind( Msgs.publishingModuleProject, moduleProject.getName() ) );
Object error = null;
try
{
error = remoteConnection.installApplication( warPath.toOSString(), appName, submon );
}
catch( Exception ex )
{
setModuleStatus( module, null );
setModuleState( module, IServer.STATE_UNKNOWN );
throw new CoreException( LiferayServerCore.createErrorStatus( ex ) );
}
finally
{
if( warPath.toFile().exists() )
{
warPath.toFile().delete();
}
}
if( error != null )
{
setModuleStatus( module, null );
setModuleState( module, IServer.STATE_UNKNOWN );
throw new CoreException( LiferayServerCore.error( error.toString() ) );
}
submon.worked( 40 ); // 90%
setModuleStatus( module, LiferayServerCore.info( Msgs.starting ) );
// scriptFile = getScriptFile("publish/startApplicationScript.groovy");
if( monitor.isCanceled() )
{
setModuleStatus( module, null );
return IServer.PUBLISH_STATE_UNKNOWN;
}
setModuleStatus( module, null );
setModuleState( module, IServer.STATE_STARTED );
submon.worked( 10 ); // 100%
monitor.done();
return IServer.PUBLISH_STATE_NONE;
}
@SuppressWarnings( "rawtypes" )
@Override
protected void publishModules( int kind, List modules, List deltaKind2, MultiStatus multi, IProgressMonitor monitor )
{
super.publishModules( kind, ( redeployModules == null ) ? modules : redeployModules, deltaKind2, multi, monitor );
}
@Override
protected void publishStart( IProgressMonitor monitor ) throws CoreException
{
int state = getServer().getServerState();
if( state != IServer.STATE_STARTED )
{
throw new CoreException(
LiferayServerCore.error( Msgs.notPublishRemoteServer ) );
}
}
@SuppressWarnings( "rawtypes" )
public void redeployModule( IModule[] module ) throws CoreException
{
setModulePublishState( module, IServer.PUBLISH_STATE_FULL );
IAdaptable info = new IAdaptable()
{
public Object getAdapter( Class adapter )
{
if( String.class.equals( adapter ) )
return "user"; //$NON-NLS-1$
return null;
}
};
final List<IModule[]> modules = new ArrayList<IModule[]>();
modules.add( module );
try
{
redeployModules = modules;
publish( IServer.PUBLISH_FULL, modules, null, info );
}
catch( CoreException e )
{
throw e;
}
finally
{
redeployModules = null;
}
}
protected int removeModule( IModule[] module, IProgressMonitor monitor ) throws CoreException
{
if( module == null )
{
throw new CoreException( LiferayServerCore.error( "Cannot publish module with length " + //$NON-NLS-1$
( module != null ? module.length : 0 ) ) );
}
if( monitor == null )
{
monitor = new NullProgressMonitor();
}
IModule publishModule = module[0];
IProject moduleProject = publishModule.getProject();
if( moduleProject == null )
{
// just return this is a dead module
setModuleStatus( module, null );
return IServer.PUBLISH_STATE_UNKNOWN;
}
/*
* First look to see if this module (ear plugin) is actually installed on liferay, and if it is then uninstall
* it
*/
monitor.beginTask( NLS.bind( Msgs.undeployingModuleProject, moduleProject.getName() ), 100 );
final String appName = ComponentUtilities.getServerContextRoot( moduleProject );
setModuleStatus( module, LiferayServerCore.info( Msgs.uninstalling ) );
monitor.subTask( Msgs.gettingRemoteConnection );
IServerManagerConnection remoteConnection = getServerManagerConnection();
monitor.worked( 25 ); // 25%
// File scriptFile = getScriptFile( "publish/uninstallApplicationScript.groovy" );
monitor.subTask( NLS.bind( Msgs.uninstallingModuleProject, moduleProject.getName() ));
Object error = null;
try
{
error = remoteConnection.uninstallApplication( appName, monitor );
}
catch( APIException e )
{
error = e.getMessage();
}
monitor.worked( 75 ); // 100%
if( error != null )
{
throw new CoreException( LiferayServerCore.error( error.toString() ) );
}
setModuleStatus( module, null );
return IServer.PUBLISH_STATE_NONE;
}
public void serverAdded( IServer server )
{
}
public void serverChanged( IServer server )
{
}
public void serverRemoved( IServer server )
{
if( server.equals( getServer() ) )
{
if( currentLaunch != null && !currentLaunch.isTerminated() )
{
try
{
currentLaunch.terminate();
}
catch( DebugException e )
{
}
}
}
ServerCore.removeServerLifecycleListener( this );
}
protected boolean shouldPublishModuleFull( IModuleResourceDelta[] deltas )
{
boolean retval = false;
if( !CoreUtil.isNullOrEmpty( deltas ) )
{
for( IModuleResourceDelta delta : deltas )
{
if( shouldPublishModuleFull( delta.getAffectedChildren() ) )
{
retval = true;
break;
}
else
{
final IModuleResource resource = delta.getModuleResource();
final IFile resourceFile = (IFile) resource.getAdapter( IFile.class );
if( resourceFile != null )
{
final IWebProject lrproject = LiferayCore.create( IWebProject.class, resourceFile.getProject() );
if( lrproject != null )
{
final IPath docrootPath = lrproject.getDefaultDocrootFolder().getFullPath();
if( lrproject.findDocrootResource( resourceFile.getFullPath().makeRelativeTo(
docrootPath ) ) != null )
{
if( resource.getName().equals( "web.xml" ) ||
resource.getName().equals(
ILiferayConstants.LIFERAY_PLUGIN_PACKAGE_PROPERTIES_FILE ) )
{
break;
}
else if( resource.getName().equals( "portlet.xml" ) )
{
break;
}
}
}
}
}
}
}
return retval;
}
@Override
public void stop( boolean force )
{
setServerState( IServer.STATE_STOPPED );
}
protected void terminateLaunch()
{
if( currentLaunch != null )
{
try
{
currentLaunch.terminate();
}
catch( DebugException e )
{
}
currentLaunch = null;
}
}
protected IStatus updateModuleState( IModule module )
{
final String appName = ComponentUtilities.getServerContextRoot( module.getProject() );
boolean appStarted =false;
try
{
appStarted = getServerManagerConnection().isLiferayPluginStarted( appName );
}
catch( APIException e )
{
LiferayServerCore.logError( e );
}
IModule[] module2 = new IModule[] { module };
setModuleState( module2, appStarted ? IServer.STATE_STARTED : IServer.STATE_STOPPED );
return Status.OK_STATUS;
}
protected IStatus updateServerState( int currentServerState, IProgressMonitor monitor )
{
if( getServer() == null )
{
return Status.OK_STATUS;
}
monitor.beginTask( Msgs.updatingServerStatus, 100 );
int remoteState = getRemoteServerState( currentServerState, monitor );
if( remoteState == IServer.STATE_STARTED )
{
setServerState( IServer.STATE_STARTED );
launchServer( monitor );
}
else if( remoteState == IServer.STATE_STOPPED )
{
terminateLaunch();
setServerState( IServer.STATE_STOPPED );
}
// check modules
IModule[] modules = getServer().getModules();
if( !CoreUtil.isNullOrEmpty( modules ) )
{
List<String> plugins = getServerManagerConnection().getLiferayPlugins();
for( IModule module : modules )
{
if( CoreUtil.isLiferayProject( module.getProject() ) )
{
final String appName = ComponentUtilities.getServerContextRoot( module.getProject() );
if( plugins.contains( appName ) )
{
updateModuleState( module );
}
else
{
setModuleState( new IModule[] { module }, IServer.STATE_UNKNOWN );
}
}
}
}
return Status.OK_STATUS;
}
private static class Msgs extends NLS
{
public static String gettingLiferayConnection;
public static String gettingRemoteConnection;
public static String installing;
public static String liferayServerInstanceRemote;
public static String notPublishRemoteServer;
public static String publishingModuleProject;
public static String restartingRemoteServerNotSupported;
public static String starting;
public static String stoppingRemoteLiferayServerNotSupported;
public static String undeployingModuleProject;
public static String uninstalling;
public static String uninstallingModuleProject;
public static String updatingModuleProject;
public static String updatingServerState;
public static String updatingServerStatus;
static
{
initializeMessages( RemoteServerBehavior.class.getName(), Msgs.class );
}
}
}