001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020
021 package org.apache.directory.studio.apacheds.jobs;
022
023
024 import java.io.File;
025 import java.io.FileOutputStream;
026 import java.io.IOException;
027 import java.util.ArrayList;
028 import java.util.List;
029
030 import org.apache.directory.studio.apacheds.ApacheDsPluginUtils;
031 import org.apache.directory.studio.apacheds.ConsolesHandler;
032 import org.apache.directory.studio.apacheds.LogMessageConsole;
033 import org.apache.directory.studio.apacheds.configuration.model.ServerConfiguration;
034 import org.apache.directory.studio.apacheds.configuration.model.v153.ServerConfigurationV153;
035 import org.apache.directory.studio.apacheds.configuration.model.v154.ServerConfigurationV154;
036 import org.apache.directory.studio.apacheds.configuration.model.v155.ServerConfigurationV155;
037 import org.apache.directory.studio.apacheds.model.Server;
038 import org.apache.directory.studio.apacheds.model.ServerStateEnum;
039 import org.apache.log4j.net.SocketServer;
040 import org.apache.mina.util.AvailablePortFinder;
041 import org.eclipse.core.runtime.CoreException;
042 import org.eclipse.core.runtime.IPath;
043 import org.eclipse.core.runtime.IProgressMonitor;
044 import org.eclipse.core.runtime.IStatus;
045 import org.eclipse.core.runtime.NullProgressMonitor;
046 import org.eclipse.core.runtime.Status;
047 import org.eclipse.core.runtime.jobs.Job;
048 import org.eclipse.debug.core.DebugEvent;
049 import org.eclipse.debug.core.DebugPlugin;
050 import org.eclipse.debug.core.IDebugEventSetListener;
051 import org.eclipse.debug.core.ILaunch;
052 import org.eclipse.debug.core.ILaunchConfiguration;
053 import org.eclipse.debug.core.ILaunchConfigurationType;
054 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
055 import org.eclipse.debug.core.ILaunchManager;
056 import org.eclipse.debug.core.model.RuntimeProcess;
057 import org.eclipse.debug.ui.IDebugUIConstants;
058 import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
059 import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
060 import org.eclipse.jdt.launching.IVMInstall;
061 import org.eclipse.jdt.launching.JavaRuntime;
062 import org.eclipse.osgi.util.NLS;
063 import org.eclipse.swt.widgets.Display;
064
065
066 /**
067 * This class implements a {@link Job} that is used to launch a server.
068 *
069 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
070 * @version $Rev$, $Date$
071 */
072 public class LaunchServerJob extends Job
073 {
074 /** The server */
075 private Server server;
076
077 /** The configuration */
078 private ServerConfiguration configuration;
079
080 /** The launch that will be created when running the server */
081 private ILaunch launch;
082
083 /** The minimum port number for the socket server */
084 private static final int MIN_PORT = 1024;
085
086 /** The logs level */
087 private String logsLevel = "WARN"; //$NON-NLS-1$
088
089 /** The logs pattern */
090 private String logsPattern = "[%d{HH:mm:ss}] %p [%c] - %m%n"; //$NON-NLS-1$
091
092
093 /**
094 * Creates a new instance of LaunchServerJob.
095 *
096 * @param server
097 * the server
098 * @param configuration
099 * the configuration
100 */
101 public LaunchServerJob( Server server, ServerConfiguration configuration )
102 {
103 super( "" ); //$NON-NLS-1$
104 this.server = server;
105 this.configuration = configuration;
106 }
107
108
109 /*
110 * (non-Javadoc)
111 *
112 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
113 */
114 protected IStatus run( IProgressMonitor monitor )
115 {
116 // Setting the name of the Job
117 setName( NLS.bind( Messages.getString( "LaunchServerJob.Starting" ), new String[] { server.getName() } ) ); //$NON-NLS-1$
118
119 // Setting the server in a "starting" state
120 server.setState( ServerStateEnum.STARTING );
121 writeToInfoConsoleMessageStream( Messages.getString( "LaunchServerJob.ServerStarting" ) ); //$NON-NLS-1$
122
123 // Getting the first available port for the Log4J socket server
124 int port = AvailablePortFinder.getNextAvailable( MIN_PORT );
125
126 // Launching the socket server
127 launchSocketServer( port );
128
129 // Overwriting the server's log4j.properties file
130 try
131 {
132 overwriteServersLog4jPropertiesFile( port );
133 }
134 catch ( IOException e )
135 {
136 ApacheDsPluginUtils.reportError( Messages.getString( "LaunchServerJob.ErrorOverwritingLog" ) //$NON-NLS-1$
137 + e.getMessage() );
138 }
139
140 // Launching Apache DS
141 launchApacheDS();
142
143 // Starting the startup listener thread
144 startStartupListenerThread();
145
146 // Starting the "terminate" listener thread
147 startTerminateListenerThread();
148
149 return Status.OK_STATUS;
150 }
151
152
153 /**
154 * Starts the startup listener thread.
155 */
156 private void startStartupListenerThread()
157 {
158 // Getting the current time
159 long startTime = System.currentTimeMillis();
160
161 // Calculating the watch dog time
162 final long watchDog = startTime + ( 1000 * 60 * 3 ); // 3 minutes
163
164 // Creating the thread
165 Thread thread = new Thread()
166 {
167 public void run()
168 {
169 // Looping until the end of the watchdog
170 while ( ( System.currentTimeMillis() < watchDog ) && ( ServerStateEnum.STARTING == server.getState() ) )
171 {
172 try
173 {
174 // Getting the port to test
175 int port = getTestingPort( configuration );
176
177 // If no protocol is enabled, we pass this and
178 // declare the server as started
179 if ( port != 0 )
180 {
181 // Trying to see if the port is available
182 if ( AvailablePortFinder.available( port ) )
183 {
184 // The port is still available
185 throw new Exception();
186 }
187 }
188
189 // If we pass the creation of the context, it means
190 // the server is correctly started
191
192 // We set the state of the server to 'started'...
193 server.setState( ServerStateEnum.STARTED );
194 writeToInfoConsoleMessageStream( Messages.getString( "LaunchServerJob.ServerStarted" ) ); //$NON-NLS-1$
195
196 // ... and we exit the thread
197 return;
198 }
199 catch ( Exception e )
200 {
201 // If we get an exception,it means the server is not
202 // yet started
203
204 // We just wait one second before starting the test once
205 // again
206 try
207 {
208 Thread.sleep( 1000 );
209 }
210 catch ( InterruptedException e1 )
211 {
212 // Nothing to do...
213 }
214 }
215 }
216
217 // If at the end of the watch dog the state of the server is
218 // still 'starting' then, we declare the server as 'stopped'
219 if ( ServerStateEnum.STARTING == server.getState() )
220 {
221 server.setState( ServerStateEnum.STOPPED );
222 writeToInfoConsoleMessageStream( Messages.getString( "LaunchServerJob.ServerStopped" ) ); //$NON-NLS-1$
223 }
224 }
225
226
227 /**
228 * Gets the testing port.
229 *
230 * @param configuration
231 * the server configuration
232 * @return
233 * the testing port
234 */
235 private int getTestingPort( ServerConfiguration configuration )
236 {
237 if ( configuration instanceof ServerConfigurationV155 )
238 {
239 return getTestingPortVersion155( ( ServerConfigurationV155 ) configuration );
240 }
241 else if ( configuration instanceof ServerConfigurationV154 )
242 {
243 return getTestingPortVersion154( ( ServerConfigurationV154 ) configuration );
244 }
245 else if ( configuration instanceof ServerConfigurationV153 )
246 {
247 return getTestingPortVersion153( ( ServerConfigurationV153 ) configuration );
248 }
249 else
250 {
251 return 0;
252 }
253 }
254
255
256 /**
257 * Gets the testing port.
258 *
259 * @param configuration
260 * the 1.5.3 server configuration
261 * @return
262 * the testing port
263 */
264 private int getTestingPortVersion153( ServerConfigurationV153 configuration )
265 {
266 // LDAP
267 if ( configuration.isEnableLdap() )
268 {
269 return configuration.getLdapPort();
270 }
271 // LDAPS
272 else if ( configuration.isEnableLdaps() )
273 {
274 return configuration.getLdapsPort();
275 }
276 // Kerberos
277 else if ( configuration.isEnableKerberos() )
278 {
279 return configuration.getKerberosPort();
280 }
281 // DNS
282 else if ( configuration.isEnableDns() )
283 {
284 return configuration.getDnsPort();
285 }
286 // NTP
287 else if ( configuration.isEnableNtp() )
288 {
289 return configuration.getNtpPort();
290 }
291 // ChangePassword
292 else if ( configuration.isEnableChangePassword() )
293 {
294 return configuration.getChangePasswordPort();
295 }
296 else
297 {
298 return 0;
299 }
300 }
301
302
303 /**
304 * Gets the testing port.
305 *
306 * @param configuration
307 * the 1.5.4 server configuration
308 * @return
309 * the testing port
310 */
311 private int getTestingPortVersion154( ServerConfigurationV154 configuration )
312 {
313 // LDAP
314 if ( configuration.isEnableLdap() )
315 {
316 return configuration.getLdapPort();
317 }
318 // LDAPS
319 else if ( configuration.isEnableLdaps() )
320 {
321 return configuration.getLdapsPort();
322 }
323 // Kerberos
324 else if ( configuration.isEnableKerberos() )
325 {
326 return configuration.getKerberosPort();
327 }
328 // DNS
329 else if ( configuration.isEnableDns() )
330 {
331 return configuration.getDnsPort();
332 }
333 // NTP
334 else if ( configuration.isEnableNtp() )
335 {
336 return configuration.getNtpPort();
337 }
338 // ChangePassword
339 else if ( configuration.isEnableChangePassword() )
340 {
341 return configuration.getChangePasswordPort();
342 }
343 else
344 {
345 return 0;
346 }
347 }
348
349
350 /**
351 * Gets the testing port.
352 *
353 * @param configuration
354 * the 1.5.5 server configuration
355 * @return
356 * the testing port
357 */
358 private int getTestingPortVersion155( ServerConfigurationV155 configuration )
359 {
360 // LDAP
361 if ( configuration.isEnableLdap() )
362 {
363 return configuration.getLdapPort();
364 }
365 // LDAPS
366 else if ( configuration.isEnableLdaps() )
367 {
368 return configuration.getLdapsPort();
369 }
370 // Kerberos
371 else if ( configuration.isEnableKerberos() )
372 {
373 return configuration.getKerberosPort();
374 }
375 // DNS
376 else if ( configuration.isEnableDns() )
377 {
378 return configuration.getDnsPort();
379 }
380 // NTP
381 else if ( configuration.isEnableNtp() )
382 {
383 return configuration.getNtpPort();
384 }
385 // ChangePassword
386 else if ( configuration.isEnableChangePassword() )
387 {
388 return configuration.getChangePasswordPort();
389 }
390 else
391 {
392 return 0;
393 }
394 }
395 };
396
397 // Starting the thread
398 thread.start();
399 }
400
401
402 /**
403 * Writes the given message to the Info console message stream.
404 *
405 * @param message
406 * the message
407 */
408 private void writeToInfoConsoleMessageStream( final String message )
409 {
410 Display.getDefault().asyncExec( new Runnable()
411 {
412 public void run()
413 {
414 LogMessageConsole console = ConsolesHandler.getDefault().getLogMessageConsole( server.getId() );
415 try
416 {
417 console.getInfoConsoleMessageStream().write( message );
418 }
419 catch ( IOException e )
420 {
421 ApacheDsPluginUtils.reportError( Messages.getString( "LaunchServerJob.ErrorWritingConsole" ) //$NON-NLS-1$
422 + e.getMessage() );
423 }
424 }
425 } );
426 }
427
428
429 /**
430 * Starting the "terminate" listener thread.
431 */
432 private void startTerminateListenerThread()
433 {
434 // Creating the thread
435 Thread thread = new Thread()
436 {
437 /** The debug event listener */
438 private IDebugEventSetListener debugEventSetListener;
439
440
441 public void run()
442 {
443 // Creating the listener
444 debugEventSetListener = new IDebugEventSetListener()
445 {
446 public void handleDebugEvents( DebugEvent[] events )
447 {
448 // Looping on the debug events array
449 for ( DebugEvent debugEvent : events )
450 {
451 // We only care of event with kind equals to
452 // 'terminate'
453 if ( debugEvent.getKind() == DebugEvent.TERMINATE )
454 {
455 // Getting the source of the debug event
456 Object source = debugEvent.getSource();
457 if ( source instanceof RuntimeProcess )
458 {
459 RuntimeProcess runtimeProcess = ( RuntimeProcess ) source;
460
461 // Getting the associated launch
462 ILaunch debugEventLaunch = runtimeProcess.getLaunch();
463 if ( debugEventLaunch.equals( launch ) )
464 {
465 // The launch we had created is now terminated
466 // The server is now stopped
467 server.setState( ServerStateEnum.STOPPED );
468
469 // Removing the listener
470 DebugPlugin.getDefault().removeDebugEventListener( debugEventSetListener );
471 }
472 }
473 }
474 }
475 }
476 };
477
478 // Adding the listener
479 DebugPlugin.getDefault().addDebugEventListener( debugEventSetListener );
480 }
481 };
482
483 // Starting the thread
484 thread.start();
485 }
486
487
488 /**
489 * Launches a Log4J {@link SocketServer} which will be used to redirect the
490 * logs of Apache DS to the console.
491 *
492 * @param port
493 * the port
494 * @param
495 *
496 */
497 private void launchSocketServer( int port )
498 {
499 final int finalPort = port;
500 final IPath serverSocketFolderPath = ApacheDsPluginUtils.getApacheDsServersFolder().append( server.getId() )
501 .append( "serverSocket" ); //$NON-NLS-1$
502 final IPath log4jPropertiesFilePath = serverSocketFolderPath.append( "log4j.properties" ); //$NON-NLS-1$
503
504 // Creating a new thread for the SocketServer
505 Thread thread = new Thread()
506 {
507 public void run()
508 {
509 SocketServer.main( new String[]
510 { "" + finalPort, log4jPropertiesFilePath.toOSString(), serverSocketFolderPath.toOSString() } ); //$NON-NLS-1$
511 }
512 };
513
514 // Launching the SocketServer
515 thread.start();
516 }
517
518
519 /**
520 * Overwrites the log4j.properties file of the server with the given port
521 * number.
522 *
523 * @param port
524 * the port
525 * @throws IOException
526 */
527 private void overwriteServersLog4jPropertiesFile( int port ) throws IOException
528 {
529 IPath confFolderPath = ApacheDsPluginUtils.getApacheDsServersFolder().append( server.getId() ).append( "conf" ); //$NON-NLS-1$
530 File confFolder = new File( confFolderPath.toOSString() );
531 ApacheDsPluginUtils.createServersLog4jPropertiesFile( new FileOutputStream( new File( confFolder,
532 "log4j.properties" ) ), port, logsLevel, logsPattern ); //$NON-NLS-1$
533 }
534
535
536 /**
537 * Launches Apache DS using a launch configuration.
538 */
539 private void launchApacheDS()
540 {
541 try
542 {
543 // Getting the default VM installation
544 IVMInstall vmInstall = JavaRuntime.getDefaultVMInstall();
545
546 // Creating a new editable launch configuration
547 ILaunchConfigurationType type = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationType(
548 IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION );
549 ILaunchConfigurationWorkingCopy workingCopy = type.newInstance( null, NLS.bind( Messages
550 .getString( "LaunchServerJob.StartingServer" ), new String[] { server.getName() } ) ); //$NON-NLS-1$
551
552 // Setting the JRE container path attribute
553 workingCopy.setAttribute( IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, vmInstall
554 .getInstallLocation().toString() );
555
556 // Setting the main type attribute
557 workingCopy.setAttribute( IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME,
558 "org.apache.directory.studio.apacheds.Launcher" ); //$NON-NLS-1$
559
560 // Creating the classpath list
561 List<String> classpath = new ArrayList<String>();
562 IPath apacheDsLibrariesFolder = ApacheDsPluginUtils.getApacheDsLibrariesFolder( server );
563 for ( String library : ApacheDsPluginUtils.getApacheDsLibraries( server ) )
564 {
565 IRuntimeClasspathEntry libraryClasspathEntry = JavaRuntime
566 .newArchiveRuntimeClasspathEntry( apacheDsLibrariesFolder.append( library ) );
567 libraryClasspathEntry.setClasspathProperty( IRuntimeClasspathEntry.USER_CLASSES );
568
569 classpath.add( libraryClasspathEntry.getMemento() );
570 }
571
572 // Setting the classpath type attribute
573 workingCopy.setAttribute( IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, classpath );
574
575 // Setting the default classpath type attribute to false
576 workingCopy.setAttribute( IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false );
577
578 // The server folder path
579 IPath serverFolderPath = ApacheDsPluginUtils.getApacheDsServersFolder().append( server.getId() );
580
581 // Setting the program arguments attribute
582 workingCopy.setAttribute( IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, "\"" //$NON-NLS-1$
583 + serverFolderPath.toOSString() + "\"" ); //$NON-NLS-1$
584
585 // Creating the VM arguments string
586 StringBuffer vmArguments = new StringBuffer();
587 vmArguments.append( "-Dlog4j.configuration=file:\"" //$NON-NLS-1$
588 + serverFolderPath.append( "conf" ).append( "log4j.properties" ).toOSString() + "\"" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
589 vmArguments.append( " " ); //$NON-NLS-1$
590 vmArguments.append( "-Dapacheds.var.dir=\"" + serverFolderPath.toOSString() + "\"" ); //$NON-NLS-1$ //$NON-NLS-2$
591 vmArguments.append( " " ); //$NON-NLS-1$
592 vmArguments.append( "-Dapacheds.log.dir=\"" + serverFolderPath.append( "log" ).toOSString() + "\"" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
593 vmArguments.append( " " ); //$NON-NLS-1$
594 vmArguments.append( "-Dapacheds.run.dir=\"" + serverFolderPath.append( "run" ).toOSString() + "\"" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
595 vmArguments.append( " " ); //$NON-NLS-1$
596 vmArguments.append( "-Dapacheds.instance=\"" + server.getName() + "\"" ); //$NON-NLS-1$ //$NON-NLS-2$
597
598 // Setting the VM arguments attribute
599 workingCopy.setAttribute( IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, vmArguments.toString() );
600
601 // Setting the launch configuration as private
602 workingCopy.setAttribute( IDebugUIConstants.ATTR_PRIVATE, true );
603
604 // Indicating that we don't want any console to show up
605 workingCopy.setAttribute( DebugPlugin.ATTR_CAPTURE_OUTPUT, false );
606
607 // Saving the launch configuration
608 ILaunchConfiguration configuration = workingCopy.doSave();
609
610 // Launching the launch configuration
611 launch = configuration.launch( ILaunchManager.RUN_MODE, new NullProgressMonitor() );
612 }
613 catch ( CoreException e )
614 {
615 ApacheDsPluginUtils.reportError( Messages.getString( "LaunchServerJob.ErrorLaunching" ) + e.getMessage() ); //$NON-NLS-1$
616 }
617 }
618
619
620 /**
621 * Gets the associated launch.
622 *
623 * @return the associated launch
624 */
625 public ILaunch getLaunch()
626 {
627 return launch;
628 }
629
630
631 /**
632 * Sets the logs level.
633 *
634 * @param logsLevel
635 * the logs level
636 */
637 public void setLogsLevel( String logsLevel )
638 {
639 this.logsLevel = logsLevel;
640 }
641
642
643 /**
644 * Sets the logs pattern.
645 *
646 * @param logsPattern
647 * the logs pattern
648 */
649 public void setLogsPattern( String logsPattern )
650 {
651 this.logsPattern = logsPattern;
652 }
653 }