mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

ugaston
22.52.2010 996f91f191f6fb87b5d1ea52c3f1b943f0764a9c
Add EclReadAndPlay util
8 files added
2004 ■■■■■ changed files
opends/tests/staf-tests/shared/java/ldapjdk/CSN.java 62 ●●●●● patch | view | raw | blame | history
opends/tests/staf-tests/shared/java/ldapjdk/Change.java 163 ●●●●● patch | view | raw | blame | history
opends/tests/staf-tests/shared/java/ldapjdk/EclReadAndPlay.java 334 ●●●●● patch | view | raw | blame | history
opends/tests/staf-tests/shared/java/ldapjdk/ImprovedLDAPConnection.java 200 ●●●●● patch | view | raw | blame | history
opends/tests/staf-tests/shared/java/ldapjdk/LDIF.java 798 ●●●●● patch | view | raw | blame | history
opends/tests/staf-tests/shared/java/ldapjdk/Reader.java 277 ●●●●● patch | view | raw | blame | history
opends/tests/staf-tests/shared/java/ldapjdk/Server.java 48 ●●●●● patch | view | raw | blame | history
opends/tests/staf-tests/shared/java/ldapjdk/Writer.java 122 ●●●●● patch | view | raw | blame | history
opends/tests/staf-tests/shared/java/ldapjdk/CSN.java
New file
@@ -0,0 +1,62 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 */
class CSN implements Comparable<CSN> {
    String value;
    public CSN (String value) {
        this.value = value;
    }
    public int compareTo (CSN anotherCSN) {
        if (this.value.equals(anotherCSN.value))  // same CSN value
            return 0;
        Long i = Long.valueOf(this.value.substring(0,12),16 );
        Long l = Long.valueOf(anotherCSN.value.substring(0,12),16 );
        return(i.compareTo(l));
    }
    public boolean equals(Object anotherCSN) {
        if (this.value.equals(anotherCSN))
            return true;
        else
            return false;
    }
    public String getValue (){
        return(value);
    }
    public String toString (){
        return(value);
    }
}
opends/tests/staf-tests/shared/java/ldapjdk/Change.java
New file
@@ -0,0 +1,163 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 */
import netscape.ldap.*;
import netscape.ldap.util.*;
import java.util.ArrayList;
class Change {
    int changeNumber = 0;
    String changelogCookie = null;
    CSN csn;
    String type = "";
    String dn = "";
    ArrayList<String> changes = new ArrayList<String>(2);
    String change = "";
    String replicaIdentifier = null;
    String changeNumberValue = null;
    String nsUniqueId = "";
    boolean deleteOldRDN = false;
    String newRDN = null;
    String newSuperior = null;
    public Change(LDAPEntry entry) throws Exception {
        LDAPAttribute attr = entry.getAttribute("replicaIdentifier");
        if ( attr == null ) {
            throw new Exception("No value found  for replicaIdentifier");
        }
        replicaIdentifier = attr.getStringValueArray()[0];
        attr = entry.getAttribute("changeNumber");
        if ( attr == null ) {
            throw new Exception("No value found  for changeNumber");
        }
        changeNumberValue = attr.getStringValueArray()[0];
        changeNumber = Integer.parseInt(changeNumberValue);
        attr = entry.getAttribute("changelogCookie");
        if ( attr != null ) {
            changelogCookie = attr.getStringValueArray()[0];
        }
        attr = entry.getAttribute("replicationCSN");
        if ( attr == null ) {
            throw new Exception("No value found  for replicationCSN");
        }
        csn = new CSN(attr.getStringValueArray()[0]);
        attr = entry.getAttribute("targetDN");
        if ( attr == null ) {
            throw new Exception("No value found  for targetDN");
        }
        dn = attr.getStringValueArray()[0];
        attr = entry.getAttribute("changeType");
        if ( attr == null ) {
            throw new Exception("No value found  for changeType");
        }
        type = attr.getStringValueArray()[0];
//        attr = entry.getAttribute("targetUniqueId");
//        if ( attr == null ) {
//            throw new Exception("No value found  for targetUniqueId");
//        }
//        nsUniqueId=attr.getStringValueArray()[0];
        attr = entry.getAttribute("targetEntryUUID");
        if ( attr == null ) {
            throw new Exception("No value found  for targetEntryUUID");
        }
        nsUniqueId = attr.getStringValueArray()[0];
        // modrdn
        if ( type.equals("modrdn") ) {
            attr = entry.getAttribute("deleteOldRDN");
            if ( attr == null ) {
                throw new Exception("No value found  for deleteOldRDN");
            }
            deleteOldRDN = Boolean.getBoolean(attr.getStringValueArray()[0]);
            attr = entry.getAttribute("newRDN");
            if ( attr == null ) {
                throw new Exception("No value found  for newRDN");
            }
            newRDN = attr.getStringValueArray()[0];
            attr = entry.getAttribute("newSuperior");
            if ( attr != null ) {
                newSuperior=attr.getStringValueArray()[0];
            }
        }
        // Conflict
        attr = entry.getAttribute("changeHasReplFixupOp");
        if ( attr != null ) {
            change = attr.getStringValueArray()[0];
            if ( change.trim().endsWith("-") ) {
                change = change.substring(0, change.length()-3) + "\r\n";
            }
            String changeHasReplFixupOp=change.replaceFirst("targetDn", "dn") + "\r\n";
            // println ("INFO", "FixupOp (csn="+ csn+"):\n" + changeHasReplFixupOp);
            changes.add(changeHasReplFixupOp);
        }
        attr = entry.getAttribute("changes");
        if ( attr != null ) {
            change = attr.getStringValueArray()[0];
            if ( change.trim().endsWith("-") ) {
                change = change.substring(0, change.length()-3) + "\r\n";
            }
        }
        if ( type.equals("modify") && ( change.equals("") ) ) {
            throw new Exception("Attribute changes is empty - replicationCSN="+ csn);
            //EclReadAndPlay.accessOut.println (getDate() + "- WARNING: Ignore change csn=" + csn );
        }
        String myChange = "dn: " + dn + "\n" +
                        "changetype: " + type + "\n" +
                        change +"\n";
        changes.add(myChange);
        changes.trimToSize();
    }
    public String toString() {
        return ("change number " + changeNumber + " (csn="+csn +")");
    }
}
opends/tests/staf-tests/shared/java/ldapjdk/EclReadAndPlay.java
New file
@@ -0,0 +1,334 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 */
import netscape.ldap.*;
import netscape.ldap.util.*;
import java.util.*;
import java.io.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
class EclReadAndPlay {
    // dbPath --> stores files containing csn of identifiers
    static final String dbPath = "."; /*"db";*/
    // configPath --> stores masters config file
    static final String configPath = "."; /*"config";*/
    static final String mastersFilename = "masters";
    static final String logsPath = "."; /*"logs";*/
    static final String accessFilename = "access";
    // Maximum time (in milliseconds) without update being read from the master: 60 s
    static final int MAX_IDLE_TIME = 60000;
    // ECL "draft" mode -- Initial changeNumber
    static final int INITIAL_CHANGENUMBER = 1;
    // ECL "opends" mode -- Initial control value:
    // --control "1.3.6.1.4.1.26027.1.5.4:false:;" ==> first cookie: ";"
    static final String INITIAL_COOKIE = ";";
    static PrintWriter standardOut = null;
    static PrintWriter accessOut = null;
    static HashMap<String,CSN> RUV;
    static Object lock;
    static HashMap<String,File> files;
    static int nb_ops = 0;
    static int total_nb_ops = 0;
    static int nb_ignored = 0;
    static int changeNumber = 0;
    static int lastChangeNumber = 0;
    static String changelogCookie = null;
    static String lastExternalChangelogCookie = null;
    static int missingChanges = 0;
    static int lastMissingChanges = 1;
    static String eclMode = null;
    static int queueSize = 0;
    static String bindDn = null;
    static String bindPwd = null;
    static boolean displayMissingChanges = false;
    static String outputFilename = null;
    public static void main( String[] args )
   {
        FileWriter out = null;
        files = new HashMap<String,File>();
        // Load latest read CSN values from files in db/ directory
        File csnDir = new File(dbPath);
        RUV = new HashMap<String,CSN>();
        try {
            FilenameFilter csnFileFilter = new FilenameFilter() {
                public boolean accept(File dir, String name) {
                    return name.endsWith(".csn");
                }
            };
            File[] csnFiles = csnDir.listFiles(csnFileFilter);
            if ( csnFiles != null ) {
                for (File f: csnFiles) {
                    String csnFilename = f.getName();
                    String id =
                        csnFilename.substring(0, csnFilename.indexOf(".csn"));
                    BufferedReader in = new BufferedReader (new FileReader(f));
                    CSN mycsn = new CSN(in.readLine());
                    if ( mycsn.value == null )
                        mycsn = new CSN("00000000000000000000");
                    // System.out.println(files[i] + "\t" + mycsn);
                    RUV.put(id, mycsn);
                    files.put(id, f);
                }
            }
        } catch (IOException e) {
            println("ERROR", e.toString());
            e.printStackTrace();
            System.exit(1);
        }
        /********** Parse arguments **********/
        int masterN=0;
        String hostport = null;
        ArrayList<Server> masters = new ArrayList<Server>();
        for (int k = 0; k < args.length; k++) {
            String opt = args[k];
            String val = args[k+1];
            // ECL mode: "opends" or "draft"
            if (opt.equals("-m")) {
                eclMode = val;
            }
            // Queue size
            if (opt.equals("-q")) {
                queueSize = Integer.parseInt(val);
            }
            // Display missing changes?
            if (opt.equals("-x")) {
                if ( val.equals("true") )
                    displayMissingChanges = true;
                else
                    displayMissingChanges = false;
            }
            // Bind DN
            if (opt.equals("-D")) {
                bindDn = val;
                System.out.println(".......... bindDN: " + bindDn);
            }
            // Bind password
            if (opt.equals("-w")) {
                bindPwd = val;
                System.out.println(".......... bindPwd: " + bindPwd);
            }
            // Stand-alone server:port
            if (opt.equals("-s")) {
                hostport = val;
                System.out.println(".......... stand-alone server: " + hostport);
            }
            // Replicated masters
            if (opt.equals("-p")) {
                masters.add(new Server(val));
            }
            // Standard output file
            if (opt.equals("-o")) {
                outputFilename = val;
            }
            k++;
        } /* for() */
        if ( eclMode == null || queueSize == 0 || bindDn == null ||
             bindPwd == null || hostport == null || masters.size() == 0 ||
             outputFilename == null ) {
            System.out.println("usage: -m {draft|opends} -q {queue size} "
                               + "-x {(displayMissingChanges):true|false} "
                               + "-o {outputFilename} -D {bindD} -w {bindPwd} "
                               + "-s {standalone-host:port} "
                               + "-p {master1-host:port} "
                               + "-p {master2-host:port}...");
            System.exit(1);
        }
        /* try {
            File mastersFile= new File(configPath, mastersFilename);
            LineNumberReader in=new LineNumberReader (new FileReader(mastersFile) );
            String line;
            while ( in.ready() ) {
                line=in.readLine();
                masters.add(new Server(line));
            }
        } catch (IOException e) {
            println ("ERROR", e.toString());
            System.exit(1);
        } */
        masters.trimToSize();
        // System.out.println(masters);
        /*************************************/
        // Output file (logs are appended)
        try {
            standardOut = new PrintWriter(new BufferedWriter(new FileWriter( new File(outputFilename)) ) );
        } catch (IOException e) {
            println ("ERROR", e.toString() );
            e.printStackTrace();
            System.exit(1);
        }
        // Access log (data is appended)
        try {
            accessOut = new PrintWriter(new BufferedWriter(new FileWriter( new File(logsPath, accessFilename)) ) );
        } catch (IOException e) {
            println ("ERROR", e.toString() );
            e.printStackTrace();
            System.exit(1);
        }
        /********** Initialise reader/writer threads **********/
        // Create a bounded blocking queue of integers
        BlockingQueue<Change> queue = new ArrayBlockingQueue<Change>(queueSize);
        // Initialise reader thread --> read updates from replicated master
        Reader reader = new Reader(queue, masters);
        reader.start();
        // Initialise writer thread --> write updates onto stand-alone server
        Writer writer = new Writer(queue, hostport);
        writer.start();
        lock = new Object();
        synchronized (lock) {
            try {
              lock.wait();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        long start = System.currentTimeMillis();
        int i=0;
        while (true) {
            int loopPeriod = 10; /* 10 s */
            sleep(loopPeriod * 1000);
            total_nb_ops += nb_ops;
            long duration = ((System.currentTimeMillis() - start)/1000);
            println("INFO", "Replayed " + nb_ops/loopPeriod
                    + " ops/sec. (Avg = " + (total_nb_ops/duration)
                    + " ops/s, Total = " + total_nb_ops
                    + " , ignored = " + nb_ignored + " )");
            nb_ops = 0;
            if ( i++ == 3 && displayMissingChanges == true ) {
                if ( eclMode.equals("draft") ) {
                    missingChanges = lastChangeNumber - changeNumber;
                    float percentage = (lastMissingChanges - missingChanges);
                    println("INFO", "Current changeNumber = " + changeNumber
                            + ", lastChangeNumber = " + lastChangeNumber
                            + ", missing changes = " + missingChanges + "/"
                            + lastMissingChanges + " (" + percentage + ")");
                    lastMissingChanges = missingChanges;
                    if (lastMissingChanges == 0)
                        lastMissingChanges = 1;
                } else if ( eclMode.equals("opends") ) {
                    println("INFO", "Current changelogCookie = "
                            + changelogCookie
                            + ", lastExternalChangelogCookie = "
                            + lastExternalChangelogCookie);
                }
                i = 0;
            }
        }
    }
     public static void inc_ops(int c) {
        nb_ops++;
        changeNumber = c;
    }
     public static void inc_ops(String c) {
         nb_ops++;
         changelogCookie = c;
     }
    public static void inc_ignored(int c) {
        nb_ignored++;
        changeNumber = c;
    }
    public static void inc_ignored(String c) {
        nb_ignored++;
        changelogCookie = c;
    }
    public static String getDate() {
        // Initialize the today's date string
        String DATE_FORMAT = "yyyy/MM/dd:HH:mm:ss";
        java.text.SimpleDateFormat sdf =
            new java.text.SimpleDateFormat(DATE_FORMAT);
        Calendar c1 = Calendar.getInstance(); // today
        return("[" + sdf.format(c1.getTime()) + "]");
   }
   public static void println(String level, String msg) {
        standardOut.println (getDate() + " - " + level + ": " + msg );
   }
   public static void sleep(int time) {
        try {
            Thread.sleep(time);
        }
        catch ( InterruptedException e )
        {
             println( "ERROR" ,  e.toString() );
        }
    }
}
opends/tests/staf-tests/shared/java/ldapjdk/ImprovedLDAPConnection.java
New file
@@ -0,0 +1,200 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 */
import netscape.ldap.*;
import netscape.ldap.util.*;
import java.util.*;
import java.io.*;
public class ImprovedLDAPConnection extends LDAPConnection {
    public ImprovedLDAPConnection() {
        super();
    }
    public void apply (Change change) {
        for (String mychange: change.changes) {
            String mytype = null;
            // Parse LDIF content
            ByteArrayInputStream stream = new ByteArrayInputStream(mychange.getBytes());
            LDIF ldif = null;
            try {
                ldif = new LDIF(new DataInputStream(stream));
                LDIFContent content = ldif.nextRecord().getContent();
                //EclReadAndPlay.println ("DEBUG", "\n\nWriting the following update: \n" + content.toString() );
                switch (content.getType()) {
                    case LDIFContent.ADD_CONTENT:
                        mytype = "ADD";
                        content = ( LDIFAddContent ) content;
                        LDAPAttributeSet attrSet = new LDAPAttributeSet( ((LDIFAddContent)content).getAttributes());
                        // remove non-user-modifiable attributes:
                        // entryuuid, pwdchangedtime, creatorsname, createtimestamp
                        LDAPAttribute entryuuidAttr = attrSet.getAttribute("entryuuid");
                        if ( entryuuidAttr != null ) {
                            attrSet.remove("entryuuid");
                        }
                        LDAPAttribute pwdchangedAttr = attrSet.getAttribute("pwdChangedTime");
                        if ( entryuuidAttr != null ) {
                            attrSet.remove("pwdchangedtime");
                        }
                        LDAPAttribute creatorAttr = attrSet.getAttribute("creatorsname");
                        if ( creatorAttr != null ) {
                            attrSet.remove("creatorsname");
                        }
                        LDAPAttribute createtimeAttr = attrSet.getAttribute("createtimestamp");
                        if ( createtimeAttr != null ) {
                            attrSet.remove("createtimestamp");
                        }
                        LDAPEntry addEntry = new LDAPEntry ( change.dn, attrSet );
                        //EclReadAndPlay.println ("INFO", "********************* Entry: ************** \n" + addEntry + "\n******************\n" );
                        try {
                            this.add( addEntry );
                        }
                        catch( LDAPException e ) {
                            EclReadAndPlay.println("ERROR", "Cannot add entry \"" + change.dn + "\" (csn="
                                         + change.csn + ")" );
                            EclReadAndPlay.println("ERROR", e.toString() );
                            e.printStackTrace();
                            System.exit(1);
                        }
                        // replace the unique id
//                        LDAPAttribute myAttr = new LDAPAttribute ("nsuniqueid", change.nsUniqueId);
//                        LDAPAttribute myAttr = new LDAPAttribute ("entryuuid", change.nsUniqueId);
//                        LDAPModification mod = new LDAPModification ( LDAPModification.REPLACE, myAttr );
//                        try {
//                            this.modify( change.dn, mod );
//                        }
//                        catch( LDAPException e ) {
//                            EclReadAndPlay.println ("ERROR", "Cannot modify nsuniqueid of entry \""
//                                                    + change.dn + "\" (csn=" + change.csn + ")" );
//                            EclReadAndPlay.println ("ERROR", e.toString() );
//                            System.exit(1);
//                        }
                        //System.out.EclReadAndPlay.println( addEntry);
                        break;
                    case LDIFContent.MODIFICATION_CONTENT:
                        mytype="MOD";
                        LDAPModification[] mods = ((LDIFModifyContent)content).getModifications();
                        // remove modifiersname and modifytimestamp mods
                        boolean[] deleteItem = new boolean[mods.length];
                        int size = 0;
                        for (int i = 0 ; i < mods.length ; i++) {
                            LDAPAttribute modAttr = mods[i].getAttribute();
                            if ( modAttr.getBaseName().equalsIgnoreCase("modifiersname") ||
                                 modAttr.getBaseName().equalsIgnoreCase("modifytimestamp") ) {
                                // remove mods[i] from mods
                                deleteItem[i] = true;
                            } else {
                                deleteItem[i] = false;
                                size++;
                            }
                        }
                        LDAPModification[] realMods = new LDAPModification[size];
                        int index = 0;
                        for (int i = 0 ; i < mods.length ; i++) {
                            if ( !deleteItem[i] ) {
                                realMods[index++] = mods[i];
                            }
                        }
                        try {
                            this.modify( change.dn, realMods );
                        }
                        catch( LDAPException e ) {
                            EclReadAndPlay.println("ERROR", "Cannot modify entry \"" + change.dn
                                         + "\" (csn=" + change.csn + ")" );
                            EclReadAndPlay.println("DEBUG", "mods\"" + mods + "\"" );
                            EclReadAndPlay.println("ERROR", e.toString() );
                            e.printStackTrace();
                            System.exit(1);
                        }
                        break;
                    case LDIFContent.MODDN_CONTENT:
                        if ( change.newRDN == null ) { // => fixOP MODRDN
                            change.newRDN=((LDIFModDNContent)content).getRDN();
                            change.deleteOldRDN=((LDIFModDNContent)content).getDeleteOldRDN();
                            change.newSuperior=((LDIFModDNContent)content).getNewParent();
                        }
                        try {
                            if (change.newSuperior == null ) {
                                mytype="MODRDN";
                                this.rename( change.dn, change.newRDN, change.deleteOldRDN );
                            }
                            else {
                                mytype="MODDN";
                                this.rename( change.dn, change.newRDN, change.newSuperior, change.deleteOldRDN );
                            }
                        }
                        catch( LDAPException e ) {
                            EclReadAndPlay.println( "ERROR", "Cannot rename entry \"" + change.dn
                                          + "\" (csn=" + change.csn + ")" );
                            EclReadAndPlay.println( "ERROR", "newRDN =\"" + change.newRDN
                                          + "\" (deleteOldRDN=" + change.deleteOldRDN + ")" );
                            EclReadAndPlay.println( "ERROR", "change =\"" + mychange + ")" );
                            EclReadAndPlay.println( "ERROR", e.toString());
                            e.printStackTrace();
                            System.exit(1);
                        }
                        break;
                    case LDIFContent.DELETE_CONTENT:
                        mytype="DEL";
                        try {
                            this.delete( change.dn );
                        }
                        catch( LDAPException e ) {
                            EclReadAndPlay.println ("ERROR", "Cannot delete entry \"" + change.dn
                                          + "\" (csn=" + change.csn + ")" );
                            EclReadAndPlay.println( "ERROR", e.toString() );
                            e.printStackTrace();
                            System.exit(1);
                        }
                        break;
                    default:
                        EclReadAndPlay.println("ERROR", "Cannot parse change (type=" + content.getType()
                                     + "):\n" + mychange + "_");
                        mytype="Unknown";
                        break;
                }
            } catch ( IOException e ) {
                EclReadAndPlay.println( "ERROR" , e.toString() );
                e.printStackTrace();
                EclReadAndPlay.println( "ERROR" , change.toString() );
            }
            EclReadAndPlay.accessOut.println(EclReadAndPlay.getDate() + "- INFO: " + mytype + " \"" + change.dn
                                   + "\" (" + change.csn +" / " + change.changeNumber + ")" );
        }
    }
}
opends/tests/staf-tests/shared/java/ldapjdk/LDIF.java
New file
@@ -0,0 +1,798 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 */
import netscape.ldap.util.*;
import java.util.*;
import netscape.ldap.*;
import netscape.ldap.client.*;
import java.io.*;
import java.net.*;
/**
 * LDAP Data Interchange Format (LDIF) is a file format used to
 * import and export directory data from an LDAP server and to
 * describe a set of changes to be applied to data in a directory.
 * This format is described in the Internet draft
 * <A HREF="ftp://ftp.ietf.org/internet-drafts/draft-good-ldap-ldif-00.txt"
 * TARGET="_blank">The LDAP Data Interchange Format (LDIF) -
 * Technical Specification</A>.
 * <P>
 *
 * This class implements an LDIF file parser.  You can construct
 * an object of this class to parse data in LDIF format and
 * manipulate the data as individual <CODE>LDIFRecord</CODE> objects.
 * <P>
 *
 * @version 1.0
 * @see netscape.ldap.util.LDIFRecord
 */
public class LDIF implements Serializable {
    /**
     * Internal constants
     */
    private final static char COMMENT = '#';
    static final long serialVersionUID = -2710382547996750924L;
    /**
     * Constructs an <CODE>LDIF</CODE> object to parse the
     * LDAP data read from stdin.
     * @exception IOException An I/O error has occurred.
     */
    public LDIF() throws IOException {
        DataInputStream ds = new DataInputStream(System.in);
        BufferedReader d = new BufferedReader(new InputStreamReader(ds, "UTF8"));
        m_reader = new LineReader(d);
        m_source = "System.in";
        m_decoder = new MimeBase64Decoder();
    }
    /**
     * Constructs an <CODE>LDIF</CODE> object to parse the
     * LDIF data read from a specified file.
     * @param file the name of the LDIF file to parse
     * @exception IOException An I/O error has occurred.
     */
    public LDIF(String file) throws IOException {
        FileInputStream fs = new FileInputStream(file);
        DataInputStream ds = new DataInputStream(fs);
        BufferedReader d = new BufferedReader(new InputStreamReader(ds, "UTF8"));
        m_reader = new LineReader(d);
        m_source = file;
        m_decoder = new MimeBase64Decoder();
    }
    /**
     * Constructs an <CODE>LDIF</CODE> object to parse the
     * LDIF data read from an input stream.
     * @param dstThe input stream providing the LDIF data
     * @exception IOException An I/O error has occurred.
     */
    public LDIF(DataInputStream ds) throws IOException {
        BufferedReader d = new BufferedReader(new InputStreamReader(ds, "UTF8"));
        m_reader = new LineReader(d);
        m_source = ds.toString();
        m_decoder = new MimeBase64Decoder();
    }
    /**
     * Returns the next record in the LDIF data. You can call this
     * method repeatedly to iterate through all records in the LDIF data.
     * <P>
     *
     * @return the next record as an <CODE>LDIFRecord</CODE>
     * object or null if there are no more records.
     * @exception IOException An I/O error has occurred.
     * @see netscape.ldap.util.LDIFRecord
     */
    public LDIFRecord nextRecord() throws IOException {
        if ( m_done )
            return null;
        else
            return parse_ldif_record( m_reader );
    }
    /**
     * Parses ldif content. The list of attributes is
     * terminated by \r\n or '-'. This function is
     * also used to parse the attributes in modifications.
     * @param ds data input stream
     */
    private LDIFRecord parse_ldif_record(LineReader d)
          throws IOException {
        String line = null;
        String dn = null;
        Vector attrs = new Vector();
        LDIFRecord rec = null;
        // Skip past any blank lines
        while( ((line = d.readLine()) != null) &&
               (line.length() < 1) ) {
        }
        if (line == null) {
            return null;
        }
        if (line.toLowerCase().startsWith("version:")) {
            m_version = Integer.parseInt(
                line.substring("version:".length()).trim() );
            if ( m_version != 1 ) {
                throwLDIFException( "Unexpected " + line );
            }
            // Do the next record
            line = d.readLine();
            if ( (line != null) && (line.length() == 0) ) {
                // Skip the newline
                line = d.readLine();
            }
            if (line == null) {
                return null;
            }
        }
        if (!line.toLowerCase().startsWith("dn:"))
            throwLDIFException("expecting dn:");
        dn = line.substring(3).trim();
        if (dn.startsWith(":") && (dn.length() > 1)) {
            String substr = dn.substring(1).trim();
            dn = new String(getDecodedBytes(substr), "UTF8");
        }
        LDIFContent content = parse_ldif_content(d);
        rec = new LDIFRecord(dn, content);
        return rec;
    }
    /**
     * Parses ldif content. The list of attributes is
     * terminated by \r\n or '-'. This function is
     * also used to parse the attributes in modifications.
     * @param ds data input stream
     */
    private LDIFContent parse_ldif_content(LineReader d)
          throws IOException {
        String line = d.readLine();
        if ((line == null) || (line.length() < 1) || (line.equals("-"))) {
            // if this is empty line, then we're finished reading all
            // the info for the current entry
            if ((line != null) && (line.length() < 1)) {
                m_currEntryDone = true;
            }
            return null;
        }
        if (line.toLowerCase().startsWith("changetype:")) {
            /* handles (changerecord) */
            LDIFContent lc = null;
            String changetype = line.substring(11).trim();
            if (changetype.equals("modify")) {
                lc = parse_mod_spec(d);
            } else if (changetype.equals("add")) {
                lc = parse_add_spec(d);
            } else if (changetype.equals("delete")) {
                lc = parse_delete_spec(d);
            } else if (changetype.equals("moddn") ||
                       changetype.equals("modrdn")) {
                lc = parse_moddn_spec(d);
            } else {
                throwLDIFException("change type not supported");
            }
            return lc;
        }
        /* handles 1*(attrval-spec) */
        Hashtable ht = new Hashtable();
        String newtype = null;
        Object val = null;
        LDAPAttribute newAttr = null;
        Vector controlVector = null;
        /* Read lines until we're past the record */
        while( true ) {
            if (line.toLowerCase().startsWith("control:")) {
                if ( controlVector == null ) {
                    controlVector = new Vector();
                }
                controlVector.addElement( parse_control_spec( line ) );
            } else {
                /* An attribute */
                int len = line.length();
                if ( len < 1 ) {
                    break;
                }
                int idx = line.indexOf(':');
                /* Must have a colon */
                if (idx == -1)
                    throwLDIFException("no ':' found");
                /* attribute type */
                newtype = line.substring(0,idx).toLowerCase();
                val = "";
                /* Could be :: for binary */
                idx++;
                if ( len > idx ) {
                    if ( line.charAt(idx) == ':' ) {
                        idx++;
                        String substr = line.substring(idx).trim();
                        val = getDecodedBytes(substr);
                    } else if (line.charAt(idx) == '<') {
                        try {
                            URL url =
                                new URL(line.substring(idx+1).trim());
                            String filename = url.getFile();
                            val = getFileContent(filename);
                        } catch (MalformedURLException ex) {
                            throwLDIFException(
                                ex +
                                ": cannot construct url "+
                                line.substring(idx+1).trim());
                        }
                    } else {
                        val = line.substring(idx).trim();
                    }
                }
                /* Is there a previous value for this attribute? */
                newAttr = (LDAPAttribute)ht.get( newtype );
                if ( newAttr == null ) {
                    newAttr = new LDAPAttribute( newtype );
                }
                if ( val instanceof String ) {
                    newAttr.addValue( (String)val );
                } else {
                    newAttr.addValue( (byte[])val );
                }
                ht.put( newtype, newAttr );
            }
            line = d.readLine();
            if (line == null || (line.length() < 1) ||
                (line.equals("-"))) {
                if ((line != null) && (line.length() < 1)) {
                    m_currEntryDone = true;
                }
                break;
            }
        }
        LDIFAttributeContent ac = new LDIFAttributeContent();
        // Copy over the attributes to the record
        Enumeration en = ht.elements();
        while( en.hasMoreElements() ) {
            ac.addElement( (LDAPAttribute)en.nextElement() );
        }
        ht.clear();
        if( controlVector != null ) {
            LDAPControl[] controls =
                new LDAPControl[controlVector.size()];
            controlVector.copyInto( controls );
            ac.setControls( controls );
            controlVector.removeAllElements();
        }
        return ac;
    }
    private byte[] getDecodedBytes(String line) {
        ByteBuf inBuf = new ByteBuf(line);
        ByteBuf decodedBuf = new ByteBuf();
        /* Translate from base 64 */
        m_decoder.translate( inBuf, decodedBuf );
        return decodedBuf.toBytes();
    }
    private byte[] getFileContent(String url) throws IOException {
        StringTokenizer tokenizer = new StringTokenizer(url, "|");
        String filename = url;
        int num = tokenizer.countTokens();
        if (num == 2) {
            String token = (String)tokenizer.nextElement();
            int index = token.lastIndexOf("/");
            String drive = token.substring(index+1);
            token = (String)tokenizer.nextElement();
            token = token.replace('/', '\\');
            filename = drive+":"+token;
        }
        File file = new File(filename);
        byte[] b = new byte[(int)file.length()];
        FileInputStream fi = new FileInputStream(filename);
        fi.read(b);
        return b;
    }
    /**
     * Parses add content
     * @param ds data input stream
     */
    private LDIFAddContent parse_add_spec(LineReader d)
          throws IOException {
        LDIFAttributeContent ac = (LDIFAttributeContent)parse_ldif_content(d);
        if (m_currEntryDone)
          m_currEntryDone = false;
        LDAPAttribute attrs[] = ac.getAttributes();
        LDIFAddContent rc = new LDIFAddContent(attrs);
        LDAPControl[] controls = ac.getControls();
        if ( controls != null ) {
            rc.setControls( controls );
        }
        return rc;
    }
    /**
     * Parses delete content
     * @param ds data input stream
     */
    private LDIFDeleteContent parse_delete_spec(LineReader d)
          throws IOException {
        Vector controlVector = null;
        LDIFDeleteContent dc = new LDIFDeleteContent();
        String line = d.readLine();
        while( line != null && !line.equals("") ) {
            if (line.toLowerCase().startsWith("control:")) {
                if ( controlVector == null ) {
                    controlVector = new Vector();
                }
                controlVector.addElement( parse_control_spec( line ) );
            } else {
                throwLDIFException("invalid SEP" );
            }
            line = d.readLine();
        }
        if( controlVector != null ) {
            LDAPControl[] controls = new LDAPControl[controlVector.size()];
            controlVector.copyInto( controls );
            dc.setControls( controls );
            controlVector.removeAllElements();
        }
        return dc;
    }
    /**
     * Parses change modification.
     * @param ds data input stream
     */
    private LDIFModifyContent parse_mod_spec(LineReader d)
          throws IOException {
        Vector controlVector = null;
        String line = null;
        line = d.readLine();
        LDIFModifyContent mc = new LDIFModifyContent();
        do {
            int oper = -1;
            if (line.toLowerCase().startsWith("add:")) {
                oper = LDAPModification.ADD;
            } else if (line.toLowerCase().startsWith("delete:")) {
                oper = LDAPModification.DELETE;
            } else if (line.toLowerCase().startsWith("replace:")) {
                oper = LDAPModification.REPLACE;
            } else
                throwLDIFException("unknown modify type");
            LDIFAttributeContent ac =
                (LDIFAttributeContent)parse_ldif_content(d);
            if (ac != null) {
                LDAPAttribute attrs[] = ac.getAttributes();
                for (int i = 0; i < attrs.length; i++) {
                    LDAPModification mod = new LDAPModification(oper, attrs[i]);
                    mc.addElement( mod );
                }
                LDAPControl[] controls = ac.getControls();
                if ( controls != null ) {
                    if ( controlVector == null ) {
                        controlVector = new Vector();
                    }
                    for( int i = 0; i < controls.length; i++ ) {
                        controlVector.addElement( controls[i] );
                    }
                }
            // if there is no attrval-spec, go into the else statement
            } else {
                int index = line.indexOf(":");
                if (index == -1)
                    throwLDIFException("colon missing in "+line);
                String attrName = line.substring(index+1).trim();
                if (oper == LDAPModification.ADD)
                    throwLDIFException("add operation needs the value for attribute "+attrName);
                LDAPAttribute attr = new LDAPAttribute(attrName);
                LDAPModification mod = new LDAPModification(oper, attr);
                mc.addElement(mod);
            }
            if (m_currEntryDone) {
                m_currEntryDone = false;
                break;
            }
            line = d.readLine();
        } while (line != null && !line.equals(""));
        if( controlVector != null ) {
            LDAPControl[] controls = new LDAPControl[controlVector.size()];
            controlVector.copyInto( controls );
            mc.setControls( controls );
            controlVector.removeAllElements();
        }
        return mc;
    }
    /**
     * Parses moddn/modrdn modification.
     * @param d data input stream
     */
    private LDIFModDNContent parse_moddn_spec(LineReader d)
                  throws IOException {
        Vector controlVector = null;
        String line = null;
        line = d.readLine();
        LDIFModDNContent mc = new LDIFModDNContent();
        String val = null;
        do {
            if (line.toLowerCase().startsWith("newrdn:") &&
              (line.length() > ("newrdn:".length()+1))) {
                mc.setRDN(line.substring("newrdn:".length()).trim());
            } else if (line.toLowerCase().startsWith("deleteoldrdn:") &&
              (line.length() > ("deleteoldrdn:".length()+1))) {
                String str = line.substring("deleteoldrdn:".length()).trim();
                if (str.equals("0") || str.equalsIgnoreCase("false"))
                    mc.setDeleteOldRDN(false);
                else if (str.equals("1") || str.equalsIgnoreCase("true"))
                    mc.setDeleteOldRDN(true);
                else
                    throwLDIFException("Incorrect input for deleteOldRdn ");
            } else if (line.toLowerCase().startsWith("newsuperior:") &&
                       (line.length() > ("newsuperior:".length()+1))) {
                mc.setNewParent(line.substring(
                    "newsuperior:".length()).trim());
            } else if (line.toLowerCase().startsWith("newparent:") &&
                       (line.length() > ("newparent:".length()+1))) {
                mc.setNewParent(line.substring(
                    "newparent:".length()).trim());
            } else if (line.toLowerCase().startsWith("control:")) {
                if ( controlVector == null ) {
                    controlVector = new Vector();
                }
                controlVector.addElement( parse_control_spec( line ) );
            }
            line = d.readLine();
        } while (line != null && !line.equals(""));
        if( controlVector != null ) {
            LDAPControl[] controls = new LDAPControl[controlVector.size()];
            controlVector.copyInto( controls );
            mc.setControls( controls );
            controlVector.removeAllElements();
        }
        return mc;
    }
    /**
     * Parses the specification of a control<BR>
     *
     * A control looks line one of the following:
     *<BR>
     * control: 1.2.3.4.10.210
     *<BR>
     * control: 1.2.3.4.10.210 true
     *<BR>
     * control: 1.2.3.4.10.210 true: someASCIIvalue
     *<BR>
     * control: 1.2.3.4.10.210: someASCIIvalue
     *<BR>
     * control: 1.2.3.4.10.210 true:: 44GK44GM44GV44KP44KJ
     *<BR>
     * control: 1.2.3.4.10.210:: 44GK44GM44GV44KP44KJ
     *<BR>
     * control: 1.2.3.4.10.210 true:< file:///usr/local/directory/cont.dta
     *<BR>
     * control: 1.2.3.4.10.210:< file:///usr/local/directory/cont.dta
     *
     * @param line a line containing a control spec
     * @return a parsed control.
     * @exception IOException if the line could not be parsed
     */
    protected LDAPControl parse_control_spec( String line )
        throws IOException {
        boolean criticality = true;
        String OID;
        byte[] val = null;
        int len = line.length();
        int idx = line.indexOf(':') + 2;
        /* OID, must be present */
        if ( idx >= len ) {
            throwLDIFException("OID required for control");
        }
        line = line.substring(idx).trim();
        idx = line.indexOf(' ');
        if ( idx < 0 ) {
            OID = line;
        } else {
            /* Optional criticality */
            OID = line.substring(0, idx);
            line = line.substring(idx+1);
            idx = line.indexOf(':');
            String criticalVal;
            if (idx > 0) {
                criticalVal = line.substring(0, idx);
            } else {
                criticalVal = line;
            }
            if ( criticalVal.compareTo("true") == 0 ) {
                criticality = true;
            } else if ( criticalVal.compareTo("false") == 0 ) {
                criticality = false;
            } else {
                throwLDIFException(
                    "Criticality for control must be true" +
                    " or false, not " + criticalVal);
            }
            /* Optional value */
            if ( idx > 0 ) {
                /* Could be :: for binary */
                idx++;
                if ( line.length() > idx ) {
                    if ( line.charAt(idx) == ':' ) {
                        idx++;
                        line = line.substring(idx).trim();
                        val = getDecodedBytes(line);
                    } else if (line.charAt(idx) == '<') {
                        String urlString = line.substring(idx+1).trim();
                        try {
                            URL url = new URL(urlString);
                            String filename = url.getFile();
                            val = getFileContent(filename);
                        } catch (MalformedURLException ex) {
                            throwLDIFException(
                                ex + ": cannot construct url "+
                                urlString);
                        }
                    } else {
                        try {
                            val = line.substring(idx).trim().getBytes("UTF8");
                        } catch(Exception x) {
                        }
                    }
                }
            }
        }
        return new LDAPControl( OID, criticality, val );
    }
    /**
     * Returns true if all the bytes in the given array are valid for output as a
     * String according to the LDIF specification. If not, the array should
     * output base64-encoded.
     * @return <code>true</code> if all the bytes in the given array are valid for
     * output as a String according to the LDIF specification; otherwise,
     * <code>false</code>.
     */
    public static boolean isPrintable(byte[] b) {
        for( int i = b.length - 1; i >= 0; i-- ) {
            if ( (b[i] < ' ') || (b[i] > 127) ) {
                if ( b[i] != '\t' )
                    return false;
            }
        }
        return true;
    }
    /**
     * Outputs the String in LDIF line-continuation format. No line will be longer
     * than the given max. A continuation line starts with a single blank space.
     * @param pw the printer writer
     * @param value the given string being printed out
     * @param max the maximum characters allowed in the line
     */
    public static void breakString( PrintWriter pw, String value, int max) {
        int leftToGo = value.length();
        int written = 0;
        int maxChars = max;
        /* Limit to 77 characters per line */
        while( leftToGo > 0 ) {
            int toWrite = Math.min( maxChars, leftToGo );
            String s = value.substring( written, written+toWrite);
            if ( written != 0 ) {
                pw.print( " " + s );
            } else {
                pw.print( s );
                maxChars -= 1;
            }
            written += toWrite;
            leftToGo -= toWrite;
            /* Don't use pw.println, because it outputs an extra CR
               in Win32 */
            pw.print( '\n' );
        }
    }
    /**
     * Gets the version of LDIF used in the data.
     * @return version of LDIF used in the data.
     */
    public int getVersion() {
        return m_version;
    }
    /**
     * Gets the string representation of the
     * entire LDIF file.
     * @return the string representation of the entire LDIF data file.
     */
    public String toString() {
        return "LDIF {" + m_source + "}";
    }
    /**
     * Throws a LDIF file exception including the current line number.
     * @param msg Error message
     */
    protected void throwLDIFException(String msg)throws IOException {
        throw new IOException ("line " +
            (m_currLineNum-m_continuationLength) + ": " + msg);
    }
    /**
     * Internal variables
     */
    private int m_version = 1;
    private boolean m_done = false;
    private LineReader m_reader = null;
    private String m_source = null;
    private MimeBase64Decoder m_decoder = null;
    private boolean m_currEntryDone = false;
    private int m_currLineNum;
    private int m_continuationLength;
    /* Concatenate continuation lines, if present */
    class LineReader {
        LineReader( BufferedReader d ) {
            _d = d;
        }
        /**
         * Reads a non-comment line.
         * @return a string or null.
         */
        String readLine() throws IOException {
            String line = null;
            String result = null;
            int readCnt = 0, continuationLength = 0;
            do {
                /* Leftover line from last time? */
                if ( _next != null ) {
                    line = _next;
                    _next = null;
                } else {
                    line = _d.readLine();
                }
                if (line != null) {
                    readCnt++;
                    /* Empty line means end of record */
                    if( line.length() < 1 ) {
                        if ( result == null )
                            result = line;
                        else {
                            _next = line;
                            break;
                        }
                    } else if( line.charAt(0) == COMMENT ) {
                        /* Ignore comment lines */
                    } else if( line.charAt(0) != ' ' ) {
                        /* Not a continuation line */
                        if( result == null ) {
                            result = line;
                        } else {
                            _next = line;
                            break;
                        }
                    } else {
                        /* Continuation line */
                        if ( result == null ) {
                            m_currLineNum += readCnt;
                            throwLDIFException("continuation out of nowhere");
                        }
                        result += line.substring(1);
                        continuationLength++;
                    }
                } else {
                    /* End of file */
                    break;
                }
            } while ( true );
            m_done = ( line == null );
            m_currLineNum += readCnt;
            if (_next != null) {
                // read one line ahead
                m_currLineNum--;
            }
            m_continuationLength = continuationLength;
            return result;
        }
        private BufferedReader _d;
        String _next = null;
    }
    /**
     * Converts a byte array to a printable string following
     * the LDIF rules (encode in base64 if necessary)
     *
     * @param b the byte array to convert
     * @return a converted string which is printable.
     */
    public static String toPrintableString( byte[] b ) {
        String s = "";
        if (isPrintable(b)) {
            try {
                s = new String(b, "UTF8");
            } catch ( java.io.UnsupportedEncodingException e ) {
            }
        } else {
            ByteBuf inBuf = new ByteBuf( b, 0, b.length );
            ByteBuf encodedBuf = new ByteBuf();
            // Translate to base 64
            MimeBase64Encoder encoder = new MimeBase64Encoder();
            encoder.translate( inBuf, encodedBuf );
            int nBytes = encodedBuf.length();
            if ( nBytes > 0 ) {
                s = new String(encodedBuf.toBytes(), 0, nBytes);
            }
        }
        return s;
    }
    /**
     * Test driver - just reads and parses an LDIF file, printing
     * each record as interpreted
     *
     * @param args name of the LDIF file to parse
     */
    public static void main( String[] args ) {
        if ( args.length != 1 ) {
            System.out.println( "Usage: java LDIF <FILENAME>" );
            System.exit( 1 );
        }
        LDIF ldif = null;
        try {
            ldif = new LDIF( args[0] );
        } catch (Exception e) {
            System.err.println("Failed to read LDIF file " + args[0] +
                               ", " + e.toString());
            System.exit(1);
        }
        try {
            for( LDIFRecord rec = ldif.nextRecord();
                 rec != null; rec = ldif.nextRecord() ) {
                System.out.println( rec.toString() + '\n' );
            }
        } catch ( IOException ex ) {
            System.out.println( ex );
            System.exit( 1 );
        }
        System.exit( 0 );
    }
}
opends/tests/staf-tests/shared/java/ldapjdk/Reader.java
New file
@@ -0,0 +1,277 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 */
import netscape.ldap.*;
import netscape.ldap.util.*;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.io.*;
class Reader extends Thread {
    BlockingQueue<Change> queue;
    ArrayList<Server> masters;
    public Reader(BlockingQueue<Change> q, ArrayList<Server> masters) {
        this.queue = q;
        this.masters = masters;
    }
    public void run() {
        // master number in the array list
        int masterN = 0;
        // ECL "draft" mode index
        int changeNumber = 0;
        // ECL "opends" mode index
        String eclCookie = EclReadAndPlay.INITIAL_COOKIE;
        try {
            Server master = null;
            LDAPConnection masterConnection = new LDAPConnection();
            LDAPSearchResults results = null;
            LDAPEntry entry = null;
            LDAPAttribute attr = null;
            int idleTime = 0;
            while (true) {
                try {
                    master = masters.get(masterN);
                    // Connect to the Directory master
                    EclReadAndPlay.println("INFO", "Connecting to master " + master.host + ":" + master.port + " ......");
                    masterConnection.connect( master.host, master.port );
                    masterConnection.authenticate( 3, EclReadAndPlay.bindDn, EclReadAndPlay.bindPwd );
                    EclReadAndPlay.println("INFO", "...... Connected to master " + master.host + ":" + master.port );
                    // Set changenumber
                    // Try to retrieve the ECL index (changenumber|changelogcookie) of the last update read
                    // ---> use the CSN stored in the file under "db" directory
                    for ( CSN csn:  EclReadAndPlay.RUV.values() ) {
                        String filter = "(& (objectclass=changelogentry)(replicationCSN="
                                        + csn.getValue() + ") )";
                        results = masterConnection.search( "cn=changelog", LDAPv3.SCOPE_SUB, filter,
                                                           new String[] {"changeNumber", "changeLogCookie"} ,
                                                           false );
                        entry = results.next();
                        if ( EclReadAndPlay.eclMode.equals("draft") ) {
                            if ( entry != null ) {
                                attr = entry.getAttribute("changeNumber");
                                if (attr != null) {
                                    String changeNumberString = attr.getStringValueArray()[0];
                                    EclReadAndPlay.println("DEBUG", "Found changeNumber " + changeNumberString
                                                 + " for csn " + csn.getValue() );
                                    int c = Integer.parseInt(changeNumberString);
                                    if ( ( changeNumber == 0 ) || ( changeNumber > c ) ) {
                                        EclReadAndPlay.println("DEBUG", "Setting changeNumber to " + ++c );
                                        changeNumber = c;
                                    }
                                } else {
                                    EclReadAndPlay.println("WARNING", "Cannot find changenumber, setting it to 1");
                                    changeNumber = EclReadAndPlay.INITIAL_CHANGENUMBER;
                                }
                            } else {
                                EclReadAndPlay.println("WARNING", "Cannot find a changelog entry for csn " + csn );
                                EclReadAndPlay.println("WARNING", "Will start from the first changelog entry");
                                results = masterConnection.search( "", LDAPv3.SCOPE_BASE, "(objectclass=*)",
                                                                   new String[]{"firstChangeNumber"} , false );
                                entry = results.next();
                                attr = entry.getAttribute("firstChangeNumber");
                                if ( attr != null ) {
                                    String changeNumberString = attr.getStringValueArray()[0];
                                    EclReadAndPlay.println("DEBUG", "Found firstChangeNumber " + changeNumberString);
                                    int c = Integer.parseInt(changeNumberString);
                                    if ( ( changeNumber == 0 ) || ( changeNumber > c ) ) {
                                        EclReadAndPlay.println("DEBUG", "Setting changeNumber to " + c );
                                        changeNumber = c;
                                    }
                                } else {
                                    EclReadAndPlay.println("WARNING", "Cannot find firstChangeNumber, setting it to 1");
                                    changeNumber = EclReadAndPlay.INITIAL_CHANGENUMBER;
                                }
                            }
                        } else if ( EclReadAndPlay.eclMode.equals("opends") ) {
                            if ( entry != null ) {
                                attr = entry.getAttribute("changeLogCookie");
                                if ( attr!= null ) {
                                    eclCookie = attr.getStringValueArray()[0];
                                    EclReadAndPlay.println("DEBUG", "Found changeLogCookie " + eclCookie
                                                 + " for csn " + csn.getValue() );
                                } else {
                                    EclReadAndPlay.println("WARNING", "Cannot find a changelog entry for csn " + csn );
                                    EclReadAndPlay.println("WARNING", "Will start from the first changelog entry");
                                    eclCookie = EclReadAndPlay.INITIAL_COOKIE;
                                }
                            } else {
                                EclReadAndPlay.println("WARNING", "Cannot find a changelog entry for csn " + csn );
                                EclReadAndPlay.println("WARNING", "Will start from the first changelog entry");
                                eclCookie = EclReadAndPlay.INITIAL_COOKIE;
                            }
                        }
                    } /* for (CSN csn: ...) */
                    synchronized (EclReadAndPlay.lock) {
                        EclReadAndPlay.lock.notifyAll();
                    }
                    String[] attributes = new String[] {"replicationCSN", "replicaIdentifier", "targetDN",
                                                        "targetEntryUUID", "changeType", "changes",
                                                        "deleteOldRDN", "newRDN", "newSuperior",
                                                        "changeNumber", "changeHasReplFixupOp",
                                                        "changeLogCookie"};
                    while (idleTime < EclReadAndPlay.MAX_IDLE_TIME) {
                        if ( EclReadAndPlay.eclMode.equals("draft") ) {
                            int limit = changeNumber + (EclReadAndPlay.queueSize - 1);
                            String filter = "(& (changeNumber>=" + changeNumber + ")(changeNumber<="
                                            + limit + ") )";
                            EclReadAndPlay.println("DEBUG", "Getting changes " + changeNumber + " to " + limit);
                            results = masterConnection.search("cn=changelog", LDAPv3.SCOPE_SUB, filter,
                                                              attributes , false );
                        } else if ( EclReadAndPlay.eclMode.equals("opends") ) {
                            // --control "1.3.6.1.4.1.26027.1.5.4:false:;"
                            String filter = "changetype=*";
                            LDAPSearchConstraints controls = new LDAPSearchConstraints();
                            LDAPControl eclControl = new LDAPControl("1.3.6.1.4.1.26027.1.5.4", false,
                                                                     eclCookie.getBytes());
                            controls.setMaxResults(199);
                            controls.setServerControls(eclControl);
                            EclReadAndPlay.println("DEBUG", "Getting changes from cookie:  " + eclCookie);
                            results = masterConnection.search("cn=changelog", LDAPv3.SCOPE_SUB, filter,
                                                              attributes , false, controls );
                        }
                        if ( ! results.hasMoreElements() ) {
                            // No new change found in retrocl => sleep 100 ms.
                            sleep(100);
                            idleTime += 100;
                            EclReadAndPlay.println("DEBUG", "No new change found in ECL => have slept for 100ms");
                        } else {
                            idleTime = 0;
                            // Forward  all the results found to the application
                            while ( results.hasMoreElements() ) {
                                EclReadAndPlay.println("DEBUG", "Going through change entries found in the ECL.");
                                try {
                                   entry = results.next();
                                } catch (LDAPException ldapEx) {
                                   if ( ldapEx.getLDAPResultCode() == LDAPException.SIZE_LIMIT_EXCEEDED )
                                      continue;
                                   else
                                      throw ldapEx;
                                }
                                //EclReadAndPlay.println("DEBUG", "Changelog entry: " + entry.toString());
                                try {
                                    // Write the change in the queue
                                    Change change = new Change(entry);
                                    queue.put(change);
                                } catch (Exception e) {
                                    EclReadAndPlay.println("DEBUG", "Ignoring change " + entry.getDN() );
                                    if ( EclReadAndPlay.eclMode.equals("draft") )
                                        EclReadAndPlay.inc_ignored(changeNumber);
                                    else if ( EclReadAndPlay.eclMode.equals("opends") )
                                        EclReadAndPlay.inc_ignored(eclCookie);
                                }
                                if ( EclReadAndPlay.eclMode.equals("draft") ) {
                                    changeNumber++;
                                    EclReadAndPlay.println("DEBUG", "change=" + entry.getDN() + ", changenumber = "
                                                 + changeNumber + ", count =" + results.getCount());
                                } else if ( EclReadAndPlay.eclMode.equals("opends") ) {
                                    attr = entry.getAttribute("changeLogCookie");
                                    if ( attr != null ) {
                                       eclCookie = attr.getStringValueArray()[0];
                                       EclReadAndPlay.println ("DEBUG", " ECL cookie value ========>  " + eclCookie );
                                    }
                                }
                            } /* while (result.hasMoreElements()) */
                        }
                        if ( EclReadAndPlay.displayMissingChanges == true ) {
                            if ( EclReadAndPlay.eclMode.equals("draft") ) {
                                results = masterConnection.search( "", LDAPv3.SCOPE_BASE, "(objectclass=*)",
                                                                   new String[]{"lastChangeNumber"} , false );
                                entry = results.next();
                                attr = entry.getAttribute("lastChangeNumber");
                                if ( attr != null ) {
                                    EclReadAndPlay.lastChangeNumber = Integer.parseInt(attr.getStringValueArray()[0]);
                                }
                            } else if ( EclReadAndPlay.eclMode.equals("opends") ) {
                                results = masterConnection.search( "", LDAPv3.SCOPE_BASE, "(objectclass=*)",
                                        new String[]{"lastExternalChangelogCookie"} , false );
                                entry = results.next();
                                attr = entry.getAttribute("lastExternalChangelogCookie");
                                if ( attr != null ) {
                                    EclReadAndPlay.lastExternalChangelogCookie = attr.getStringValueArray()[0];
                                }
                            }
                        }
                    } /* while (idleTime <= EclReadAndPlay.MAX_IDLE_TIME) */
                    EclReadAndPlay.println("WARNING", "No new changes read in the ECL for " + Integer.toString(idleTime) +
                                 " milliseconds. ======> EXIT");
                    System.exit(0);
                } catch( LDAPException e ) {
                    int errorCode = e.getLDAPResultCode();
                    // if server is down => switch
                    if ( ( errorCode == 91 ) || ( errorCode == 81 ) || ( errorCode == 80 ) ) {
                        // clear the queue of changes
                        queue.clear();
                        EclReadAndPlay.println( "WARNING", "Connection lost to " + master.host + ":" + master.port + ".");
                        masterN = (masterN+1) % masters.size();
                    } else {
                        EclReadAndPlay.println( "ERROR" , e.toString() );
                        e.printStackTrace();
                        System.exit(1);
                    }
                }
            } /* while (true) */
        } catch (Exception e) {
             e.printStackTrace();
             System.exit(1);
        }
    }
}
opends/tests/staf-tests/shared/java/ldapjdk/Server.java
New file
@@ -0,0 +1,48 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 */
import java.util.*;
public class Server {
    public String host;
    public int port;
    public Server (String host, int port) {
        this.host=host;
        this.port=port;
    }
    public Server (String hostPort) {
        StringTokenizer st = new StringTokenizer(hostPort, ":");
        this.host=st.nextToken();
        this.port=Integer.parseInt(st.nextToken());
    }
    public String toString() {
        return (host + ":" + port);
    }
}
opends/tests/staf-tests/shared/java/ldapjdk/Writer.java
New file
@@ -0,0 +1,122 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 */
import netscape.ldap.*;
import netscape.ldap.util.*;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.io.*;
class Writer extends Thread {
    BlockingQueue<Change> q;
    String hostport;
    public Writer(BlockingQueue<Change> q, String hostport) {
        this.q = q;
        this.hostport = hostport;
    }
    public void run() {
        try {
            Server application = new Server( hostport );
            ImprovedLDAPConnection applicationConnection = new ImprovedLDAPConnection();
            // Connect to the stand-alone server
            EclReadAndPlay.println("INFO", "****** Connecting to application "
                         + application.host + ":" + application.port + " ......");
            applicationConnection.connect( application.host, application.port );
            applicationConnection.authenticate( 3, EclReadAndPlay.bindDn, EclReadAndPlay.bindPwd );
            EclReadAndPlay.println("INFO", "****** ...... Connected to application "
                         + application.host + ":" + application.port );
            while (true) {
                // Read change from the queue
                Change change = q.take();
                //EclReadAndPlay.println ("DEBUG", "Change read from the queue -----> : " + change.toString() );
                CSN RUVcsn=EclReadAndPlay.RUV.get(change.replicaIdentifier);
                if ( RUVcsn != null ) {
                    // if operation is not replicated
                    if ( change.csn == null )
                        continue;
                    if (change.csn.compareTo(RUVcsn) < 0) {
                        // EclReadAndPlay.println ("DEBUG", Integer.toHexString(i.intValue()) + " < " + Integer.toHexString(l.intValue()) );
                        EclReadAndPlay.println("DEBUG", "Operation " + change.changeNumberValue + " csn "
                                     +  change.csn + " has already been replayed");
                        continue;
                    }
                }
                try {
                    // Write change on stand-alone server
                    applicationConnection.apply(change);
                    // Write change CSN to file under "db" directory
                    File f;
                    if (EclReadAndPlay.files.containsKey(change.replicaIdentifier)) {
                        f = EclReadAndPlay.files.get(change.replicaIdentifier);
                        // f.renameTo(new File(EclReadAndPlay.dbPath, new String(change.replicaIdentifier+".tmp") ));
                    } else {
                        f = new File(EclReadAndPlay.dbPath, change.replicaIdentifier + ".csn");
                        EclReadAndPlay.files.put(change.replicaIdentifier,f);
                    }
                    FileWriter out = new FileWriter(f);
                    out.write(change.csn.value);
                    out.flush();
                    out.close();
                    EclReadAndPlay.RUV.put(change.replicaIdentifier,change.csn);
                    if ( EclReadAndPlay.eclMode.equals("draft") )
                        EclReadAndPlay.inc_ops(change.changeNumber);
                    else if ( EclReadAndPlay.eclMode.equals("opends") )
                        EclReadAndPlay.inc_ops(change.changelogCookie);
                    // Log a message for the written change on "logs/access" file
                    EclReadAndPlay.accessOut.println(EclReadAndPlay.getDate()
                                   + "- INFO: " + change.type + " \""
                                   + change.dn + "\" (" + change.csn +" / "
                                   + change.changeNumber + ")" );
                } catch (Exception e) {
                    EclReadAndPlay.println( "ERROR", e.toString() );
                    e.printStackTrace();
                    System.exit(1);
                }
                    //nb_changes++;
            }
        } catch (Exception e) {
             e.printStackTrace();
             System.exit(1);
        }
    }
}