blob: 17a8da63cd7d1cc8e9c188480140e2a6566af8ef [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.tools;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import javax.naming.InvalidNameException;
import javax.naming.NamingException;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.directory.daemon.AvailablePortFinder;
import org.apache.directory.shared.asn1.ber.Asn1Decoder;
import org.apache.directory.shared.asn1.ber.IAsn1Container;
import org.apache.directory.shared.asn1.ber.tlv.TLVStateEnum;
import org.apache.directory.shared.asn1.codec.DecoderException;
import org.apache.directory.shared.asn1.codec.EncoderException;
import org.apache.directory.shared.ldap.codec.LdapDecoder;
import org.apache.directory.shared.ldap.codec.LdapMessageCodec;
import org.apache.directory.shared.ldap.codec.LdapMessageContainer;
import org.apache.directory.shared.ldap.codec.LdapResultCodec;
import org.apache.directory.shared.ldap.codec.add.AddRequestCodec;
import org.apache.directory.shared.ldap.codec.bind.BindRequestCodec;
import org.apache.directory.shared.ldap.codec.bind.BindResponseCodec;
import org.apache.directory.shared.ldap.codec.bind.LdapAuthentication;
import org.apache.directory.shared.ldap.codec.bind.SimpleAuthentication;
import org.apache.directory.shared.ldap.codec.del.DelRequestCodec;
import org.apache.directory.shared.ldap.codec.extended.ExtendedResponseCodec;
import org.apache.directory.shared.ldap.codec.modify.ModifyRequestCodec;
import org.apache.directory.shared.ldap.codec.modifyDn.ModifyDNRequestCodec;
import org.apache.directory.shared.ldap.codec.unbind.UnBindRequestCodec;
import org.apache.directory.shared.ldap.entry.Entry;
import org.apache.directory.shared.ldap.entry.EntryAttribute;
import org.apache.directory.shared.ldap.entry.Modification;
import org.apache.directory.shared.ldap.entry.Value;
import org.apache.directory.shared.ldap.ldif.ChangeType;
import org.apache.directory.shared.ldap.ldif.LdifEntry;
import org.apache.directory.shared.ldap.ldif.LdifReader;
import org.apache.directory.shared.ldap.message.ResultCodeEnum;
import org.apache.directory.shared.ldap.name.LdapDN;
import org.apache.directory.shared.ldap.name.Rdn;
import org.apache.directory.shared.ldap.util.StringTools;
/**
* A command to import data into a server. The data to be imported must be
* stored in a Ldif File, and they could be added entries or modified entries.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev: 406112 $
*/
public class ImportCommand extends ToolCommand
{
public static final String PORT_RANGE = "(" + AvailablePortFinder.MIN_PORT_NUMBER + ", "
+ AvailablePortFinder.MAX_PORT_NUMBER + ")";
private int port = 10389;
private String host = "localhost";
private String password = "secret";
private String user = "uid=admin,ou=system";
private String auth = "simple";
private File ldifFile;
private String logs;
private boolean ignoreErrors = false;
private static final int IMPORT_ERROR = -1;
private static final int IMPORT_SUCCESS = 0;
/**
* Socket used to connect to the server
*/
private SocketChannel channel;
private SocketAddress serverAddress;
private IAsn1Container ldapMessageContainer = new LdapMessageContainer();
private Asn1Decoder ldapDecoder = new LdapDecoder();
/**
* The constructor save the command's name into it's super class
*
*/
protected ImportCommand()
{
super( "import" );
}
/**
* Connect to the LDAP server through a socket and establish the Input and
* Output Streams. All the required information for the connection should be
* in the options from the command line, or the default values.
*
* @throws UnknownHostException
* The hostname or the Address of server could not be found
* @throws IOException
* There was a error opening or establishing the socket
*/
private void connect() throws UnknownHostException, IOException
{
serverAddress = new InetSocketAddress( host, port );
channel = SocketChannel.open( serverAddress );
channel.configureBlocking( true );
}
private void sendMessage( ByteBuffer bb ) throws IOException
{
channel.write( bb );
bb.clear();
}
private LdapMessageCodec readResponse( ByteBuffer bb ) throws IOException, DecoderException, NamingException
{
LdapMessageCodec messageResp = null;
while ( true )
{
int nbRead = channel.read( bb );
if ( nbRead == -1 )
{
break;
}
else
{
bb.flip();
// Decode the PDU
ldapDecoder.decode( bb, ldapMessageContainer );
if ( ldapMessageContainer.getState() == TLVStateEnum.PDU_DECODED )
{
messageResp = ( ( LdapMessageContainer ) ldapMessageContainer ).getLdapMessage();
if ( messageResp instanceof BindResponseCodec )
{
BindResponseCodec resp = ( ( LdapMessageContainer ) ldapMessageContainer ).getLdapMessage()
.getBindResponse();
if ( resp.getLdapResult().getResultCode() != ResultCodeEnum.SUCCESS )
{
System.out.println( "Error : " + resp.getLdapResult().getErrorMessage() );
}
}
else if ( messageResp instanceof ExtendedResponseCodec )
{
ExtendedResponseCodec resp = ( ( LdapMessageContainer ) ldapMessageContainer ).getLdapMessage()
.getExtendedResponse();
if ( resp.getLdapResult().getResultCode() != ResultCodeEnum.SUCCESS )
{
System.out.println( "Error : " + resp.getLdapResult().getErrorMessage() );
}
}
( ( LdapMessageContainer ) ldapMessageContainer ).clean();
break;
}
else
{
bb.flip();
}
}
}
return messageResp;
}
/**
* Send the entry to the encoder, then wait for a
* reponse from the LDAP server on the results of the operation.
*
* @param ldifEntry
* The entry to add
* @param msgId
* message id number
*/
private int addEntry( LdifEntry ldifEntry, int messageId ) throws IOException, DecoderException, InvalidNameException,
NamingException, EncoderException
{
AddRequestCodec addRequest = new AddRequestCodec();
String dn = ldifEntry.getDn().getName();
if ( isDebugEnabled() )
{
System.out.println( "Adding entry " + dn );
}
Entry entry = ldifEntry.getEntry();
addRequest.setEntryDn( new LdapDN( dn ) );
// Copy the attributes
for ( EntryAttribute attribute:entry )
{
addRequest.addAttributeType( attribute.getId() );
for ( Value<?> value: attribute )
{
addRequest.addAttributeValue( value );
}
}
LdapMessageCodec message = new LdapMessageCodec();
message.setProtocolOP( addRequest );
message.setMessageId( messageId );
// Encode and send the addRequest message
ByteBuffer bb = message.encode( null );
bb.flip();
sendMessage( bb );
bb.clear();
// Get the response
LdapMessageCodec response = readResponse( bb );
LdapResultCodec result = response.getAddResponse().getLdapResult();
if ( result.getResultCode() == ResultCodeEnum.SUCCESS )
{
if ( isDebugEnabled() )
{
System.out.println( "Add of Entry " + entry.getDn() + " was successful" );
}
return IMPORT_SUCCESS;
}
else
{
System.err.println( "Add of entry " + entry.getDn()
+ " failed for the following reasons provided by the server:\n" + result.getErrorMessage() );
return IMPORT_ERROR;
}
}
/**
* Send the entry to the encoder, then wait for a
* reponse from the LDAP server on the results of the operation.
*
* @param entry
* The entry to delete
* @param msgId
* message id number
*/
private int deleteEntry( LdifEntry entry, int messageId ) throws IOException, DecoderException,
InvalidNameException, NamingException, EncoderException
{
DelRequestCodec delRequest = new DelRequestCodec();
String dn = entry.getDn().getName();
if ( isDebugEnabled() )
{
System.out.println( "Deleting entry " + dn );
}
delRequest.setEntry( new LdapDN( dn ) );
LdapMessageCodec message = new LdapMessageCodec();
message.setProtocolOP( delRequest );
message.setMessageId( messageId );
// Encode and send the delete request
ByteBuffer bb = message.encode( null );
bb.flip();
sendMessage( bb );
bb.clear();
// Get the response
LdapMessageCodec response = readResponse( bb );
LdapResultCodec result = response.getDelResponse().getLdapResult();
if ( result.getResultCode() == ResultCodeEnum.SUCCESS )
{
if ( isDebugEnabled() )
{
System.out.println( "Delete of Entry " + entry.getDn() + " was successful" );
}
return IMPORT_SUCCESS;
}
else
{
System.err.println( "Delete of entry " + entry.getDn()
+ " failed for the following reasons provided by the server:\n" + result.getErrorMessage() );
return IMPORT_ERROR;
}
}
/**
* Send the entry to the encoder, then wait for a
* reponse from the LDAP server on the results of the operation.
*
* @param entry
* The entry to modify
* @param msgId
* message id number
*/
private int changeModRDNEntry( LdifEntry entry, int messageId ) throws IOException, DecoderException,
InvalidNameException, NamingException, EncoderException
{
ModifyDNRequestCodec modifyDNRequest = new ModifyDNRequestCodec();
String dn = entry.getDn().getName();
if ( isDebugEnabled() )
{
System.out.println( "Modify DN of entry " + dn );
}
modifyDNRequest.setEntry( new LdapDN( dn ) );
modifyDNRequest.setDeleteOldRDN( entry.isDeleteOldRdn() );
modifyDNRequest.setNewRDN( new Rdn( entry.getNewRdn() ) );
if ( StringTools.isEmpty( entry.getNewSuperior() ) == false )
{
modifyDNRequest.setNewSuperior( new LdapDN( entry.getNewSuperior() ) );
}
LdapMessageCodec message = new LdapMessageCodec();
message.setProtocolOP( modifyDNRequest );
message.setMessageId( messageId );
// Encode and send the delete request
ByteBuffer bb = message.encode( null );
bb.flip();
sendMessage( bb );
bb.clear();
// Get the response
LdapMessageCodec response = readResponse( bb );
LdapResultCodec result = response.getModifyDNResponse().getLdapResult();
if ( result.getResultCode() == ResultCodeEnum.SUCCESS )
{
if ( isDebugEnabled() )
{
System.out.println( "ModifyDn of Entry " + entry.getDn() + " was successful" );
}
return IMPORT_SUCCESS;
}
else
{
System.err.println( "ModifyDn of entry " + entry.getDn()
+ " failed for the following reasons provided by the server:\n" + result.getErrorMessage() );
return IMPORT_ERROR;
}
}
/**
* Send the entry to the encoder, then wait for a
* reponse from the LDAP server on the results of the operation.
*
* @param entry The entry to modify
* @param msgId message id number
*/
private int changeModifyEntry( LdifEntry entry, int messageId ) throws IOException, DecoderException,
InvalidNameException, NamingException, EncoderException
{
ModifyRequestCodec modifyRequest = new ModifyRequestCodec();
String dn = entry.getDn().getName();
if ( isDebugEnabled() )
{
System.out.println( "Modify of entry " + dn );
}
modifyRequest.setObject( new LdapDN( dn ) );
modifyRequest.initModifications();
for ( Modification modification: entry.getModificationItems() )
{
modifyRequest.setCurrentOperation( modification.getOperation() );
modifyRequest.addAttributeTypeAndValues( modification.getAttribute().getId() );
for ( Value<?> value:modification.getAttribute() )
{
modifyRequest.addAttributeValue( value );
}
}
LdapMessageCodec message = new LdapMessageCodec();
message.setProtocolOP( modifyRequest );
message.setMessageId( messageId );
// Encode and send the delete request
ByteBuffer bb = message.encode( null );
bb.flip();
sendMessage( bb );
bb.clear();
// Get the response
LdapMessageCodec response = readResponse( bb );
LdapResultCodec result = response.getModifyResponse().getLdapResult();
if ( result.getResultCode() == ResultCodeEnum.SUCCESS )
{
if ( isDebugEnabled() )
{
System.out.println( "Modify of Entry " + entry.getDn() + " was successful" );
}
return IMPORT_SUCCESS;
}
else
{
System.err.println( "Modify of entry " + entry.getDn()
+ " failed for the following reasons provided by the server:\n" + result.getErrorMessage() );
return IMPORT_ERROR;
}
}
/**
* Send the change operation to the encoder, then wait for a
* reponse from the LDAP server on the results of the operation.
*
* @param entry
* The entry to add
* @param msgId
* message id number
*/
private int changeEntry( LdifEntry entry, int messageId ) throws IOException, DecoderException,
InvalidNameException, NamingException, EncoderException
{
switch ( entry.getChangeType().getChangeType() )
{
case ChangeType.ADD_ORDINAL:
// No difference with the injection of new entries
return addEntry( entry, messageId );
case ChangeType.DELETE_ORDINAL:
return deleteEntry( entry, messageId );
case ChangeType.MODIFY_ORDINAL:
return changeModifyEntry( entry, messageId );
case ChangeType.MODDN_ORDINAL:
case ChangeType.MODRDN_ORDINAL:
return changeModRDNEntry( entry, messageId );
default:
return IMPORT_ERROR;
}
}
/**
* Bind to the ldap server
*
* @param messageId The message Id
*/
private void bind( int messageId ) throws NamingException, EncoderException, DecoderException, IOException
{
BindRequestCodec bindRequest = new BindRequestCodec();
LdapMessageCodec message = new LdapMessageCodec();
LdapAuthentication authentication = null;
if ( "simple".equals( auth ) )
{
authentication = new SimpleAuthentication();
( ( SimpleAuthentication ) authentication ).setSimple( StringTools.getBytesUtf8( password ) );
}
bindRequest.setAuthentication( authentication );
bindRequest.setName( new LdapDN( user ) );
bindRequest.setVersion( 3 );
message.setProtocolOP( bindRequest );
message.setMessageId( messageId );
// Encode and send the bind request
ByteBuffer bb = message.encode( null );
bb.flip();
connect();
sendMessage( bb );
bb.clear();
// Get the bind response
LdapMessageCodec response = readResponse( bb );
LdapResultCodec result = response.getBindResponse().getLdapResult();
if ( result.getResultCode() == ResultCodeEnum.SUCCESS )
{
if ( isDebugEnabled() )
{
System.out.println( "Binding of user " + user + " was successful" );
}
}
else
{
System.err.println( "Binding of user " + user
+ " failed for the following reasons provided by the server:\n" + result.getErrorMessage() );
System.exit( 1 );
}
}
/**
* Unbind from the server
*
* @param messageId
* The message Id
* @throws InvalidNameException
* @throws EncoderException
* @throws DecoderException
* @throws IOException
*/
private void unbind( int messageId ) throws InvalidNameException, EncoderException, DecoderException, IOException
{
UnBindRequestCodec unbindRequest = new UnBindRequestCodec();
LdapMessageCodec message = new LdapMessageCodec();
message.setProtocolOP( unbindRequest );
message.setMessageId( messageId );
ByteBuffer bb = message.encode( null );
bb.flip();
sendMessage( bb );
if ( isDebugEnabled() )
{
System.out.println( "Unbinding of user " + user + " was successful" );
}
}
/**
* Execute the command
*
* @param cmd
* The command to be executed
*/
public void execute( CommandLine cmd ) throws Exception
{
processOptions( cmd );
if ( isDebugEnabled() )
{
System.out.println( "Parameters for Ldif import request:" );
System.out.println( "port = " + port );
System.out.println( "host = " + host );
System.out.println( "user = " + user );
System.out.println( "auth type = " + auth );
System.out.println( "file = " + ldifFile );
System.out.println( "logs = " + logs );
}
int messageId = 0;
// Login to the server
bind( messageId++ );
if ( isDebugEnabled() )
{
System.out.println( "Connection to the server established.\n" + "Importing data ... " );
}
LdifReader ldifReader = new LdifReader( ldifFile );
if ( ldifReader.containsEntries() )
{
// Parse the file and inject every entry
long t0 = System.currentTimeMillis();
int nbAdd = 0;
for ( LdifEntry entry:ldifReader )
{
// Check if we have had some error, has next() does not throw any exception
if ( ldifReader.hasError() )
{
System.err
.println( "Found an error while persing an entry : " + ldifReader.getError().getMessage() );
if ( ignoreErrors == false )
{
unbind( messageId );
System.err.println( "Import failed..." );
System.exit( 1 );
}
}
if ( ( addEntry( entry, messageId++ ) == IMPORT_ERROR ) && ( ignoreErrors == false ) )
{
unbind( messageId );
System.err.println( "Import failed..." );
System.exit( 1 );
}
nbAdd++;
if ( nbAdd % 10 == 0 )
{
System.out.print( '.' );
}
if ( nbAdd % 500 == 0 )
{
System.out.println( nbAdd );
}
}
long t1 = System.currentTimeMillis();
System.out.println( "Done!" );
System.out.println( nbAdd + " entries added in " + ( ( t1 - t0 ) / 1000 ) + " seconds" );
}
else
{
// Parse the file and inject every modification
long t0 = System.currentTimeMillis();
int nbMod = 0;
for ( LdifEntry entry:ldifReader )
{
// Check if we have had some error, has next() does not throw any exception
if ( ldifReader.hasError() )
{
System.err
.println( "Found an error while persing an entry : " + ldifReader.getError().getMessage() );
if ( ignoreErrors == false )
{
unbind( messageId );
System.err.println( "Import failed..." );
System.exit( 1 );
}
}
if ( ( changeEntry( entry, messageId++ ) == IMPORT_ERROR ) && ( ignoreErrors == false ) )
{
unbind( messageId );
System.err.println( "Import failed..." );
System.exit( 1 );
}
nbMod++;
if ( nbMod % 10 == 0 )
{
System.out.print( '.' );
}
if ( nbMod % 500 == 0 )
{
System.out.println( nbMod );
}
}
long t1 = System.currentTimeMillis();
System.out.println( "Done!" );
System.out.println( nbMod + " entries changed in " + ( ( t1 - t0 ) / 1000 ) + " seconds" );
}
ldifReader.close();
// Logout to the server
unbind( messageId++ );
}
/**
* Read the command line and get the options : 'h' : host 'p' : port 'u' :
* user 'w' : password 'a' : authentication type 'i' : ignore errors 'f' :
* ldif file to import
*
* @param cmd
* The command line
*/
private void processOptions( CommandLine cmd )
{
if ( isDebugEnabled() )
{
System.out.println( "Processing options for launching diagnostic UI ..." );
}
// -------------------------------------------------------------------
// figure out the host value
// -------------------------------------------------------------------
if ( cmd.hasOption( 'h' ) )
{
host = cmd.getOptionValue( 'h' );
if ( isDebugEnabled() )
{
System.out.println( "ignore-errors overriden by -i option: true" );
}
}
else if ( isDebugEnabled() )
{
System.out.println( "ignore-errors set to default: false" );
}
// -------------------------------------------------------------------
// figure out and error check the port value
// -------------------------------------------------------------------
if ( cmd.hasOption( 'p' ) ) // - user provided port w/ -p takes
// precedence
{
String val = cmd.getOptionValue( 'p' );
try
{
port = Integer.parseInt( val );
}
catch ( NumberFormatException e )
{
System.err.println( "port value of '" + val + "' is not a number" );
System.exit( 1 );
}
if ( port > AvailablePortFinder.MAX_PORT_NUMBER )
{
System.err.println( "port value of '" + val + "' is larger than max port number: "
+ AvailablePortFinder.MAX_PORT_NUMBER );
System.exit( 1 );
}
else if ( port < AvailablePortFinder.MIN_PORT_NUMBER )
{
System.err.println( "port value of '" + val + "' is smaller than the minimum port number: "
+ AvailablePortFinder.MIN_PORT_NUMBER );
System.exit( 1 );
}
if ( isDebugEnabled() )
{
System.out.println( "port overriden by -p option: " + port );
}
}
else if ( getApacheDS() != null )
{
port = getApacheDS().getLdapServer().getPort();
if ( isDebugEnabled() )
{
System.out.println( "port overriden by server.xml configuration: " + port );
}
}
else if ( isDebugEnabled() )
{
System.out.println( "port set to default: " + port );
}
// -------------------------------------------------------------------
// figure out the user value
// -------------------------------------------------------------------
if ( cmd.hasOption( 'u' ) )
{
user = cmd.getOptionValue( 'u' );
if ( isDebugEnabled() )
{
System.out.println( "user overriden by -u option: " + user );
}
}
else if ( isDebugEnabled() )
{
System.out.println( "user set to default: " + user );
}
// -------------------------------------------------------------------
// figure out the password value
// -------------------------------------------------------------------
if ( cmd.hasOption( 'w' ) )
{
password = cmd.getOptionValue( 'w' );
if ( isDebugEnabled() )
{
System.out.println( "password overriden by -w option: " + password );
}
}
else if ( isDebugEnabled() )
{
System.out.println( "password set to default: " + password );
}
// -------------------------------------------------------------------
// figure out the authentication type
// -------------------------------------------------------------------
if ( cmd.hasOption( 'a' ) )
{
auth = cmd.getOptionValue( 'a' );
if ( isDebugEnabled() )
{
System.out.println( "authentication type overriden by -a option: " + auth );
}
}
else if ( isDebugEnabled() )
{
System.out.println( "authentication type set to default: " + auth );
}
// -------------------------------------------------------------------
// figure out the 'ignore-errors' flag
// -------------------------------------------------------------------
if ( cmd.hasOption( 'e' ) )
{
ignoreErrors = true;
if ( isDebugEnabled() )
{
System.out.println( "authentication type overriden by -a option: " + auth );
}
}
else if ( isDebugEnabled() )
{
System.out.println( "authentication type set to default: " + auth );
}
// -------------------------------------------------------------------
// figure out the ldif file to import
// -------------------------------------------------------------------
if ( cmd.hasOption( 'f' ) )
{
String ldifFileName = cmd.getOptionValue( 'f' );
ldifFile = new File( ldifFileName );
if ( ldifFile.exists() == false )
{
System.err.println( "ldif file '" + ldifFileName + "' does not exist" );
System.exit( 1 );
}
if ( ldifFile.canRead() == false )
{
System.err.println( "ldif file '" + ldifFileName + "' can't be read" );
System.exit( 1 );
}
if ( isDebugEnabled() )
{
try
{
System.out.println( "ldif file to import: " + ldifFile.getCanonicalPath() );
}
catch ( IOException ioe )
{
System.out.println( "ldif file to import: " + ldifFileName );
}
}
}
else
{
System.err.println( "ldif file name must be provided" );
System.exit( 1 );
}
}
public Options getOptions()
{
Options opts = new Options();
Option op = new Option( "h", "host", true, "server host: defaults to localhost" );
op.setRequired( false );
opts.addOption( op );
op = new Option( "p", "port", true, "server port: defaults to 10389 or server.xml specified port" );
op.setRequired( false );
opts.addOption( op );
op = new Option( "u", "user", true, "the user: default to uid=admin, ou=system" );
op.setRequired( false );
opts.addOption( op );
op = new Option( "w", "password", true, "the apacheds administrator's password: defaults to secret" );
op.setRequired( false );
opts.addOption( op );
op = new Option( "a", "auth", true, "the authentication mode: defaults to 'simple'" );
op.setRequired( false );
opts.addOption( op );
op = new Option( "f", "file", true, "the ldif file to import" );
op.setRequired( true );
opts.addOption( op );
op = new Option( "e", "ignore", false, "continue to process the file even if errors are encountered " );
op.setRequired( false );
opts.addOption( op );
return opts;
}
}