/** * Copyright (c) 2008-2011 Sonatype, Inc. * All rights reserved. Includes the third-party code listed at http://www.sonatype.com/products/nexus/attributions. * * This program is free software: you can redistribute it and/or modify it only under the terms of the GNU Affero General * Public License Version 3 as published by the Free Software Foundation. * * This program 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 Affero General Public License Version 3 * for more details. * * You should have received a copy of the GNU Affero General Public License Version 3 along with this program. If not, see * http://www.gnu.org/licenses. * * Sonatype Nexus (TM) Open Source Version is available from Sonatype, Inc. Sonatype and Sonatype Nexus are trademarks of * Sonatype, Inc. Apache Maven is a trademark of the Apache Foundation. M2Eclipse is a trademark of the Eclipse Foundation. * All other trademarks are the property of their respective owners. */ package org.sonatype.nexus.test.launcher; import java.io.File; import java.io.FileInputStream; import org.apache.log4j.Logger; import org.codehaus.plexus.classworlds.launcher.Launcher; import org.sonatype.appbooter.ctl.AppBooterServiceException; import org.sonatype.appbooter.ctl.Service; import org.sonatype.nexus.rt.boot.ITAppBooterCustomizer; public class ThreadedPlexusAppBooterService implements Service { private static Logger LOG = Logger.getLogger( ThreadedPlexusAppBooterService.class ); private LauncherThread launcherThread; private Launcher launcher = new Launcher(); private int controlPort; private String testId; private static int THREAD_COUNT = 1; public ThreadedPlexusAppBooterService( File classworldsConf, int controlPort, String testId ) throws Exception { // System.setProperty( "plexus"+ PlexusAppBooterService.ENABLE_CONTROL_SOCKET, "true" ); this.controlPort = controlPort; this.testId = testId; // we are "tricking" this line: // set plexus.appbooter.customizers default org.sonatype.nexus.NexusBooterCustomizer System.setProperty( "plexus.appbooter.customizers", ITAppBooterCustomizer.class.getName() + ",org.sonatype.nexus.NexusBooterCustomizer" ); ClassLoader systemClassLoader = this.launcher.getSystemClassLoader(); this.launcher.setSystemClassLoader( null ); this.launcher.configure( new FileInputStream( classworldsConf ) ); this.launcher.setAppMain( "org.sonatype.appbooter.PlexusAppBooter", "plexus.core" ); this.launcher.getMainRealm().importFrom( systemClassLoader, "org.codehaus.plexus.classworlds" ); } public boolean isShutdown() { return this.launcherThread == null || !this.launcherThread.isAlive(); } public boolean isStopped() { return this.isShutdown(); } public void shutdown() throws AppBooterServiceException { // ControllerClient client; // try // { // client = new ControllerClient( controlPort ); // client.shutdown(); // } // catch ( Exception e ) // { // throw new AppBooterServiceException( "Failed to connect to client", e ); // } // this.launcherThread = null; if ( this.launcherThread != null && this.launcherThread.isAlive() ) { synchronized ( launcherThread ) { this.launcherThread.interrupt(); try { this.launcherThread.join( 20000 ); } catch ( InterruptedException e ) { System.err.println( "Error waiting for launcher Thread to finish: " + e.getMessage() ); // pass it on. Thread.currentThread().interrupt(); } } } } public void start() throws AppBooterServiceException { if ( this.launcherThread == null ) { LOG.info( "Creating LauncherThread (" + launcher + ", " + controlPort + ")" ); this.launcherThread = new LauncherThread( launcher, controlPort, testId ); this.launcherThread.start(); } else { LOG.info( "Existing LauncherThread (" + launcher + ", " + controlPort + ")" ); } if ( !this.launcherThread.isAlive() ) { LOG.info( "Starting LauncherThread (" + launcher + ", " + controlPort + ")" ); LOG.info( "LauncherThread state: " + launcherThread.getState() ); Exception launcherThreadException = launcherThread.getException(); if ( launcherThreadException != null ) { throw new AppBooterServiceException( "LauncherThread cannot be started: " + launcherThreadException.getMessage(), launcherThreadException ); } else { // it died off, without any exception this.launcherThread = new LauncherThread( launcher, controlPort, testId ); this.launcherThread.start(); } } } public void stop() throws AppBooterServiceException { this.shutdown(); } @SuppressWarnings( "deprecation" ) public void forceStop() { if ( this.launcherThread != null ) { this.launcherThread.stop(); this.launcherThread = null; } } class LauncherThread extends Thread { private Launcher launcher; private int controlPortArg; private String testIdArg; private Exception exception; public LauncherThread( Launcher launcher, int controlPort, String testId ) { this.launcher = launcher; this.controlPortArg = controlPort; this.testIdArg = ITAppBooterCustomizer.TEST_ID_PREFIX + testId; this.setName( "LauncherThread-" + THREAD_COUNT++ ); } @Override public void run() { try { this.launcher.launch( new String[] { Integer.toString( controlPortArg ), testIdArg } ); } catch ( Exception e ) { exception = e; e.printStackTrace(); } } public Exception getException() { return exception; } } public void clean() { this.launcherThread = null; // TODO: this causes severe problems // Maybe since not all the _isolated_ classloader threads are done yet? // System.gc(); } }