From 996f91f191f6fb87b5d1ea52c3f1b943f0764a9c Mon Sep 17 00:00:00 2001
From: ugaston <ugaston@localhost>
Date: Fri, 22 Jan 2010 17:52:57 +0000
Subject: [PATCH] Add EclReadAndPlay util

---
 opends/tests/staf-tests/shared/java/ldapjdk/Writer.java                 |  122 +++
 opends/tests/staf-tests/shared/java/ldapjdk/ImprovedLDAPConnection.java |  200 +++++
 opends/tests/staf-tests/shared/java/ldapjdk/CSN.java                    |   62 +
 opends/tests/staf-tests/shared/java/ldapjdk/Server.java                 |   48 +
 opends/tests/staf-tests/shared/java/ldapjdk/LDIF.java                   |  798 +++++++++++++++++++++++
 opends/tests/staf-tests/shared/java/ldapjdk/Reader.java                 |  277 ++++++++
 opends/tests/staf-tests/shared/java/ldapjdk/Change.java                 |  163 ++++
 opends/tests/staf-tests/shared/java/ldapjdk/EclReadAndPlay.java         |  334 +++++++++
 8 files changed, 2,004 insertions(+), 0 deletions(-)

diff --git a/opends/tests/staf-tests/shared/java/ldapjdk/CSN.java b/opends/tests/staf-tests/shared/java/ldapjdk/CSN.java
new file mode 100644
index 0000000..97b412d
--- /dev/null
+++ b/opends/tests/staf-tests/shared/java/ldapjdk/CSN.java
@@ -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);
+    }
+}
diff --git a/opends/tests/staf-tests/shared/java/ldapjdk/Change.java b/opends/tests/staf-tests/shared/java/ldapjdk/Change.java
new file mode 100644
index 0000000..dee13aa
--- /dev/null
+++ b/opends/tests/staf-tests/shared/java/ldapjdk/Change.java
@@ -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 +")");
+    }
+}
diff --git a/opends/tests/staf-tests/shared/java/ldapjdk/EclReadAndPlay.java b/opends/tests/staf-tests/shared/java/ldapjdk/EclReadAndPlay.java
new file mode 100644
index 0000000..b1b2ce0
--- /dev/null
+++ b/opends/tests/staf-tests/shared/java/ldapjdk/EclReadAndPlay.java
@@ -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() );
+        }
+    }
+}
diff --git a/opends/tests/staf-tests/shared/java/ldapjdk/ImprovedLDAPConnection.java b/opends/tests/staf-tests/shared/java/ldapjdk/ImprovedLDAPConnection.java
new file mode 100644
index 0000000..5932d52
--- /dev/null
+++ b/opends/tests/staf-tests/shared/java/ldapjdk/ImprovedLDAPConnection.java
@@ -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 + ")" );
+        }
+    }
+    
+}
+
+
diff --git a/opends/tests/staf-tests/shared/java/ldapjdk/LDIF.java b/opends/tests/staf-tests/shared/java/ldapjdk/LDIF.java
new file mode 100644
index 0000000..975a385
--- /dev/null
+++ b/opends/tests/staf-tests/shared/java/ldapjdk/LDIF.java
@@ -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 );
+    }
+}
diff --git a/opends/tests/staf-tests/shared/java/ldapjdk/Reader.java b/opends/tests/staf-tests/shared/java/ldapjdk/Reader.java
new file mode 100644
index 0000000..a23a04b
--- /dev/null
+++ b/opends/tests/staf-tests/shared/java/ldapjdk/Reader.java
@@ -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);
+        }
+    }
+
+}
diff --git a/opends/tests/staf-tests/shared/java/ldapjdk/Server.java b/opends/tests/staf-tests/shared/java/ldapjdk/Server.java
new file mode 100644
index 0000000..fc34c3c
--- /dev/null
+++ b/opends/tests/staf-tests/shared/java/ldapjdk/Server.java
@@ -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);
+    }
+}
diff --git a/opends/tests/staf-tests/shared/java/ldapjdk/Writer.java b/opends/tests/staf-tests/shared/java/ldapjdk/Writer.java
new file mode 100644
index 0000000..b322819
--- /dev/null
+++ b/opends/tests/staf-tests/shared/java/ldapjdk/Writer.java
@@ -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);
+        }
+    }
+       
+}

--
Gitblit v1.10.0