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