blob: 9c9048998721e7a0701a177ff3885541a5c1d8a1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.directory.server;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.UUID;
import org.apache.directory.api.util.Network;
import org.apache.directory.api.util.Strings;
import org.apache.directory.server.core.api.InstanceLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The command line main for the server.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class UberjarMain
{
/** A logger for this class */
private static final Logger LOG = LoggerFactory.getLogger( UberjarMain.class );
/** The key of the property use to specify the shutdown port */
private static final String PROPERTY_SHUTDOWN_PORT = "apacheds.shutdown.port";
/** The ApacheDS service */
private ApacheDsService service;
/**
* Takes a single argument, the path to the installation home, which
* contains the configuration to load with server startup settings.
*
* @param args the arguments
* @throws Exception If the startup failed
*/
public static void main( String[] args ) throws Exception
{
if ( ( args == null ) || ( args.length < 1 ) )
{
throw new IllegalArgumentException( "Instance directory argument is missing" );
}
String instanceDirectory = args[0];
Action action = ( args.length == 2 ) ? Action.fromString( args[1] ) : Action.START;
UberjarMain instance = new UberjarMain();
switch ( action )
{
case START :
// Starts the server
LOG.debug( "Starting runtime" );
instance.start( instanceDirectory );
break;
case STOP :
// Stops the server
LOG.debug( "Stopping runtime" );
InstanceLayout layout = new InstanceLayout( instanceDirectory );
try ( Socket socket = new Socket( Network.LOOPBACK, readShutdownPort( layout ) );
PrintWriter writer = new PrintWriter( socket.getOutputStream() ) )
{
writer.print( readShutdownPassword( layout ) );
}
break;
case REPAIR :
// Try to fix the JDBM database
LOG.debug( "Fixing the database runtime" );
instance.repair( instanceDirectory );
break;
default:
throw new IllegalArgumentException( "Unexpected action " + action );
}
LOG.trace( "Exiting main" );
}
private int getShutdownPort()
{
int shutdownPort = Integer.parseInt( System.getProperty( PROPERTY_SHUTDOWN_PORT, "0" ) );
if ( shutdownPort < 0 || ( shutdownPort > 0 && shutdownPort < 1024 ) || shutdownPort > 65536 )
{
throw new IllegalArgumentException( "Shutdown port [" + shutdownPort + "] is an illegal port number" );
}
return shutdownPort;
}
private static int readShutdownPort( InstanceLayout layout ) throws IOException
{
return Integer.parseInt( Strings.asciiBytesToString(
Files.readAllBytes( Paths.get( layout.getRunDirectory().getAbsolutePath(), ".shutdown.port" ) ) ) );
}
private static String readShutdownPassword( InstanceLayout layout ) throws IOException
{
return Strings.asciiBytesToString( Files.readAllBytes(
Paths.get( layout.getRunDirectory().getAbsolutePath(), ".shutdown.pwd" ) ) );
}
/**
* Try to start the databases
*
* @param instanceDirectory The directory containing the server instance
*/
public void start( String instanceDirectory )
{
InstanceLayout layout = new InstanceLayout( instanceDirectory );
// Creating ApacheDS service
service = new ApacheDsService();
// Initializing the service
try
{
LOG.info( "Starting the service." );
service.start( layout );
startShutdownListener( layout );
}
catch ( Exception e )
{
LOG.error( "Failed to start the service.", e );
stop();
System.exit( 1 );
}
}
/**
* Try to repair the databases
*
* @param instanceDirectory The directory containing the server instance
*/
public void repair( String instanceDirectory )
{
InstanceLayout layout = new InstanceLayout( instanceDirectory );
// Creating ApacheDS service
service = new ApacheDsService();
try
{
System.out.println( "Starting the service." );
// must start servers otherwise stop() won't work
service.start( layout, true );
// no need to start the shutdown listener
System.out.println( "Service started." );
}
catch ( Exception e )
{
return;
}
// Repairing the database
try
{
LOG.info( "Starting the service." );
service.repair( layout );
LOG.info( "Database repaired." );
}
catch ( Exception e )
{
LOG.error( "Failed to repair the database.", e );
stop();
System.exit( 1 );
}
// Stop the service
stop();
}
public void stop()
{
if ( service != null )
{
try
{
LOG.info( "Stopping the service." );
service.stop();
LOG.info( "Service stopped successfully." );
}
catch ( Exception e )
{
LOG.error( "Failed to start the service.", e );
System.exit( 1 );
}
}
}
/**
* Starts a thread that creates a ServerSocket which listens for shutdown command.
*
* @param layout the InstanceLayout
* @throws IOException
*/
private void startShutdownListener( final InstanceLayout layout ) throws IOException
{
final int shutdownPort = getShutdownPort();
final String shutdownPassword = writeShutdownPassword( layout, UUID.randomUUID().toString() );
new Thread( new Runnable()
{
@Override
public void run()
{
// bind to localhost only to prevent connections from outside the box
try ( ServerSocket shutdownSocket = new ServerSocket( shutdownPort, 1, Network.LOOPBACK ) )
{
writeShutdownPort( layout, shutdownSocket.getLocalPort() );
LOG.info( "Start the shutdown listener on port {}", shutdownSocket.getLocalPort() );
Socket socket;
while ( ( socket = shutdownSocket.accept() ) != null )
{
if ( shutdownPassword == null || shutdownPassword.isEmpty() )
{
stop();
break;
}
else
{
try
{
InputStreamReader reader = new InputStreamReader( socket.getInputStream() );
CharBuffer buffer = CharBuffer.allocate( 2048 );
while ( reader.read( buffer ) >= 0 )
{
// read till end of stream
}
buffer.flip();
String password = buffer.toString();
reader.close();
if ( shutdownPassword.equals( password ) )
{
stop();
break;
}
else
{
LOG.warn( "Illegal attempt to shutdown, incorrect password {}", password );
}
}
catch ( IOException e )
{
LOG.warn( "Failed to handle the shutdown request", e );
}
}
}
}
catch ( IOException e )
{
LOG.error( "Failed to start the shutdown listener, stopping the server", e );
stop();
}
}
} ).start();
}
private static String writeShutdownPassword( InstanceLayout layout, String password ) throws IOException
{
Files.write(
Paths.get( layout.getRunDirectory().getAbsolutePath(), ".shutdown.pwd" ),
password.getBytes( Charset.forName( "utf-8" ) ) );
return password;
}
private static int writeShutdownPort( InstanceLayout layout, int portNumber ) throws IOException
{
Files.write(
Paths.get( layout.getRunDirectory().getAbsolutePath(), ".shutdown.port" ),
Integer.toString( portNumber ).getBytes( Charset.forName( "utf-8" ) ) );
return portNumber;
}
private enum Action
{
START, STOP, REPAIR;
public static Action fromString( String actionString )
{
for ( Action action : values() )
{
if ( action.name().equalsIgnoreCase( actionString ) )
{
return action;
}
}
throw new IllegalArgumentException( "Unknown action " + actionString );
}
}
}