river-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From peter_firmst...@apache.org
Subject svn commit: r1468119 [3/15] - in /river/jtsk/skunk/qa_refactor/trunk: qa/src/com/sun/jini/qa/harness/ qa/src/com/sun/jini/test/impl/mahalo/ qa/src/com/sun/jini/test/resources/ qa/src/com/sun/jini/test/share/ qa/src/com/sun/jini/test/spec/javaspace/conf...
Date Mon, 15 Apr 2013 15:26:46 GMT
Modified: river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mahalo/Job.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mahalo/Job.java?rev=1468119&r1=1468118&r2=1468119&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mahalo/Job.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mahalo/Job.java Mon Apr 15 15:26:44 2013
@@ -1,367 +1,349 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- * 
- *      http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.sun.jini.mahalo;
-
-import com.sun.jini.thread.TaskManager;
-import com.sun.jini.thread.WakeupManager;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * A <code>Job</code> manages the division of work for a problem
- * whose solution is obtained by assembling partial results to
- * original problem.
- *
- * @author Sun Microsystems, Inc.
- *
- */
-public abstract class Job {
-    private TaskManager pool;
-    private WakeupManager wm;
-    private int pending = -1;
-    Object[] results;
-    int[] attempts;
-    private Map tasks = new HashMap();  //used to maintain account
-					//of the tasks for which
-					//the job is responsible
-
-    static final Logger logger = TxnManagerImpl.participantLogger;
-    
-    /**
-     * Create the <code>Job</code> object giving it the
-     * <code>TaskManager</code> responsible for the pool of
-     * threads which perform the necessary work.
-     *
-     * @param pool the <code>TaskManager</code> which provides the threads
-     */
-    public Job(TaskManager pool, WakeupManager wm) {
-        this.wm = wm;
-	this.pool = pool;
-    }
-
-
-    /**
-     * Used by a task to do a piece of work and record the
-     * number of attempts.
-     *
-     * @param who The task which is performing the work
-     * @param param A parameter used in performing the work
-     */
-    boolean performWork(TaskManager.Task who, Object param)
-        throws JobException
-    {
-	Integer tmp = null;
-	
-	synchronized (tasks) {
-	    tmp = (Integer)tasks.get(who);
-	}
-
-	if (tmp == null)
-	    throw new UnknownTaskException();
-
-	int rank = tmp.intValue();
-
-	synchronized (attempts) {
-	    attempts[rank]++;
-	}
-
-	Object result = doWork(who, param);
-	if (result == null)
-	    return false;
-
-	try {
-	    reportDone(who, result);
-	} catch (UnknownTaskException e) {
-	} catch (PartialResultException e) {
-	} catch (JobException e) {
-	}
-
-	return true;
-    }
-
-
-    /**
-     * Given a <code>TaskManager.Task</code>, this method
-     * returns the current number of attempts it has made.
-     * 
-     * @param who The task for which the number of attempts
-     *		  is inquired
-     */
-    int attempt(TaskManager.Task who) throws JobException {
-	Integer tmp = null;
-
-	synchronized(tasks) {
-	    tmp = (Integer)tasks.get(who);
-	}
-
-	if (tmp == null)
-	    throw new UnknownTaskException();
-
-	int rank = tmp.intValue();
-
-	synchronized(attempts)  {
-	    return attempts[rank];
-	}
-    }
-
-
-
-    /**
-     * The work performed is implemented here.
-     * A null return value indicates failure
-     * while a non-null return value indicates
-     * success and contains the result.
-     * 
-     * @param who The task performing the work
-     * @param param A parameter used to do the work
-     *
-     */
-    abstract Object doWork(TaskManager.Task who, Object param)
-	throws JobException;
-
-    /**
-     * Create the tasks required to compute all of the
-     * <code>PartialResult</code> objects necessary for the
-     * solution to the original problem. 
-     */
-    abstract TaskManager.Task[] createTasks();
-
-
-    /**
-     * Schedules tasks for execution
-     */
-    public void scheduleTasks() {
-	TaskManager.Task[] tmp = createTasks();
-
-	if (tmp != null) {
-            if (logger.isLoggable(Level.FINEST)) {
-                logger.log(Level.FINEST,
-                    "Job:scheduleTasks with {0} tasks",
-                    Integer.valueOf(tmp.length));
-            }
-
-	    results = new Object[tmp.length];
-	    attempts = new int[tmp.length];
-	    setPending(tmp.length);
-
-	    for (int i = 0; i < tmp.length; i++) {
-
-		//Record the position if each
-		//task for later use when assembling
-		//the partial results
-
-		synchronized(tasks) {
-		    tasks.put(tmp[i],Integer.valueOf(i));
-		    pool.add(tmp[i]);
-                    if (logger.isLoggable(Level.FINEST)) {
-                        logger.log(Level.FINEST,
-                            "Job:scheduleTasks added {0} to thread pool",
-                            tmp[i]);
-                    }
-		    attempts[i] = 0;
-		}
-	    }
-	}
-    }
-
-
-    private synchronized void awaitPending(long waitFor) {
-	if (pending < 0)
-	    return;
-
-	if (pending == 0)
-	    return;
-
-	try {
-            if (logger.isLoggable(Level.FINEST)) {
-                logger.log(Level.FINEST,
-                    "Job:awaitPending waiting for {0} items",
-                    Integer.valueOf(pending));
-            }
-
-	    if (waitFor == Long.MAX_VALUE) {
-		while (pending > 0) {
-		    wait();
-                    if (logger.isLoggable(Level.FINEST)) {
-                        logger.log(Level.FINEST,
-                            "Job:awaitPending awoken");
-                    }
-		}
-	    } else {
-		//When waiting for a given amount of time,
-		//if notified, make sure that the desired
-		//wait time has actually transpired.
-
-		long start = System.currentTimeMillis();
-		long curr = start;
-
-		while ((pending > 0) && ((curr - start) < waitFor)) {
-                    wait(waitFor - (curr - start));
-		    curr = System.currentTimeMillis();
-		}
-	    }
-        } catch (InterruptedException ie) {
-        }
-    }
-
-    private synchronized void setPending(int num) {
-        pending = num;
-
-	if (pending <= 0) {
-            if (logger.isLoggable(Level.FINEST)) {
-                logger.log(Level.FINEST,
-                    "Job:setPending notifying, pending = {0}",
-		    Integer.valueOf(pending));
-            }
-	    notifyAll();
-	}
-    }
-
-    private synchronized void decrementPending() {
-	pending--;
-
-	if (pending <= 0) {
-            if (logger.isLoggable(Level.FINEST)) {
-                logger.log(Level.FINEST,
-                    "Job:decrementPending notifying, pending = {0}",
-		    Integer.valueOf(pending));
-            }
-	    notifyAll();
-	}
-    }
-
-
-    /**
-     * Returns a reference to the <code>TaskManager</code> which
-     * supplies the threads used to executed tasks created by
-     * this <code>Job</code>
-     */
-    protected TaskManager getPool() {
-	return pool;
-    }
-
-    /**
-     * Returns a reference to the <code>WakeupManager</code> which
-     * provides the scheduling of tasks created by
-     * this <code>Job</code>
-     */
-    protected WakeupManager getMgr() {
-	return wm;
-    }
-
-    /*
-     * Tasks which perform work on behalf of the <code>Job</code>
-     * report in that they are done using this method.
-     */
-    private void reportDone(TaskManager.Task who, Object param)
-	throws JobException
-    {
-	if (param == null)
-	    throw new NullPointerException("param must be non-null");
-
-	if (who == null)
-	    throw new NullPointerException("task must be non-null");
-
-	Integer position = null;
-	
-	synchronized(tasks) {
-	    position = (Integer) tasks.get(who);
-	}
-
-	if (position == null) 
-	    throw new UnknownTaskException();
-
-	synchronized(results) {
-	    if (results[position.intValue()] == null) {
-                if (logger.isLoggable(Level.FINEST)) {
-                    logger.log(Level.FINEST,
-                        "Job:reportDone who = {0}, param = {1}",
-		        new Object[] { who, param});
-                }
-	        results[position.intValue()] = param;
-	        decrementPending();
-	    } else {
-	        throw new PartialResultException("result already set");
-	    }
-	}
-    }
-
-
-    /**
-     * Check to see if the <code>Job</code> execution has
-     * completed.
-     *
-     * @param waitFor	The amount of time the caller is willing
-     *			to wait for the completion status to arrive.
-     */
-    public boolean isCompleted(long waitFor) throws JobException {
-	//If nothing has started, the
-	//task could not have completed.
-	//Less than zero means initial value
-	//and greater than zero means there
-	//are outstanding tasks. In each of
-	//these cases, the Job is not done.
-
-	awaitPending(waitFor);
-
-	synchronized(this) {
-	    if (pending == 0)
-		return true;
-
-	    if (pending < 0)
-		throw new JobNotStartedException("No jobs started");
-
-	    return false;
-	} 
-    }
-
-
-    /**
-     * Generate the solution to the original problem.
-     * The subclass decides how it computes the final
-     * outcome.
-     */
-    abstract Object computeResult() throws JobException;
-
-
-    /**
-     * Halt all of the work being performed  by
-     * the <code>Job</code>
-     */
-    public void stop() {
-	Set s = tasks.keySet();
-	Object[] vals = s.toArray();
-
-	//Remove and interrupt all tasks
-
-	for (int i = 0; i < vals.length; i++) {
-	    TaskManager.Task t = (TaskManager.Task) vals[i];
-	    pool.remove(t);
-	}
-
-	//Erase record of tasks, results and the
-	//counting mechanism
-
-	tasks = new HashMap();
-	setPending(-1);
-	results = null;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.sun.jini.mahalo;
+
+import com.sun.jini.thread.TaskManager;
+import com.sun.jini.thread.WakeupManager;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A <code>Job</code> manages the division of work for a problem
+ * whose solution is obtained by assembling partial results to
+ * original problem.
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ */
+abstract class Job {
+    private final TaskManager pool;
+    private final WakeupManager wm;
+    private final AtomicInteger pend;
+    final ConcurrentMap<Integer,Object> results;
+    volatile AtomicIntegerArray attempts = null;
+    private final ConcurrentMap<Object,Integer> tasks;  //used to maintain account
+					//of the tasks for which
+					//the job is responsible
+                                        // sync on tasks.
+
+    static final Logger logger = TxnManagerImpl.participantLogger;
+    
+    /**
+     * Create the <code>Job</code> object giving it the
+     * <code>TaskManager</code> responsible for the pool of
+     * threads which perform the necessary work.
+     *
+     * @param pool the <code>TaskManager</code> which provides the threads
+     */
+    public Job(TaskManager pool, WakeupManager wm) {
+        this.wm = wm;
+	this.pool = pool;
+        pend = new AtomicInteger(-1);
+        results = new ConcurrentHashMap<Integer,Object>();
+        tasks = new ConcurrentHashMap<Object,Integer>();
+    }
+
+
+    /**
+     * Used by a task to do a piece of work and record the
+     * number of attempts.
+     *
+     * @param who The task which is performing the work
+     * @param param A parameter used in performing the work
+     */
+    boolean performWork(TaskManager.Task who, Object param)
+        throws JobException
+    {
+	Integer tmp = tasks.get(who);
+	if (tmp == null) throw new UnknownTaskException("Task didn't belong to this job");
+	int rank = tmp.intValue();
+        attempts.incrementAndGet(rank);
+
+	Object r = doWork(who, param);
+        
+	if (r == null) return false;
+
+	try {
+	    reportDone(who, r);
+	} catch (UnknownTaskException e) {
+            logger.log(Level.FINER, "trouble reporting job completion", e);
+            e.printStackTrace(System.err);
+	} catch (PartialResultException e) {
+            logger.log(Level.FINER, "trouble reporting job completion", e);
+            e.printStackTrace(System.err);
+	} catch (JobException e) {
+            logger.log(Level.FINER, "trouble reporting job completion", e);
+            e.printStackTrace(System.err);
+	}
+
+	return true;
+    }
+
+
+    /**
+     * Given a <code>TaskManager.Task</code>, this method
+     * returns the current number of attempts it has made.
+     * 
+     * @param who The task for which the number of attempts
+     *		  is inquired
+     */
+    int attempt(TaskManager.Task who) throws JobException {
+	Integer tmp = tasks.get(who);
+	if (tmp == null) throw new UnknownTaskException();
+	int rank = tmp.intValue();
+        return attempts.get(rank);
+    }
+
+
+
+    /**
+     * The work performed is implemented here.
+     * A null return value indicates failure
+     * while a non-null return value indicates
+     * success and contains the result.
+     * 
+     * @param who The task performing the work
+     * @param param A parameter used to do the work
+     *
+     */
+    abstract Object doWork(TaskManager.Task who, Object param)
+	throws JobException;
+
+    /**
+     * Create the tasks required to compute all of the
+     * <code>PartialResult</code> objects necessary for the
+     * solution to the original problem. 
+     */
+    abstract TaskManager.Task[] createTasks();
+
+
+    /**
+     * Schedules tasks for execution
+     */
+    public void scheduleTasks() {
+	TaskManager.Task[] tmp = createTasks();
+        int length = tmp.length;
+	if (tmp != null) {
+            if (logger.isLoggable(Level.FINEST)) {
+                logger.log(Level.FINEST,
+                    "Job:scheduleTasks with {0} tasks",
+                    Integer.valueOf(length));
+            }
+ 
+            results.clear();
+            attempts = new AtomicIntegerArray(length);
+            setPending(length);
+            
+            for (int i = 0; i < length; i++) {
+
+		//Record the position if each
+		//task for later use when assembling
+		//the partial results
+                tasks.put(tmp[i],Integer.valueOf(i));
+                attempts.set(i,0);
+                pool.add(tmp[i]);
+                if (logger.isLoggable(Level.FINEST)) {
+                    logger.log(Level.FINEST,
+                        "Job:scheduleTasks added {0} to thread pool",
+                        tmp[i]);
+                }
+            }
+
+        }
+    }
+
+
+    private void awaitPending(long waitFor) {
+        if (pend.get() < 1) return; // 0 or -1
+
+	try {
+            if (logger.isLoggable(Level.FINEST)) {
+                logger.log(Level.FINEST,
+                    "Job:awaitPending waiting for {0} items",
+                    Integer.valueOf(pend.get()));
+            }
+
+	    if (waitFor == Long.MAX_VALUE) {
+		while (pend.get() > 0) {
+                    synchronized (this){
+                        wait();
+                    }
+                    if (logger.isLoggable(Level.FINEST)) {
+                        logger.log(Level.FINEST,
+                            "Job:awaitPending awoken");
+                    }
+		}
+	    } else {
+		//When waiting for a given amount of time,
+		//if notified, make sure that the desired
+		//wait time has actually transpired.
+
+		long start = System.currentTimeMillis();
+		long curr = start;
+
+		while ((pend.get() > 0) && ((curr - start) < waitFor)) {
+                    synchronized (this){
+                        wait(waitFor - (curr - start));
+                    }
+		    curr = System.currentTimeMillis();
+		}
+	    }
+        } catch (InterruptedException ie) {
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    private void setPending(int num) {
+        pend.set(num);
+
+	if (pend.get() <= 0) {
+            if (logger.isLoggable(Level.FINEST)) {
+                logger.log(Level.FINEST,
+                    "Job:setPending notifying, pending = {0}",
+		    Integer.valueOf(pend.get()));
+            }
+            synchronized (this){
+                notifyAll();
+            }
+	}
+    }
+
+    private void decrementPending() {
+        int pending = pend.decrementAndGet();
+
+	if (pending <= 0) {
+            if (logger.isLoggable(Level.FINEST)) {
+                logger.log(Level.FINEST,
+                    "Job:decrementPending notifying, pending = {0}",
+		    Integer.valueOf(pending));
+            }
+            synchronized (this){
+                notifyAll();
+            }
+	}
+    }
+
+
+    /**
+     * Returns a reference to the <code>TaskManager</code> which
+     * supplies the threads used to executed tasks created by
+     * this <code>Job</code>
+     */
+    protected TaskManager getPool() {
+	return pool;
+    }
+
+    /**
+     * Returns a reference to the <code>WakeupManager</code> which
+     * provides the scheduling of tasks created by
+     * this <code>Job</code>
+     */
+    protected WakeupManager getMgr() {
+	return wm;
+    }
+
+    /*
+     * Tasks which perform work on behalf of the <code>Job</code>
+     * report in that they are done using this method.
+     */
+    private void reportDone(TaskManager.Task who, Object param)
+	throws JobException
+    {
+	if (param == null) throw new NullPointerException("param must be non-null");
+	if (who == null) throw new NullPointerException("task must be non-null");
+
+	Integer position = tasks.get(who);
+	if (position == null) throw new UnknownTaskException();
+        
+        Object exists = results.putIfAbsent(position, param);
+        if (exists == null){
+            if (logger.isLoggable(Level.FINEST)) {
+                logger.log(Level.FINEST,
+                    "Job:reportDone who = {0}, param = {1}",
+                    new Object[] { who, param});
+            }
+            decrementPending();
+        } else {
+            throw new PartialResultException("result already set");
+        }
+    }
+
+
+    /**
+     * Check to see if the <code>Job</code> execution has
+     * completed.
+     *
+     * @param waitFor	The amount of time the caller is willing
+     *			to wait for the completion status to arrive.
+     */
+    public boolean isCompleted(long waitFor) throws JobException {
+	//If nothing has started, the
+	//task could not have completed.
+	//Less than zero means initial value
+	//and greater than zero means there
+	//are outstanding tasks. In each of
+	//these cases, the Job is not done.
+
+	awaitPending(waitFor);
+        int pending = pend.get();
+        if (pending == 0) return true;
+        if (pending < 0) throw new JobNotStartedException("No jobs started");
+        return false;
+    }
+
+
+    /**
+     * Generate the solution to the original problem.
+     * The subclass decides how it computes the final
+     * outcome.
+     */
+    abstract Object computeResult() throws JobException;
+
+
+    /**
+     * Halt all of the work being performed  by
+     * the <code>Job</code>
+     */
+    public void stop() {
+	Set s = tasks.keySet();
+        Object[] vals = s.toArray();
+        tasks.clear();
+
+	//Remove and interrupt all tasks
+        int l = vals.length;
+	for (int i = 0; i < l; i++) {
+	    TaskManager.Task t = (TaskManager.Task) vals[i];
+	    pool.remove(t);
+	}
+
+	//Erase record of tasks, results and the
+	//counting mechanism
+        setPending(-1);
+        results.clear();
+    }
+}

Modified: river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mahalo/JoinStateManager.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mahalo/JoinStateManager.java?rev=1468119&r1=1468118&r2=1468119&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mahalo/JoinStateManager.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/mahalo/JoinStateManager.java Mon Apr 15 15:26:44 2013
@@ -1,809 +1,809 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- * 
- *      http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.sun.jini.mahalo;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInput;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutput;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.rmi.MarshalledObject;
-import java.rmi.RemoteException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import net.jini.config.Configuration;
-import net.jini.config.ConfigurationException;
-import net.jini.core.discovery.LookupLocator;
-import net.jini.core.entry.Entry;
-import net.jini.core.lookup.ServiceID;
-import net.jini.discovery.DiscoveryManagement;
-import net.jini.discovery.DiscoveryLocatorManagement;
-import net.jini.discovery.DiscoveryGroupManagement;
-import net.jini.discovery.LookupDiscoveryManager;
-import net.jini.id.Uuid;
-import net.jini.lookup.JoinManager;
-import net.jini.lookup.ServiceIDListener;
-import net.jini.security.ProxyPreparer;
-
-
-import com.sun.jini.config.Config;
-import com.sun.jini.logging.Levels;
-import com.sun.jini.reliableLog.LogHandler;
-import com.sun.jini.reliableLog.ReliableLog;
-
-/**
- * <code>JoinStateManager</code> provides a utility that manages
- * a service's join state (optionally persisting that state) and
- * manages the join protocol protocol on behalf of the service.
- *
- * @author Sun Microsystems, Inc.
- *
- * @see net.jini.lookup.ServiceIDListener
- * @see com.sun.jini.reliableLog.LogHandler
- */
- 
-class JoinStateManager extends LogHandler {
-    /** Logger for logging initialization related messages */
-    private static final Logger initlogger = TxnManagerImpl.initLogger;
-
-    /** Logger for logging operations related messages */
-    private static final Logger operationsLogger = TxnManagerImpl.operationsLogger;
-
-     /** Logger for transaction persistence related messages */
-    private static final Logger persistenceLogger = TxnManagerImpl.persistenceLogger;
-
-    /** <code>ProxyPreparer</code> for <code>LookupLocators</code> */
-    private ProxyPreparer lookupLocatorPreparer;
-
-    /**
-     * Object used to find lookups. Has to implement DiscoveryManagement
-     * and DiscoveryLocatorManagement as well as DiscoveryGroupManagement.
-     */
-    private DiscoveryManagement dm;
-
-    /**
-     * <code>JoinManager</code> that is handling the details of binding
-     * into Jini lookup services.
-     */
-    private JoinManager  mgr;
-
-    /**
-     * The object coordinating our persistent state.
-     */
-    private ReliableLog log;
-
-    /**
-     * The join state, this data needs to be persisted between restarts
-     */
-    private Entry[]		attributes;
-    private LookupLocator[]	locators;
-    private String[]		groups;
-
-    /** Service's internal <code>Uuid</code> which needs to be persisted */
-    private Uuid		serviceUuid;
-    
-    /**
-     * Conceptually, true if this is the first time this
-     * service has come up, implemented as if there was
-     * no previous state then this is the first time.
-     */
-    private boolean initial = true;
-    
-    /**
-     * Simple constructor.
-     */
-    JoinStateManager(String logPath) throws IOException { 
-        super(); 
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "JoinStateManager", logPath);
-	}
-	this.log = (logPath==null) ? null : new ReliableLog(logPath, this);
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "JoinStateManager");
-	}
-    }
-    
-    void recover() throws IOException {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "recover");
-	}
-	if (log != null) log.recover();
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "recover");
-	}
-    }
-    /**
-     * Start the manager. Start looking for lookup and registering
-     * with them.
-     * @param config object to use to obtain
-     *               <code>DiscoveryManagement</code> object, and if
-     *               this is the initial incarnation of this service, 
-     *               the object used to get the initial set of groups,
-     *               locators, and deployer defined attributes.
-     * @param service The proxy object to register with lookups.
-     * @param baseAttributes Any attributes the implementation wants
-     *                       attached, only used if this is the
-     *                       initial incarnation.
-     * @throws IOException if the is problem persisting the 
-     *         initial state or in starting discovery.
-     * @throws ConfigurationException if the configuration
-     *         is invalid.
-     */
-    void startManager(Configuration config, Object service,
-		      ServiceID serviceID, Entry[] baseAttributes) 
-	throws IOException, ConfigurationException
-    {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "startManager",
-	        new Object[] { config, service, serviceID, 
-		    Arrays.asList(baseAttributes)});
-	}
-	if (serviceID == null || serviceUuid == null)
-            throw new AssertionError(
-	       "serviceID and serviceUuid must be set");
-    
-        // Default do nothing preparer
-        final ProxyPreparer defaultPreparer =
-            new net.jini.security.BasicProxyPreparer();
-        lookupLocatorPreparer =
-            (ProxyPreparer)Config.getNonNullEntry(config,
-                TxnManager.MAHALO, "lookupLocatorPreparer",
-                ProxyPreparer.class, defaultPreparer);
-        if(initlogger.isLoggable(Level.CONFIG)) {
-	    initlogger.log(Level.CONFIG, "lookupLocatorPreparer: {0}",
-	         lookupLocatorPreparer);
-	}
-//TODO - defer creation of default LDM
-	dm = (DiscoveryManagement)
-	    Config.getNonNullEntry(config, TxnManager.MAHALO,
-		"discoveryManager", DiscoveryManagement.class, 
-		new LookupDiscoveryManager(
-                    DiscoveryGroupManagement.NO_GROUPS, null, null, config));
-        if(initlogger.isLoggable(Level.CONFIG)) {
-	    initlogger.log(Level.CONFIG, "discoveryManager: {0}", dm);
-	}
-
-	if (dm instanceof DiscoveryGroupManagement) {
-	    // Verify proper initial state ---> NO_GROUPS
-            String[] groups =
-                       ((DiscoveryGroupManagement)dm).getGroups();
-            if( (groups == DiscoveryGroupManagement.ALL_GROUPS) || 
-	        (groups.length != 0) )
-            {
-                throw new ConfigurationException(
-		    "discoveryManager entry must be configured " +
-		    "to initially discover/join NO_GROUPS");
-            }//endif
-    
-        } else { 
-            throw new ConfigurationException("Entry for component " +
-		TxnManager.MAHALO + ", name " +
-		"discoveryManager must implement " +
-	        "net.jini.discovery.DiscoveryGroupManagement");
-	}
-	
-	if (dm instanceof DiscoveryLocatorManagement) {
-            LookupLocator[] locs =
-                    ((DiscoveryLocatorManagement)dm).getLocators();
-            if( (locs != null) && (locs.length != 0) ) {
-                throw new ConfigurationException
-                    ("discoveryManager entry must be initially"
-		    + "configured with no locators");
-            }//endif
-	} else {
-	    throw new ConfigurationException("Entry for component " +
-		TxnManager.MAHALO + ", name " +
-		"discoveryManager must implement " +
-		"net.jini.discovery.DiscoveryLocatorManagement");
-	}
-
-	// if this is the first incarnation, consult config for groups,
-	// locators and attributes.
-	if (initial) {
-            if(initlogger.isLoggable(Level.FINEST)) {
-	        initlogger.log(Level.FINEST, "Obtaining initial values");
-	    }
-	    groups = (String[])
-		config.getEntry(TxnManager.MAHALO,
-		    "initialLookupGroups", String[].class, 
-		    new String[] { "" }); //default to public group
-            if(initlogger.isLoggable(Level.CONFIG)) {
-	        initlogger.log(Level.CONFIG, "Obtaining initial groups: {0}",
-		(groups==null?
-		    Arrays.asList(new String[] {"<ALL_GROUPS>"}):
-		    Arrays.asList(groups)));
-	    }
-	    locators = (LookupLocator[])
-		Config.getNonNullEntry(config, TxnManager.MAHALO,
-		     "initialLookupLocators", LookupLocator[].class, 
-                     new LookupLocator[0]);
-            if(initlogger.isLoggable(Level.CONFIG)) {
-	        initlogger.log(Level.CONFIG, "Obtaining initial locators: {0}",
-		Arrays.asList(locators));
-	    }
-	    final Entry[] cAttrs = (Entry[])
-		Config.getNonNullEntry(config, TxnManager.MAHALO,
-		    "initialLookupAttributes", Entry[].class, new Entry[0]);
-            if(initlogger.isLoggable(Level.CONFIG)) {
-	        initlogger.log(Level.CONFIG, "Obtaining initial attributes: {0}",
-		Arrays.asList(cAttrs));
-	    }
-	    if (cAttrs.length == 0) {
-		attributes = baseAttributes;
-	    } else {
-		attributes = new Entry[cAttrs.length + baseAttributes.length];
-                System.arraycopy(baseAttributes, 0, attributes,
-                                 0, baseAttributes.length);
-                System.arraycopy(cAttrs, 0, attributes,
-                                 baseAttributes.length, cAttrs.length);
-	    }
-            if(initlogger.isLoggable(Level.FINEST)) {
-	        initlogger.log(Level.FINEST, "Combined attributes: {0}",
-		Arrays.asList(attributes));
-	    }
-
-        } else {
-            /* recovery : if there are any locators get and
-             * use recoveredLookupLocatorPreparer
-             */
-            if(initlogger.isLoggable(Level.FINEST)) {
-	        initlogger.log(Level.FINEST, "Recovered locators: {0}",
-		Arrays.asList(locators));
-	    }
-            if (locators.length > 0) {
-                final ProxyPreparer recoveredLookupLocatorPreparer =
-                    (ProxyPreparer)Config.getNonNullEntry(config,
-                        TxnManager.MAHALO,
-                        "recoveredLookupLocatorPreparer", ProxyPreparer.class,
-                         defaultPreparer);
-                if(initlogger.isLoggable(Level.CONFIG)) {
-	            initlogger.log(Level.CONFIG, "recoveredLookupLocatorPreparer: {0}",
-		    recoveredLookupLocatorPreparer);
-                }
-		final List prepared = new java.util.LinkedList();
-                for (int i=0; i<locators.length; i++) {
-                    try {
-                        prepared.add(recoveredLookupLocatorPreparer.
-                                     prepareProxy(locators[i]));
-                    } catch (Throwable t) {
-                        if(initlogger.isLoggable(Levels.HANDLED)) {
-	                    initlogger.log(Levels.HANDLED, 
-			    "Exception re-preparing LookupLocator: {0}. " 
-			        + "Dropping locator.",
-			    locators[i]);
-			}
-                        if(initlogger.isLoggable(Levels.HANDLED)) {
-	                    initlogger.log(Levels.HANDLED, 
-			    "Preparer exception: ", t);
-			}
-                    }
-                }
-                locators =
-                    (LookupLocator[])prepared.toArray(new LookupLocator[0]);
-	    }
-	}
-
-	// Now that we have groups & locators (either from 
-	// a previous incarnation or from the config) start discovery.
-        if(initlogger.isLoggable(Level.FINEST)) {
-	    initlogger.log(Level.FINEST, "Setting groups and locators");
-	}
-	((DiscoveryGroupManagement)dm).setGroups(groups);
-	((DiscoveryLocatorManagement)dm).setLocators(locators);
-
-        if(initlogger.isLoggable(Level.FINEST)) {
-	    initlogger.log(Level.FINEST, "Creating JoinManager");
-        }
-	mgr = new JoinManager(service, attributes, serviceID, 
-            dm, null, config);
-        // Once we are running we don't need the attributes,
-        // locators, and groups fields, null them out (the
-        // state is in the mgr and dm.
-        attributes = null;
-        groups = null;
-        locators = null;
-
-	// Now that we have state, make sure it is written to disk.
-        if(initlogger.isLoggable(Level.FINEST)) {
-	    initlogger.log(Level.FINEST, "Taking snapshot");
-	}
-	update();
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "startManager");
-	}
-    }
-
-    public void setServiceUuid(Uuid serviceUuid) {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "setServiceUuid", serviceUuid);
-	}
-	if (serviceUuid == null)
-            throw new NullPointerException("serviceUuid can't be null");
-        this.serviceUuid = serviceUuid;
-	// Can't update until mgr & dm are started.
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "setServiceUuid");
-	}
-    }
-    
-    public Uuid getServiceUuid() {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "getServiceUuid");
-	}
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "getServiceUuid", serviceUuid);
-	}
-        return serviceUuid;
-    }
-
-    /** 
-     * Make a good faith attempt to terminate
-     * discovery, and cancel any lookup registrations.  */
-    public void stop() {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "stop");
-	}
-	// Unregister with lookup
-
-	// Terminate the JoinManager first so it will not call
-	// into the dm after it has been terminated.
-	if (mgr != null)
-	    mgr.terminate();
-
-	if (dm != null) 
-	    dm.terminate();
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "stop");
-	}
-    }
-
-    public void destroy() {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "destroy");
-	}
-	stop();
-	if (log != null)
-	    log.deletePersistentStore();
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "destroy");
-	}
-    }
-
-    /* Basically we are implementing JoinAdmin, for get methods we just
-     * delegate to JoinManager, for the set methods we call
-     * JoinManager to and then persist the change by calling the
-     * appropriate method on our JoinAdminState.  If the call on our
-     * JoinAdminState throws an IOException we throw a runtime
-     * exception since JoinAdmin methods don't let us throw a
-     * IOException 
-     */
-
-    /** 
-     * Get the current attribute sets for the service. 
-     * 
-     * @return the current attribute sets for the service
-     */
-    public Entry[] getLookupAttributes() {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "getLookupAttributes");
-	}
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "getLookupAttributes");
-	}
-	return mgr.getAttributes();
-    }
-
-    /** 
-     * Add attribute sets for the service.  The resulting set will be used
-     * for all future joins.  The attribute sets are also added to all 
-     * currently-joined lookup services.
-     *
-     * @param attrSets the attribute sets to add
-     * @throws java.rmi.RuntimeException if the change can not be persisted.
-     */
-    public void addLookupAttributes(Entry[] attrSets) {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "addLookupAttributes");
-	}
-	mgr.addAttributes(attrSets, true);
-	update();
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "addLookupAttributes");
-	}
-    }
-
-    /**  
-     * Modify the current attribute sets, using the same semantics as
-     * ServiceRegistration.modifyAttributes.  The resulting set will be used
-     * for all future joins.  The same modifications are also made to all 
-     * currently-joined lookup services.
-     *
-     * @param attrSetTemplates the templates for matching attribute sets
-     * @param attrSets the modifications to make to matching sets
-     *     
-     * @throws java.rmi.RuntimeException if the change can not be persisted.
-     * @see net.jini.core.lookup.ServiceRegistration#modifyAttributes
-     */
-    public void modifyLookupAttributes(Entry[] attrSetTemplates,
-				       Entry[] attrSets) {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "modifyLookupAttributes");
-	}
-	mgr.modifyAttributes(attrSetTemplates, attrSets, true);
-	update();
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "modifyLookupAttributes");
-	}
-    }
-
-   /**
-     * Get the list of groups to join.  An empty array means the service
-     * joins no groups (as opposed to "all" groups).
-     *
-     * @return an array of groups to join. An empty array means the service
-     *         joins no groups (as opposed to "all" groups).
-     * @see #setLookupGroups
-     */
-    public String[] getLookupGroups() {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "getLookupGroups");
-	}
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "getLookupGroups");
-	}
-	return ((DiscoveryGroupManagement)dm).getGroups();
-    }
-
-    /**
-     * Add new groups to the set to join.  Lookup services in the new
-     * groups will be discovered and joined.
-     *
-     * @param groups groups to join
-     * @throws java.rmi.RuntimeException if the change can not be persisted.
-     * @see #removeLookupGroups
-     */
-    public void addLookupGroups(String[] groups) {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "addLookupGroups");
-	}
- 	try {
-	    ((DiscoveryGroupManagement)dm).addGroups(groups);
-	} catch (IOException e) {
-	    throw new RuntimeException("Could not change groups");
-	}
-	update();
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "addLookupGroups");
-	}
-    }
-
-    /**
-     * Remove groups from the set to join.  Leases are cancelled at lookup
-     * services that are not members of any of the remaining groups.
-     *
-     * @param groups groups to leave
-     * @throws java.rmi.RuntimeException if the change can not be persisted.
-     * @see #addLookupGroups
-     */
-    public void removeLookupGroups(String[] groups) {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "removeLookupGroups");
-	}
-        ((DiscoveryGroupManagement)dm).removeGroups(groups);
-	update();
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "removeLookupGroups");    
-	}
-    }
-
-    /**
-     * Replace the list of groups to join with a new list.  Leases are
-     * cancelled at lookup services that are not members of any of the
-     * new groups.  Lookup services in the new groups will be discovered
-     * and joined.
-     *
-     * @param groups groups to join
-     * @throws java.rmi.RuntimeException if the change can not be persisted.
-     * @see #getLookupGroups
-     */
-    public void setLookupGroups(String[] groups) {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "setLookupGroups");
-	}
-	try {
-	    ((DiscoveryGroupManagement)dm).setGroups(groups);
-	} catch (IOException e) {
-	    throw new RuntimeException("Could not change groups");
-	}
-	update();
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "setLookupGroups");
-	}
-    }
-    
-    /** 
-     * Get the list of locators of specific lookup services to join. 
-     *
-     * @return the list of locators of specific lookup services to join
-     * @see #setLookupLocators
-     */
-    public LookupLocator[] getLookupLocators() {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "getLookupLocators");
-	}
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "getLookupLocators");
-	}
-    	return ((DiscoveryLocatorManagement)dm).getLocators();
-    }
-
-    /**
-     * Add locators for specific new lookup services to join.  The new
-     * lookup services will be discovered and joined.
-     *
-     * @param locators locators of specific lookup services to join
-     * @throws java.rmi.RuntimeException if the change can not be persisted.
-     * @see #removeLookupLocators
-     */
-    public void addLookupLocators(LookupLocator[] locators) 
-        throws RemoteException
-    {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "addLookupLocators");
-	}
-        prepareLocators(locators);
-	((DiscoveryLocatorManagement)dm).addLocators(locators);
-	update();
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "addLookupLocators");
-	}
-    }
-
-    /**
-     * Remove locators for specific lookup services from the set to join.
-     * Any leases held at the lookup services are cancelled.
-     *
-     * @param locators locators of specific lookup services to leave
-     * @throws java.rmi.RuntimeException if the change can not be persisted.
-     * @see #addLookupLocators
-     */
-    public void removeLookupLocators(LookupLocator[] locators)         
-        throws RemoteException
-    {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "removeLookupLocators");
-	}
-        prepareLocators(locators);
-	((DiscoveryLocatorManagement)dm).removeLocators(locators);
-	update();
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "removeLookupLocators");
-	}
-    }
-
-    /**
-     * Replace the list of locators of specific lookup services to join
-     * with a new list.  Leases are cancelled at lookup services that were
-     * in the old list but are not in the new list.  Any new lookup services
-     * will be discovered and joined.
-     *
-     * @param locators locators of specific lookup services to join
-     * @throws java.rmi.RuntimeException if the change can not be persisted.
-     * @see #getLookupLocators
-     */
-    public void setLookupLocators(LookupLocator[] locators) 
-        throws RemoteException
-    {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "setLookupLocators");
-	}
-        prepareLocators(locators);
-	((DiscoveryLocatorManagement)dm).setLocators(locators);
-	update();
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "setLookupLocators");
-	}
-    }
-
-    /**
-     * Apply <code>lookupLocatorPreparer</code> to each locator in the
-     * array, replacing the original locator with the result of the
-     * <code>prepareProxy</code> call. If call fails with an exception
-     * throw that exception.
-     * @param locators the <code>LookupLocator</code>s to be prepared.
-     * @throws RemoteException if preparation of any of the locators
-     *         does.
-     * @throws SecurityException if preparation of any of the locators
-     *         does.
-     */
-    private void prepareLocators(LookupLocator[] locators)
-        throws RemoteException
-    {
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.entering(JoinStateManager.class.getName(), 
-	        "prepareLocators");
-	}
-        for (int i = 0; i<locators.length; i++) {
-            locators[i] = (LookupLocator)lookupLocatorPreparer.prepareProxy(
-                locators[i]);
-	}
-	if (operationsLogger.isLoggable(Level.FINER)) {
-            operationsLogger.exiting(JoinStateManager.class.getName(), 
-	        "prepareLocators");
-	}
-    }
-
-    private void update() {
-	if (log != null) {
-	    synchronized (log) {
-		try {
-	            log.snapshot();
-	        } catch (IOException e) {
-	            if (persistenceLogger.isLoggable(Level.WARNING)) {
-                        persistenceLogger.log(Level.WARNING,
-	                    "Failed to persist join state", e);
-	            }
-//TODO - need a better strategy here
-	            throw new RuntimeException("Problem persisting state.", e);
-	        }
-	    }
-	}
-    }
-
-    /**
-     * Utility method to write out an array of entities to an
-     * <code>ObjectOutputStream</code>.  Can be recovered by a call
-     * to <code>readAttributes()</code>
-     * <p>
-     * Packages each attribute in its own <code>MarshalledObject</code> so
-     * a bad codebase on an attribute class will not corrupt the whole array.
-     */
-    // @see JoinAdminActivationState#readAttributes
-    static private void writeAttributes(Entry[] attributes,
-                                        ObjectOutput out)
-        throws IOException
-    {
-        // Need to package each attribute in its own marshaled object,
-        // this makes sure that the attribute's code base is preserved
-        // and when we unpack to discard attributes who's codebase
-        // has been lost without throwing away those we can still deal with.
-         
-        out.writeInt(attributes.length);
-        for (int i=0; i<attributes.length; i++) {
-            out.writeObject(new MarshalledObject(attributes[i]));
-	}
-    }
- 
-    /**
-     * Utility method to read in an array of entities from a
-     * <code>ObjectInputStream</code>.  Array should have been written
-     * by a call to <code>writeAttributes()</code>
-     * <p>
-     *   
-     * Will try and recover as many attributes as possible.
-     * Attributes which can't be recovered won't be returned but they
-     * will remain in the log.
-     * 
-     */
-    // @see JoinAdminActivationState#writeAttributes
-    static private Entry[] readAttributes(ObjectInput in)
-        throws IOException, ClassNotFoundException
-    {
-        final List entries = new java.util.LinkedList();
-        final int objectCount = in.readInt();
-        for (int i=0; i<objectCount; i++) {
-            try {
-                MarshalledObject mo = (MarshalledObject)in.readObject();
-                entries.add(mo.get());
-            } catch (IOException e) {
-                if(initlogger.isLoggable(Levels.HANDLED)) {
-	            initlogger.log(Levels.HANDLED, 
-		    "Exception getting service attribute ... skipping", e);
-		}
-            } catch (ClassNotFoundException e) {
-                if(initlogger.isLoggable(Levels.HANDLED)) {
-	            initlogger.log(Levels.HANDLED, 
-		    "Exception getting service attribute ... skipping", e);
-		}
-            }
-        }
- 
-        return (Entry[])entries.toArray(new Entry[0]);
-    }
-
-    // -----------------------------------
-    //  Methods required by LogHandler
-    // -----------------------------------
- 
-    // inherit doc comment
-    public void snapshot(OutputStream out) throws IOException {
-	ObjectOutputStream oostream = new ObjectOutputStream(out);
-        oostream.writeObject(serviceUuid);
-        writeAttributes(mgr.getAttributes(), oostream);
-        oostream.writeObject(((DiscoveryLocatorManagement)dm).getLocators());
-        oostream.writeObject(((DiscoveryGroupManagement)dm).getGroups());
-	oostream.flush();
-    }
-
-    // inherit doc comment
-    public void recover(InputStream in) 
-	throws Exception 
-    {
-	initial = false;
-	ObjectInputStream oistream = new ObjectInputStream(in);
-	serviceUuid = (Uuid)oistream.readObject();
-        attributes  = readAttributes(oistream);
-        locators    = (LookupLocator [])oistream.readObject();
-        groups      = (String [])oistream.readObject();
-    }
-    /**
-     * This method always throws
-     * <code>UnsupportedOperationException</code> since
-     * <code>FileJoinAdminState</code> should never update a
-     * log.
-     */
-    public void applyUpdate(Object update) throws Exception {
-        throw new UnsupportedOperationException(
-                "JoinStateManager:Updating log" +
-                ", this should not happen");
-    }
-
-}
-
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.sun.jini.mahalo;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.rmi.MarshalledObject;
+import java.rmi.RemoteException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.jini.config.Configuration;
+import net.jini.config.ConfigurationException;
+import net.jini.core.discovery.LookupLocator;
+import net.jini.core.entry.Entry;
+import net.jini.core.lookup.ServiceID;
+import net.jini.discovery.DiscoveryManagement;
+import net.jini.discovery.DiscoveryLocatorManagement;
+import net.jini.discovery.DiscoveryGroupManagement;
+import net.jini.discovery.LookupDiscoveryManager;
+import net.jini.id.Uuid;
+import net.jini.lookup.JoinManager;
+import net.jini.lookup.ServiceIDListener;
+import net.jini.security.ProxyPreparer;
+
+
+import com.sun.jini.config.Config;
+import com.sun.jini.logging.Levels;
+import com.sun.jini.reliableLog.LogHandler;
+import com.sun.jini.reliableLog.ReliableLog;
+
+/**
+ * <code>JoinStateManager</code> provides a utility that manages
+ * a service's join state (optionally persisting that state) and
+ * manages the join protocol protocol on behalf of the service.
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ * @see net.jini.lookup.ServiceIDListener
+ * @see com.sun.jini.reliableLog.LogHandler
+ */
+ 
+class JoinStateManager extends LogHandler {
+    /** Logger for logging initialization related messages */
+    private static final Logger initlogger = TxnManagerImpl.initLogger;
+
+    /** Logger for logging operations related messages */
+    private static final Logger operationsLogger = TxnManagerImpl.operationsLogger;
+
+     /** Logger for transaction persistence related messages */
+    private static final Logger persistenceLogger = TxnManagerImpl.persistenceLogger;
+
+    /** <code>ProxyPreparer</code> for <code>LookupLocators</code> */
+    private volatile ProxyPreparer lookupLocatorPreparer;
+
+    /**
+     * Object used to find lookups. Has to implement DiscoveryManagement
+     * and DiscoveryLocatorManagement as well as DiscoveryGroupManagement.
+     */
+    private volatile DiscoveryManagement dm;
+
+    /**
+     * <code>JoinManager</code> that is handling the details of binding
+     * into Jini lookup services.
+     */
+    private volatile JoinManager  mgr;
+
+    /**
+     * The object coordinating our persistent state.
+     */
+    private volatile ReliableLog log;
+
+    /**
+     * The join state, this data needs to be persisted between restarts
+     */
+    private volatile Entry[]		attributes;
+    private volatile LookupLocator[]	locators;
+    private volatile String[]		groups;
+
+    /** Service's internal <code>Uuid</code> which needs to be persisted */
+    private volatile Uuid		serviceUuid;
+    
+    /**
+     * Conceptually, true if this is the first time this
+     * service has come up, implemented as if there was
+     * no previous state then this is the first time.
+     */
+    private volatile boolean initial = true;
+    
+    /**
+     * Simple constructor.
+     */
+    JoinStateManager(String logPath) throws IOException { 
+        super(); 
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "JoinStateManager", logPath);
+	}
+	this.log = (logPath==null) ? null : new ReliableLog(logPath, this);
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "JoinStateManager");
+	}
+    }
+    
+    void recover() throws IOException {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "recover");
+	}
+	if (log != null) log.recover();
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "recover");
+	}
+    }
+    /**
+     * Start the manager. Start looking for lookup and registering
+     * with them.
+     * @param config object to use to obtain
+     *               <code>DiscoveryManagement</code> object, and if
+     *               this is the initial incarnation of this service, 
+     *               the object used to get the initial set of groups,
+     *               locators, and deployer defined attributes.
+     * @param service The proxy object to register with lookups.
+     * @param baseAttributes Any attributes the implementation wants
+     *                       attached, only used if this is the
+     *                       initial incarnation.
+     * @throws IOException if the is problem persisting the 
+     *         initial state or in starting discovery.
+     * @throws ConfigurationException if the configuration
+     *         is invalid.
+     */
+    void startManager(Configuration config, Object service,
+		      ServiceID serviceID, Entry[] baseAttributes) 
+	throws IOException, ConfigurationException
+    {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "startManager",
+	        new Object[] { config, service, serviceID, 
+		    Arrays.asList(baseAttributes)});
+	}
+	if (serviceID == null || serviceUuid == null)
+            throw new AssertionError(
+	       "serviceID and serviceUuid must be set");
+    
+        // Default do nothing preparer
+        final ProxyPreparer defaultPreparer =
+            new net.jini.security.BasicProxyPreparer();
+        lookupLocatorPreparer =
+            (ProxyPreparer)Config.getNonNullEntry(config,
+                TxnManager.MAHALO, "lookupLocatorPreparer",
+                ProxyPreparer.class, defaultPreparer);
+        if(initlogger.isLoggable(Level.CONFIG)) {
+	    initlogger.log(Level.CONFIG, "lookupLocatorPreparer: {0}",
+	         lookupLocatorPreparer);
+	}
+//TODO - defer creation of default LDM
+	dm = (DiscoveryManagement)
+	    Config.getNonNullEntry(config, TxnManager.MAHALO,
+		"discoveryManager", DiscoveryManagement.class, 
+		new LookupDiscoveryManager(
+                    DiscoveryGroupManagement.NO_GROUPS, null, null, config));
+        if(initlogger.isLoggable(Level.CONFIG)) {
+	    initlogger.log(Level.CONFIG, "discoveryManager: {0}", dm);
+	}
+
+	if (dm instanceof DiscoveryGroupManagement) {
+	    // Verify proper initial state ---> NO_GROUPS
+            String[] groups =
+                       ((DiscoveryGroupManagement)dm).getGroups();
+            if( (groups == DiscoveryGroupManagement.ALL_GROUPS) || 
+	        (groups.length != 0) )
+            {
+                throw new ConfigurationException(
+		    "discoveryManager entry must be configured " +
+		    "to initially discover/join NO_GROUPS");
+            }//endif
+    
+        } else { 
+            throw new ConfigurationException("Entry for component " +
+		TxnManager.MAHALO + ", name " +
+		"discoveryManager must implement " +
+	        "net.jini.discovery.DiscoveryGroupManagement");
+	}
+	
+	if (dm instanceof DiscoveryLocatorManagement) {
+            LookupLocator[] locs =
+                    ((DiscoveryLocatorManagement)dm).getLocators();
+            if( (locs != null) && (locs.length != 0) ) {
+                throw new ConfigurationException
+                    ("discoveryManager entry must be initially"
+		    + "configured with no locators");
+            }//endif
+	} else {
+	    throw new ConfigurationException("Entry for component " +
+		TxnManager.MAHALO + ", name " +
+		"discoveryManager must implement " +
+		"net.jini.discovery.DiscoveryLocatorManagement");
+	}
+
+	// if this is the first incarnation, consult config for groups,
+	// locators and attributes.
+	if (initial) {
+            if(initlogger.isLoggable(Level.FINEST)) {
+	        initlogger.log(Level.FINEST, "Obtaining initial values");
+	    }
+	    groups = (String[])
+		config.getEntry(TxnManager.MAHALO,
+		    "initialLookupGroups", String[].class, 
+		    new String[] { "" }); //default to public group
+            if(initlogger.isLoggable(Level.CONFIG)) {
+	        initlogger.log(Level.CONFIG, "Obtaining initial groups: {0}",
+		(groups==null?
+		    Arrays.asList(new String[] {"<ALL_GROUPS>"}):
+		    Arrays.asList(groups)));
+	    }
+	    locators = (LookupLocator[])
+		Config.getNonNullEntry(config, TxnManager.MAHALO,
+		     "initialLookupLocators", LookupLocator[].class, 
+                     new LookupLocator[0]);
+            if(initlogger.isLoggable(Level.CONFIG)) {
+	        initlogger.log(Level.CONFIG, "Obtaining initial locators: {0}",
+		Arrays.asList(locators));
+	    }
+	    final Entry[] cAttrs = (Entry[])
+		Config.getNonNullEntry(config, TxnManager.MAHALO,
+		    "initialLookupAttributes", Entry[].class, new Entry[0]);
+            if(initlogger.isLoggable(Level.CONFIG)) {
+	        initlogger.log(Level.CONFIG, "Obtaining initial attributes: {0}",
+		Arrays.asList(cAttrs));
+	    }
+	    if (cAttrs.length == 0) {
+		attributes = baseAttributes;
+	    } else {
+		attributes = new Entry[cAttrs.length + baseAttributes.length];
+                System.arraycopy(baseAttributes, 0, attributes,
+                                 0, baseAttributes.length);
+                System.arraycopy(cAttrs, 0, attributes,
+                                 baseAttributes.length, cAttrs.length);
+	    }
+            if(initlogger.isLoggable(Level.FINEST)) {
+	        initlogger.log(Level.FINEST, "Combined attributes: {0}",
+		Arrays.asList(attributes));
+	    }
+
+        } else {
+            /* recovery : if there are any locators get and
+             * use recoveredLookupLocatorPreparer
+             */
+            if(initlogger.isLoggable(Level.FINEST)) {
+	        initlogger.log(Level.FINEST, "Recovered locators: {0}",
+		Arrays.asList(locators));
+	    }
+            if (locators.length > 0) {
+                final ProxyPreparer recoveredLookupLocatorPreparer =
+                    (ProxyPreparer)Config.getNonNullEntry(config,
+                        TxnManager.MAHALO,
+                        "recoveredLookupLocatorPreparer", ProxyPreparer.class,
+                         defaultPreparer);
+                if(initlogger.isLoggable(Level.CONFIG)) {
+	            initlogger.log(Level.CONFIG, "recoveredLookupLocatorPreparer: {0}",
+		    recoveredLookupLocatorPreparer);
+                }
+		final List prepared = new java.util.LinkedList();
+                for (int i=0; i<locators.length; i++) {
+                    try {
+                        prepared.add(recoveredLookupLocatorPreparer.
+                                     prepareProxy(locators[i]));
+                    } catch (Throwable t) {
+                        if(initlogger.isLoggable(Levels.HANDLED)) {
+	                    initlogger.log(Levels.HANDLED, 
+			    "Exception re-preparing LookupLocator: {0}. " 
+			        + "Dropping locator.",
+			    locators[i]);
+			}
+                        if(initlogger.isLoggable(Levels.HANDLED)) {
+	                    initlogger.log(Levels.HANDLED, 
+			    "Preparer exception: ", t);
+			}
+                    }
+                }
+                locators =
+                    (LookupLocator[])prepared.toArray(new LookupLocator[0]);
+	    }
+	}
+
+	// Now that we have groups & locators (either from 
+	// a previous incarnation or from the config) start discovery.
+        if(initlogger.isLoggable(Level.FINEST)) {
+	    initlogger.log(Level.FINEST, "Setting groups and locators");
+	}
+	((DiscoveryGroupManagement)dm).setGroups(groups);
+	((DiscoveryLocatorManagement)dm).setLocators(locators);
+
+        if(initlogger.isLoggable(Level.FINEST)) {
+	    initlogger.log(Level.FINEST, "Creating JoinManager");
+        }
+	mgr = new JoinManager(service, attributes, serviceID, 
+            dm, null, config);
+        // Once we are running we don't need the attributes,
+        // locators, and groups fields, null them out (the
+        // state is in the mgr and dm.
+        attributes = null;
+        groups = null;
+        locators = null;
+
+	// Now that we have state, make sure it is written to disk.
+        if(initlogger.isLoggable(Level.FINEST)) {
+	    initlogger.log(Level.FINEST, "Taking snapshot");
+	}
+	update();
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "startManager");
+	}
+    }
+
+    public void setServiceUuid(Uuid serviceUuid) {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "setServiceUuid", serviceUuid);
+	}
+	if (serviceUuid == null)
+            throw new NullPointerException("serviceUuid can't be null");
+        this.serviceUuid = serviceUuid;
+	// Can't update until mgr & dm are started.
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "setServiceUuid");
+	}
+    }
+    
+    public Uuid getServiceUuid() {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "getServiceUuid");
+	}
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "getServiceUuid", serviceUuid);
+	}
+        return serviceUuid;
+    }
+
+    /** 
+     * Make a good faith attempt to terminate
+     * discovery, and cancel any lookup registrations.  */
+    public void stop() {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "stop");
+	}
+	// Unregister with lookup
+
+	// Terminate the JoinManager first so it will not call
+	// into the dm after it has been terminated.
+	if (mgr != null)
+	    mgr.terminate();
+
+	if (dm != null) 
+	    dm.terminate();
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "stop");
+	}
+    }
+
+    public void destroy() {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "destroy");
+	}
+	stop();
+	if (log != null)
+	    log.deletePersistentStore();
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "destroy");
+	}
+    }
+
+    /* Basically we are implementing JoinAdmin, for get methods we just
+     * delegate to JoinManager, for the set methods we call
+     * JoinManager to and then persist the change by calling the
+     * appropriate method on our JoinAdminState.  If the call on our
+     * JoinAdminState throws an IOException we throw a runtime
+     * exception since JoinAdmin methods don't let us throw a
+     * IOException 
+     */
+
+    /** 
+     * Get the current attribute sets for the service. 
+     * 
+     * @return the current attribute sets for the service
+     */
+    public Entry[] getLookupAttributes() {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "getLookupAttributes");
+	}
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "getLookupAttributes");
+	}
+	return mgr.getAttributes();
+    }
+
+    /** 
+     * Add attribute sets for the service.  The resulting set will be used
+     * for all future joins.  The attribute sets are also added to all 
+     * currently-joined lookup services.
+     *
+     * @param attrSets the attribute sets to add
+     * @throws java.rmi.RuntimeException if the change can not be persisted.
+     */
+    public void addLookupAttributes(Entry[] attrSets) {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "addLookupAttributes");
+	}
+	mgr.addAttributes(attrSets, true);
+	update();
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "addLookupAttributes");
+	}
+    }
+
+    /**  
+     * Modify the current attribute sets, using the same semantics as
+     * ServiceRegistration.modifyAttributes.  The resulting set will be used
+     * for all future joins.  The same modifications are also made to all 
+     * currently-joined lookup services.
+     *
+     * @param attrSetTemplates the templates for matching attribute sets
+     * @param attrSets the modifications to make to matching sets
+     *     
+     * @throws java.rmi.RuntimeException if the change can not be persisted.
+     * @see net.jini.core.lookup.ServiceRegistration#modifyAttributes
+     */
+    public void modifyLookupAttributes(Entry[] attrSetTemplates,
+				       Entry[] attrSets) {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "modifyLookupAttributes");
+	}
+	mgr.modifyAttributes(attrSetTemplates, attrSets, true);
+	update();
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "modifyLookupAttributes");
+	}
+    }
+
+   /**
+     * Get the list of groups to join.  An empty array means the service
+     * joins no groups (as opposed to "all" groups).
+     *
+     * @return an array of groups to join. An empty array means the service
+     *         joins no groups (as opposed to "all" groups).
+     * @see #setLookupGroups
+     */
+    public String[] getLookupGroups() {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "getLookupGroups");
+	}
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "getLookupGroups");
+	}
+	return ((DiscoveryGroupManagement)dm).getGroups();
+    }
+
+    /**
+     * Add new groups to the set to join.  Lookup services in the new
+     * groups will be discovered and joined.
+     *
+     * @param groups groups to join
+     * @throws java.rmi.RuntimeException if the change can not be persisted.
+     * @see #removeLookupGroups
+     */
+    public void addLookupGroups(String[] groups) {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "addLookupGroups");
+	}
+ 	try {
+	    ((DiscoveryGroupManagement)dm).addGroups(groups);
+	} catch (IOException e) {
+	    throw new RuntimeException("Could not change groups");
+	}
+	update();
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "addLookupGroups");
+	}
+    }
+
+    /**
+     * Remove groups from the set to join.  Leases are cancelled at lookup
+     * services that are not members of any of the remaining groups.
+     *
+     * @param groups groups to leave
+     * @throws java.rmi.RuntimeException if the change can not be persisted.
+     * @see #addLookupGroups
+     */
+    public void removeLookupGroups(String[] groups) {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "removeLookupGroups");
+	}
+        ((DiscoveryGroupManagement)dm).removeGroups(groups);
+	update();
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "removeLookupGroups");    
+	}
+    }
+
+    /**
+     * Replace the list of groups to join with a new list.  Leases are
+     * cancelled at lookup services that are not members of any of the
+     * new groups.  Lookup services in the new groups will be discovered
+     * and joined.
+     *
+     * @param groups groups to join
+     * @throws java.rmi.RuntimeException if the change can not be persisted.
+     * @see #getLookupGroups
+     */
+    public void setLookupGroups(String[] groups) {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "setLookupGroups");
+	}
+	try {
+	    ((DiscoveryGroupManagement)dm).setGroups(groups);
+	} catch (IOException e) {
+	    throw new RuntimeException("Could not change groups");
+	}
+	update();
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "setLookupGroups");
+	}
+    }
+    
+    /** 
+     * Get the list of locators of specific lookup services to join. 
+     *
+     * @return the list of locators of specific lookup services to join
+     * @see #setLookupLocators
+     */
+    public LookupLocator[] getLookupLocators() {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "getLookupLocators");
+	}
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "getLookupLocators");
+	}
+    	return ((DiscoveryLocatorManagement)dm).getLocators();
+    }
+
+    /**
+     * Add locators for specific new lookup services to join.  The new
+     * lookup services will be discovered and joined.
+     *
+     * @param locators locators of specific lookup services to join
+     * @throws java.rmi.RuntimeException if the change can not be persisted.
+     * @see #removeLookupLocators
+     */
+    public void addLookupLocators(LookupLocator[] locators) 
+        throws RemoteException
+    {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "addLookupLocators");
+	}
+        prepareLocators(locators);
+	((DiscoveryLocatorManagement)dm).addLocators(locators);
+	update();
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "addLookupLocators");
+	}
+    }
+
+    /**
+     * Remove locators for specific lookup services from the set to join.
+     * Any leases held at the lookup services are cancelled.
+     *
+     * @param locators locators of specific lookup services to leave
+     * @throws java.rmi.RuntimeException if the change can not be persisted.
+     * @see #addLookupLocators
+     */
+    public void removeLookupLocators(LookupLocator[] locators)         
+        throws RemoteException
+    {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "removeLookupLocators");
+	}
+        prepareLocators(locators);
+	((DiscoveryLocatorManagement)dm).removeLocators(locators);
+	update();
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "removeLookupLocators");
+	}
+    }
+
+    /**
+     * Replace the list of locators of specific lookup services to join
+     * with a new list.  Leases are cancelled at lookup services that were
+     * in the old list but are not in the new list.  Any new lookup services
+     * will be discovered and joined.
+     *
+     * @param locators locators of specific lookup services to join
+     * @throws java.rmi.RuntimeException if the change can not be persisted.
+     * @see #getLookupLocators
+     */
+    public void setLookupLocators(LookupLocator[] locators) 
+        throws RemoteException
+    {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "setLookupLocators");
+	}
+        prepareLocators(locators);
+	((DiscoveryLocatorManagement)dm).setLocators(locators);
+	update();
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "setLookupLocators");
+	}
+    }
+
+    /**
+     * Apply <code>lookupLocatorPreparer</code> to each locator in the
+     * array, replacing the original locator with the result of the
+     * <code>prepareProxy</code> call. If call fails with an exception
+     * throw that exception.
+     * @param locators the <code>LookupLocator</code>s to be prepared.
+     * @throws RemoteException if preparation of any of the locators
+     *         does.
+     * @throws SecurityException if preparation of any of the locators
+     *         does.
+     */
+    private void prepareLocators(LookupLocator[] locators)
+        throws RemoteException
+    {
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.entering(JoinStateManager.class.getName(), 
+	        "prepareLocators");
+	}
+        for (int i = 0; i<locators.length; i++) {
+            locators[i] = (LookupLocator)lookupLocatorPreparer.prepareProxy(
+                locators[i]);
+	}
+	if (operationsLogger.isLoggable(Level.FINER)) {
+            operationsLogger.exiting(JoinStateManager.class.getName(), 
+	        "prepareLocators");
+	}
+    }
+
+    private void update() {
+	if (log != null) {
+	    synchronized (log) {
+		try {
+	            log.snapshot();
+	        } catch (IOException e) {
+	            if (persistenceLogger.isLoggable(Level.WARNING)) {
+                        persistenceLogger.log(Level.WARNING,
+	                    "Failed to persist join state", e);
+	            }
+//TODO - need a better strategy here
+	            throw new RuntimeException("Problem persisting state.", e);
+	        }
+	    }
+	}
+    }
+
+    /**
+     * Utility method to write out an array of entities to an
+     * <code>ObjectOutputStream</code>.  Can be recovered by a call
+     * to <code>readAttributes()</code>
+     * <p>
+     * Packages each attribute in its own <code>MarshalledObject</code> so
+     * a bad codebase on an attribute class will not corrupt the whole array.
+     */
+    // @see JoinAdminActivationState#readAttributes
+    static private void writeAttributes(Entry[] attributes,
+                                        ObjectOutput out)
+        throws IOException
+    {
+        // Need to package each attribute in its own marshaled object,
+        // this makes sure that the attribute's code base is preserved
+        // and when we unpack to discard attributes who's codebase
+        // has been lost without throwing away those we can still deal with.
+         
+        out.writeInt(attributes.length);
+        for (int i=0; i<attributes.length; i++) {
+            out.writeObject(new MarshalledObject(attributes[i]));
+	}
+    }
+ 
+    /**
+     * Utility method to read in an array of entities from a
+     * <code>ObjectInputStream</code>.  Array should have been written
+     * by a call to <code>writeAttributes()</code>
+     * <p>
+     *   
+     * Will try and recover as many attributes as possible.
+     * Attributes which can't be recovered won't be returned but they
+     * will remain in the log.
+     * 
+     */
+    // @see JoinAdminActivationState#writeAttributes
+    static private Entry[] readAttributes(ObjectInput in)
+        throws IOException, ClassNotFoundException
+    {
+        final List entries = new java.util.LinkedList();
+        final int objectCount = in.readInt();
+        for (int i=0; i<objectCount; i++) {
+            try {
+                MarshalledObject mo = (MarshalledObject)in.readObject();
+                entries.add(mo.get());
+            } catch (IOException e) {
+                if(initlogger.isLoggable(Levels.HANDLED)) {
+	            initlogger.log(Levels.HANDLED, 
+		    "Exception getting service attribute ... skipping", e);
+		}
+            } catch (ClassNotFoundException e) {
+                if(initlogger.isLoggable(Levels.HANDLED)) {
+	            initlogger.log(Levels.HANDLED, 
+		    "Exception getting service attribute ... skipping", e);
+		}
+            }
+        }
+ 
+        return (Entry[])entries.toArray(new Entry[0]);
+    }
+
+    // -----------------------------------
+    //  Methods required by LogHandler
+    // -----------------------------------
+ 
+    // inherit doc comment
+    public void snapshot(OutputStream out) throws IOException {
+	ObjectOutputStream oostream = new ObjectOutputStream(out);
+        oostream.writeObject(serviceUuid);
+        writeAttributes(mgr.getAttributes(), oostream);
+        oostream.writeObject(((DiscoveryLocatorManagement)dm).getLocators());
+        oostream.writeObject(((DiscoveryGroupManagement)dm).getGroups());
+	oostream.flush();
+    }
+
+    // inherit doc comment
+    public void recover(InputStream in) 
+	throws Exception 
+    {
+	initial = false;
+	ObjectInputStream oistream = new ObjectInputStream(in);
+	serviceUuid = (Uuid)oistream.readObject();
+        attributes  = readAttributes(oistream);
+        locators    = (LookupLocator [])oistream.readObject();
+        groups      = (String [])oistream.readObject();
+    }
+    /**
+     * This method always throws
+     * <code>UnsupportedOperationException</code> since
+     * <code>FileJoinAdminState</code> should never update a
+     * log.
+     */
+    public void applyUpdate(Object update) throws Exception {
+        throw new UnsupportedOperationException(
+                "JoinStateManager:Updating log" +
+                ", this should not happen");
+    }
+
+}
+



Mime
View raw message