[or-cvs] r11736: refactored a lot of things to make old and new parts (RMI, n (in puppetor/trunk/src/de/uniba/wiai/lspi/puppetor: . examples groovy impl)

kloesing at seul.org kloesing at seul.org
Mon Oct 1 21:59:35 UTC 2007


Author: kloesing
Date: 2007-10-01 17:59:35 -0400 (Mon, 01 Oct 2007)
New Revision: 11736

Added:
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/HiddenService.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/HiddenServiceEventType.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/PuppeTorException.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/LongRunningNetwork.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/MergingProxiesWithLongRunningNetwork.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/MergingRoutersWithLongRunningNetwork.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/HiddenServiceImpl.java
Removed:
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/AliceEventType.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/BobEventType.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/MiscEventType.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/diststorage/
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/rmi/
Modified:
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientEventType.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventType.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeEventType.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerEventType.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/groovy/RmiPuppetzShell.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
Log:
refactored a lot of things to make old and new parts (RMI, network merging, Groovy shell) better fit together

Deleted: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/AliceEventType.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/AliceEventType.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/AliceEventType.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * 
- *     * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-public class AliceEventType implements EventType{
-
-	/**
-	 * Alice has received an onion request; this event is parsed from a log
-	 * statement in connection_ap_handshake_rewrite_and_attach().
-	 */
-	public static final AliceEventType ALICE_ONION_REQUEST_RECEIVED = new AliceEventType();
-
-	/**
-	 * Alice sends a fetch request for a hidden service descriptor to a
-	 * directory server; this event is parsed from a log statement in
-	 * rend_client_refetch_renddesc().
-	 */
-	public static final AliceEventType ALICE_SENDING_FETCH_DESC = new AliceEventType();
-
-	/**
-	 * Alice receives a reply to a previous fetch request for a hidden service
-	 * descriptors from a directory server; this event is parsed from a log
-	 * statement in connection_dir_client_reached_eof().
-	 */
-	public static final AliceEventType ALICE_DESC_FETCHED_RECEIVED = new AliceEventType();
-
-	/**
-	 * Alice has built a circuit to a rendezvous point and sends an
-	 * ESTABLISH_RENDEZVOUS cell; this event is parsed from a log statement in
-	 * rend_client_send_establish_rendezvous().
-	 */
-	public static final AliceEventType ALICE_BUILT_REND_CIRC_SENDING_ESTABLISH_RENDEZVOUS = new AliceEventType();
-
-	/**
-	 * Alice receives a RENDEZVOUS_ESTABLISHED cell from a rendezvous point;
-	 * this event is parsed from a log statement in
-	 * rend_client_rendezvous_acked().
-	 */
-	public static final AliceEventType ALICE_RENDEZVOUS_ESTABLISHED_RECEIVED = new AliceEventType();
-
-	/**
-	 * Alice has built a circuit to an introduction point (which does not
-	 * automatically lead to sending an INTRODUCE1 cell, because the rendezvous
-	 * circuit might not be ready); this event is parsed from a log statement in
-	 * rend_client_introcirc_has_opened().
-	 */
-	public static final AliceEventType ALICE_BUILT_INTRO_CIRC = new AliceEventType();
-
-	/**
-	 * Alice sends an INTRODUCE1 cell to an introduction point; this event is
-	 * parsed from a log statement in rend_client_send_introduction().
-	 */
-	public static final AliceEventType ALICE_SENDING_INTRODUCE1 = new AliceEventType();
-
-	/**
-	 * Alice has received an INTRODUCE_ACK cell as an acknowledgement to the
-	 * previous INTRODUCE1 cell; this event is parsed from a log statement in
-	 * rend_client_introduction_acked().
-	 */
-	public static final AliceEventType ALICE_INTRODUCE_ACK_RECEIVED = new AliceEventType();
-
-	/**
-	 * Alice has received a RENDEZVOUS2 cell and can now open an application
-	 * connection to the client; this event is parsed from a log statement in
-	 * rend_client_receive_rendezvous().
-	 */
-	public static final AliceEventType ALICE_RENDEZVOUS2_RECEIVED_APP_CONN_OPENED = new AliceEventType();
-}
\ No newline at end of file

Deleted: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/BobEventType.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/BobEventType.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/BobEventType.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * 
- *     * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-public class BobEventType implements EventType{
-
-	/**
-	 * Bob has built a circuit to an introduction point and sends an
-	 * ESTABLISH_INTRO cell; this event is parsed from a log statement in
-	 * rend_service_intro_has_opened().
-	 */
-	public static final BobEventType BOB_BUILT_INTRO_CIRC_SENDING_ESTABLISH_INTRO = new BobEventType();
-	
-	/**
-	 * Bob has received an INTRO_ESTABLISHED cell, i.e. a node has confirmed to
-	 * work as introduction point; this event is parsed from a log statement in
-	 * rend_service_intro_established().
-	 */
-	public static final BobEventType BOB_INTRO_ESTABLISHED_RECEIVED = new BobEventType();
-	
-	/**
-	 * Bob posts a hidden service descriptor to the directory servers (or to
-	 * hidden service directories in a modified Tor); this event is parsed from
-	 * a log statement in upload_service_descriptor().
-	 */
-	public static final BobEventType BOB_SENDING_PUBLISH_DESC = new BobEventType();
-	
-	/**
-	 * Bob received a response from a directory server (or from a hidden service
-	 * directory in a modified Tor) to a previous publish request; this event is
-	 * parsed from a log statement in connection_dir_client_reached_eof().
-	 */
-	public static final BobEventType BOB_DESC_PUBLISHED_RECEIVED = new BobEventType();
-	
-	/**
-	 * Bob has received an INTRODUCE2 cell, i.e. a node wants to establish a
-	 * connection, and will now try to establish a circuit to the client's
-	 * rendezvous point; this event is parsed from a log statement in
-	 * rend_service_introduce().
-	 */
-	public static final BobEventType BOB_INTRODUCE2_RECEIVED = new BobEventType();
-	
-	/**
-	 * Bob has built a circuit to a rendezvous point and sends an RENDEZVOUS1
-	 * cell; this event is parsed from a log statement in
-	 * rend_service_rendezvous_has_opened().
-	 */
-	public static final BobEventType BOB_BUILT_REND_CIRC_SENDING_RENDEZVOUS1 = new BobEventType();
-	
-	/**
-	 * Bob opens a connection to the actual hidden server; this event is parsed
-	 * from a log statement in connection_exit_begin_conn().
-	 */
-	public static final BobEventType BOB_APP_CONN_OPENED = new BobEventType();
-
-}
\ No newline at end of file

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -46,21 +46,22 @@
 	 * <p>
 	 * Performs one or more HTTP requests to a previously provided address and
 	 * port. All requests are performed by a thread in the background, so that
-	 * this method may return immediately. That thread will try for
+	 * this method returns immediately. That thread will try for
 	 * <code>retries</code> times to make the request with a timeout of
-	 * <code>timeoutForEachRetry</code> millis each. If an attempt is not
-	 * successful, the thread nevertheless waits for the timeout to expire. If
-	 * <code>stopOnSuccess</code> is set to <code>true</code>, the thread
-	 * will quit performing requests after the first successful request.
+	 * <code>timeoutForEachRetry</code> milliseconds each. If an attempt is not
+	 * successful, the thread nevertheless waits for the timeout to expire
+	 * before performing the next attempt. If <code>stopOnSuccess</code> is
+	 * set to <code>true</code>, the thread will quit performing requests
+	 * immediately after the first successful request.
 	 * </p>
 	 * 
 	 * <p>
-	 * For each sent request the application fires an
-	 * <event>EventType.APPLICATION_SENDING_REQUEST</code> event. On receiving
-	 * a reply it fires an event of type <code>EventType.APPLICATION_REPLY_RECEIVED</code>,
-	 * if a request is not successful or times out, an <code>EventType.APPLICATION_GAVE_UP_REQUEST</code>
+	 * For each sent request the application fires a
+	 * <event>ClientEventType.CLIENT_SENDING_REQUEST</code> event. On receiving
+	 * a reply it fires an event of type <code>ClientEventType.CLIENT_REPLY_RECEIVED</code>,
+	 * if a request is not successful or times out, a <code>ClientEventType.CLIENT_GAVE_UP_REQUEST</code>
 	 * event is fired. After all requests have been performed (either
-	 * successfully, or not) an event of type <code>EventType.APPLICATION_REQUESTS_PERFORMED</code>
+	 * successfully, or not) an event of type <code>ClientEventType.CLIENT_REQUESTS_PERFORMED</code>
 	 * is fired.
 	 * </p>
 	 * 
@@ -82,7 +83,7 @@
 	 *             Thrown if an invalid value is given for either of the
 	 *             parameters.
 	 */
-	public abstract void performRequest(int retries, long timeoutForEachRetry,
+	public abstract void startRequests(int retries, long timeoutForEachRetry,
 			boolean stopOnSuccess);
 
 	/**
@@ -92,7 +93,6 @@
 	 *             Thrown if no requests have been started before.
 	 */
 	public abstract void stopRequest();
-	
 
 	/**
 	 * Returns the name of this client.
@@ -100,4 +100,27 @@
 	 * @return The name of this client.
 	 */
 	public abstract String getClientApplicationName();
+
+	/**
+	 * Returns the SOCKS port of the local Tor node to which requests are sent.
+	 * 
+	 * @return The SOCKS port of the local Tor node to which requests are sent.
+	 */
+	public abstract int getSocksPort();
+
+	/**
+	 * Returns the target name for the requests sent by this client; can be
+	 * either a server name/address or an onion address.
+	 * 
+	 * @return The target name for the requests sent by this client.
+	 */
+	public abstract String getTargetName();
+
+	/**
+	 * Returns the target port for the requests sent by this client; can be
+	 * either a server port or a virtual port of a hidden service.
+	 * 
+	 * @return The target port for the requests sent by this client.
+	 */
+	public abstract int getTargetPort();
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientEventType.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientEventType.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientEventType.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -31,32 +31,59 @@
  */
 package de.uniba.wiai.lspi.puppetor;
 
-public class ClientEventType implements EventType{
-	
+/**
+ * Event types that can be fired by a client application running as thread in
+ * the background.
+ */
+public class ClientEventType implements EventType {
+
 	/**
+	 * String identifying the type of the event type.
+	 */
+	String typeString;
+
+	/**
+	 * Creates a new event type with the given type string.
+	 * 
+	 * @param typeString
+	 *            String identifying the type of the event type.
+	 */
+	public ClientEventType(String typeString) {
+		this.typeString = typeString;
+	}
+
+	public String getTypeName() {
+		return this.typeString;
+	}
+
+	/**
 	 * The client application is sending a request; this event is fired
 	 * internally and not parsed from a log statement from Tor.
 	 */
-	public static final ClientEventType CLIENT_SENDING_REQUEST = new ClientEventType();
+	public static final ClientEventType CLIENT_SENDING_REQUEST = new ClientEventType(
+			"CLIENT_SENDING_REQUEST");
 
 	/**
 	 * The client application has received a reply to a previously sent request;
 	 * this event is fired internally and not parsed from a log statement from
 	 * Tor.
 	 */
-	public static final ClientEventType CLIENT_REPLY_RECEIVED = new ClientEventType();
+	public static final ClientEventType CLIENT_REPLY_RECEIVED = new ClientEventType(
+			"CLIENT_REPLY_RECEIVED");
 
 	/**
 	 * The client application has given up waiting for the reply to a previously
 	 * sent request; this event is fired internally and not parsed from a log
 	 * statement from Tor.
 	 */
-	public static final ClientEventType CLIENT_GAVE_UP_REQUEST = new ClientEventType();
+	public static final ClientEventType CLIENT_GAVE_UP_REQUEST = new ClientEventType(
+			"CLIENT_GAVE_UP_REQUEST");
 
 	/**
 	 * The client application has completed a series of requests, whether they
 	 * were successful or not; this event is fired internally and not parsed
 	 * from a log statement from Tor.
 	 */
-	public static final ClientEventType CLIENT_REQUESTS_PERFORMED = new ClientEventType();
+	public static final ClientEventType CLIENT_REQUESTS_PERFORMED = new ClientEventType(
+			"CLIENT_REQUESTS_PERFORMED");
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -52,56 +52,31 @@
 	 * 
 	 * @return <code>DirServer</code> string to configure a node to use this
 	 *         node as directory server.
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if a problem occurs when determining the fingerprint
 	 *             of this node.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract String determineDirServerString()
-			throws TorProcessException, RemoteException;
+	public abstract String getDirServerString() throws PuppeTorException,
+			RemoteException;
 
 	/**
-	 * Writes the given (possibly empty) set of onion router fingerprints to the
-	 * <code>approved-routers</code> file of this node. This will confirm to
-	 * directory clients, that the given routers can be trusted.
+	 * Adds the given (possibly empty) set of onion router fingerprints to the
+	 * set of approved routers to confirm to directory clients, that the given
+	 * routers can be trusted. Changes are only stored locally and not written
+	 * to the <code>approved-routers</code> file to disk which will be done
+	 * when writing the configuration of this node.
 	 * 
 	 * @param approvedRouters
-	 *            The set of approved routers to be written. Each provided
-	 *            string must be formatted as
-	 *            <code>nickname 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code>.
-	 * @throws IllegalArgumentException
-	 *             Thrown if <code>null</code> is passed as parameter;
-	 *             however, if an empty set is passed, an empty
-	 *             <code>approved-routers</code> file will be written.
-	 * @throws TorProcessException
-	 *             Thrown if the <code>approved-routers</code> file cannot be
-	 *             written to disk.
-	 * @throws RemoteException
-	 *             Thrown if an error occurs when accessed remotely.
-	 */
-	public void writeApprovedRouters(Set<String> approvedRouters)
-			throws TorProcessException, RemoteException;
-
-	/**
-	 * Adds the given set of onion router fingerprints to the
-	 * <code>approved-routers</code> file of this node. This will confirm to
-	 * directory clients, that the given routers can be trusted.
-	 * 
-	 * @param approvedRouters
 	 *            The set of approved routers to be added. Each provided string
 	 *            must be formatted as
 	 *            <code>nickname 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code>.
 	 * @throws IllegalArgumentException
-	 *             Thrown if <code>null</code> is passed as parameter;
-	 *             however, if an empty set is passed, the
-	 *             <code>approved-routers</code> file will not be changed.
-	 * @throws TorProcessException
-	 *             Thrown if the <code>approved-routers</code> file cannot be
-	 *             written to disk.
+	 *             Thrown if <code>null</code> is passed as parameter.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
 	public void addApprovedRouters(Set<String> approvedRouters)
-			throws TorProcessException, RemoteException;
+			throws RemoteException;
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -36,12 +36,12 @@
 /**
  * An <code>Event</code> is created for every state change of an asynchronous
  * system component, e.g. a Tor process or a client/server application running
- * as thread in the background. In contrast to <code>NodeState</code> or
- * <code>NetworkState</code> an <code>Event</code> cannot be a pre- or
- * postconditions for a method invocation. There is no prescribed order in which
- * events are fired by a certain process or application. Some events can be
- * fired only once, others possibly multiple times. All management operations
- * for events are contained in the <code>EventManager</code>.
+ * as thread in the background. In contrast to <code>NodeState</code> an
+ * <code>Event</code> cannot be a pre- or postconditions for a method
+ * invocation. There is no prescribed order in which events are fired by a
+ * certain process or application. Some events can be fired only once, others
+ * possibly multiple times. All management operations for events are contained
+ * in the <code>EventManager</code>.
  * 
  * @author kloesing
  */
@@ -56,7 +56,7 @@
 	public abstract String getSource();
 
 	/**
-	 * Returns the type of this event.
+	 * Returns the event type.
 	 * 
 	 * @return The event type.
 	 */

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -40,7 +40,7 @@
  * asynchronous events by Tor processes and client or server applications
  * running as threads in the background. A test application can either register
  * event listeners to be notified asynchronously about events when they occur,
- * or synchronize with an event by being blocked until the certain event occurs.
+ * or synchronize with an event by being blocked until a certain event occurs.
  * 
  * @author kloesing
  */
@@ -54,7 +54,7 @@
 	 * signalized in a later invocation on the event listener, but not in both.
 	 * This prevents race conditions by eliminating the gap between registration
 	 * of an event handler and asking if an event has been fired before
-	 * registering. This method can be invoked in any node or network state.
+	 * registering.
 	 * 
 	 * @param source
 	 *            The name of the source of events that the listener is
@@ -72,7 +72,7 @@
 	 *         empty list is returned instead of <code>null</code>.
 	 * @throws IllegalArgumentException
 	 *             Thrown if <code>null</code> is passed for either of the
-	 *             parameters or if <code>source</code> is not known.
+	 *             parameters or if the <code>source</code> is unknown.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
@@ -81,16 +81,14 @@
 
 	/**
 	 * Registers the given <code>listener</code> as event listener for future
-	 * events originating from any source. This method can be invoked in any
-	 * node or network state.
+	 * events originating from any source.
 	 * 
 	 * @param listener
 	 *            The listener that wants to be notified about events from the
 	 *            given <code>source</code>. If the <code>listener</code>
-	 *            is already registered for the same <code>source</code>,
-	 *            nothing happens, i.e. the <code>listener</code> will not
-	 *            receive multiple invocations for the same event. May not be
-	 *            <code>null</code>.
+	 *            is already registered for all sources, nothing happens, i.e.
+	 *            the <code>listener</code> will not receive multiple
+	 *            invocations for the same event. May not be <code>null</code>.
 	 * @throws IllegalArgumentException
 	 *             Thrown if <code>null</code> is passed for the parameter.
 	 * @throws RemoteException
@@ -107,11 +105,11 @@
 	 *            The source of the events that the invoking thread is
 	 *            interested in. May not be <code>null</code> and must be the
 	 *            name of a previously created node, client, or server.
-	 * @throws IllegalArgumentException
-	 *             Thrown if <code>null</code> is passed as parameter or if
-	 *             <code>source</code> is unknown.
 	 * @return List of all previously observed events from the given
 	 *         <code>source</code>.
+	 * @throws IllegalArgumentException
+	 *             Thrown if <code>null</code> is passed as parameter or if
+	 *             the <code>source</code> is unknown.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
@@ -119,16 +117,16 @@
 			throws RemoteException;
 
 	/**
-	 * Returns whether the given <code>event</code> has been observed from the
-	 * given <code>source</code> before, or not.
+	 * Returns whether the given <code>eventType</code> has been observed from
+	 * the given <code>source</code> before, or not.
 	 * 
 	 * @param source
 	 *            The source of the event that the invoking thread is interested
 	 *            in. May not be <code>null</code> and must be the name of a
 	 *            previously created node, client, or server.
-	 * @param event
-	 *            The event that the invoking thread is interested int. May not
-	 *            be <code>null</code>.
+	 * @param eventType
+	 *            The event type that the invoking thread is interested in. May
+	 *            not be <code>null</code>.
 	 * @throws IllegalArgumentException
 	 *             Thrown if <code>null</code> is passed for either of the
 	 *             parameters or if <code>source</code> is unknown.
@@ -137,19 +135,14 @@
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract boolean hasEventOccured(String source, EventType event)
+	public abstract boolean hasEventOccured(String source, EventType eventType)
 			throws RemoteException;
 
 	/**
 	 * Removes the given <code>listener</code> as event listener from all
 	 * previously registered sources. If this listener is not registered for any
-	 * source, nothing happens. This method can be invoked in any node or
-	 * network state.
+	 * source, nothing happens.
 	 * 
-	 * TODO should we include the source as parameter, too, to have even more
-	 * control over removing listeners from sources? Or should we overload the
-	 * method?
-	 * 
 	 * @param listener
 	 *            The listener that shall be removed from the list of registered
 	 *            listeners. May not be <code>null</code>.
@@ -162,125 +155,130 @@
 			throws RemoteException;
 
 	/**
-	 * Checks if the given <code>event</code> has been observed from the given
-	 * <code>source</code> before; if not, blocks the invoking thread until
-	 * the next event is fired from that source. Note that this method does not
-	 * restrict waiting to a timeout, so that it could potentially block
-	 * forever! This method can be invoked in any node or network state.
+	 * Checks if the given <code>eventType</code> has been observed from the
+	 * given <code>source</code> before; if not, blocks the invoking thread
+	 * until the next event of this type is fired from that source. Note that
+	 * this method does not restrict waiting to a timeout, so that it could
+	 * potentially block forever!
 	 * 
 	 * @param source
 	 *            The source of the event that the invoking thread is willing to
 	 *            wait for. May not be <code>null</code> and must be the name
 	 *            of a previously created node, client, or server.
-	 * @param event
-	 *            The event that the invoking thread is willing to wait for from
-	 *            the given <code>source</code>. May not be <code>null</code>.
+	 * @param eventType
+	 *            The event type that the invoking thread is willing to wait for
+	 *            from the given <code>source</code>. May not be
+	 *            <code>null</code>.
 	 * @throws IllegalArgumentException
 	 *             Thrown if <code>null</code> is passed for either of the
-	 *             parameters or if <code>source</code> is unknown.
+	 *             parameters or if the <code>source</code> is unknown.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract void waitForAnyOccurence(String source, EventType event)
+	public abstract void waitForAnyOccurence(String source, EventType eventType)
 			throws RemoteException;
 
 	/**
-	 * Checks if the given <code>event</code> has been observed from the given
-	 * <code>source</code> before; if not, blocks the invoking thread until
-	 * the next event is fired from that source or the given timeout of
-	 * <code>maximumTimeToWaitInMillis</code> millis has expired. This method
-	 * can be invoked in any node or network state.
+	 * Checks if the given <code>eventType</code> has been observed from the
+	 * given <code>source</code> before; if not, blocks the invoking thread
+	 * until the next event of this type is fired from that source or the given
+	 * timeout of <code>maximumTimeToWaitInMillis</code> milliseconds has
+	 * expired.
 	 * 
 	 * @param source
 	 *            The source of the event that the invoking thread is willing to
 	 *            wait for. May not be <code>null</code> and must be the name
 	 *            of a previously created node, client, or server.
-	 * @param event
-	 *            The event that the invoking thread is willing to wait for from
-	 *            the given <code>source</code>. May not be <code>null</code>.
+	 * @param eventType
+	 *            The event type that the invoking thread is willing to wait for
+	 *            from the given <code>source</code>. May not be
+	 *            <code>null</code>.
 	 * @param maximumTimeToWaitInMillis
 	 *            The maximum time to wait in milliseconds. A positive value or
 	 *            zero restricts waiting to this time. If this value is
 	 *            negative, we will wait potentially forever.
 	 * @return <code>true</code> if an event of the given type has been fired
-	 *         by the source within the given timeout, <code>false</code>
-	 *         otherwise.
+	 *         by the <code>source</code> within the given timeout,
+	 *         <code>false</code> otherwise.
 	 * @throws IllegalArgumentException
 	 *             Thrown if an invalid value is passed for either of the
-	 *             parameters or if <code>source</code> is unknown.
+	 *             parameters or if the <code>source</code> is unknown.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract boolean waitForAnyOccurence(String source, EventType event,
-			long maximumTimeToWaitInMillis) throws RemoteException;
+	public abstract boolean waitForAnyOccurence(String source,
+			EventType eventType, long maximumTimeToWaitInMillis)
+			throws RemoteException;
 
 	/**
 	 * Blocks the invoking thread until the next <code>event</code> is fired
 	 * from the given <code>source</code>. This method only waits for the
 	 * next occurence of an event, regardless of previous occurrences. Note that
 	 * this method does not restrict waiting to a timeout, so that it could
-	 * potentially block forever! This method can be invoked in any node or
-	 * network state.
+	 * potentially block forever!
 	 * 
 	 * @param source
 	 *            The source of the event that the invoking thread is willing to
 	 *            wait for. May not be <code>null</code> and must be the name
 	 *            of a previously created node, client, or server.
-	 * @param event
-	 *            The event that the invoking thread is willing to wait for from
-	 *            the given <code>source</code>. May not be <code>null</code>.
+	 * @param eventType
+	 *            The event type that the invoking thread is willing to wait for
+	 *            from the given <code>source</code>. May not be
+	 *            <code>null</code>.
 	 * @throws IllegalArgumentException
 	 *             Thrown if <code>null</code> is passed for either of the
-	 *             parameters or if <code>source</code> is unknown.
+	 *             parameters or if the <code>source</code> is unknown.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract void waitForNextOccurence(String source, EventType event)
+	public abstract void waitForNextOccurence(String source, EventType eventType)
 			throws RemoteException;
 
 	/**
 	 * Blocks the invoking thread until the next <code>event</code> is fired
 	 * from the given <code>source</code> or the given timeout of
-	 * <code>maximumTimeToWaitInMillis</code> millis has expired. This method
+	 * <code>maximumTimeToWaitInMillis</code> milliseconds has expired. This method
 	 * only waits for the next occurence of an event, regardless of previous
-	 * occurrences. This method can be invoked in any node or network state.
+	 * occurrences.
 	 * 
 	 * @param source
 	 *            The source of the event that the invoking thread is willing to
 	 *            wait for. May not be <code>null</code> and must be the name
 	 *            of a previously created node, client, or server.
-	 * @param event
-	 *            The event that the invoking thread is willing to wait for from
-	 *            the given <code>source</code>. May not be <code>null</code>.
+	 * @param eventType
+	 *            The event type that the invoking thread is willing to wait for
+	 *            from the given <code>source</code>. May not be
+	 *            <code>null</code>.
 	 * @param maximumTimeToWaitInMillis
 	 *            The maximum time to wait in milliseconds. A positive value or
 	 *            zero restricts waiting to this time. If this value is
 	 *            negative, we will wait potentially forever.
 	 * @return <code>true</code> if an event of the given type has been fired
-	 *         by the source within the given timeout, <code>false</code>
-	 *         otherwise.
+	 *         by the <code>source</code> within the given timeout,
+	 *         <code>false</code> otherwise.
 	 * @throws IllegalArgumentException
 	 *             Thrown if an invalid value is passed for either of the
-	 *             parameters or if <code>source</code> is unknown.
+	 *             parameters or if the <code>source</code> is unknown.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
 	public abstract boolean waitForNextOccurence(String source,
-			EventType event, long maximumTimeToWaitInMillis)
+			EventType eventType, long maximumTimeToWaitInMillis)
 			throws RemoteException;
 
 	/**
 	 * Registers a new event type by passing a pattern string that can be
 	 * applied to a regular expression when parsing Tor log statements. This is
 	 * useful for log statements that are only included in modified Tor
-	 * versions.
+	 * versions. Therefore, the event type may be an instance of a self-defined
+	 * class that implements <code>EventType</code>.
 	 * 
 	 * @param patternString
 	 *            The pattern string that will be used for parsing Tor log
 	 *            statements; the syntax corresponds to java.util.regex.Pattern.
 	 * @param eventType
-	 *            The event type that will be fired when a log statement was
-	 *            parsed that includes the given pattern.
+	 *            The event type of the event that will be fired when a log
+	 *            statement was parsed that includes the given pattern.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventType.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventType.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventType.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -40,4 +40,11 @@
  */
 public interface EventType {
 
+	/**
+	 * Returns a string representation of the event type name for display
+	 * purposes.
+	 * 
+	 * @return String representation of the event type name.
+	 */
+	public abstract String getTypeName();
 }

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/HiddenService.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/HiddenService.java	                        (rev 0)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/HiddenService.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -0,0 +1,56 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * A <code>HiddenService</code> instance contains all configurations of a
+ * hidden service that is registered at a node.
+ * 
+ * @author kloesing
+ */
+public interface HiddenService {
+
+	/**
+	 * Determines the onion address for a previously added hidden service with
+	 * name <code>serviceName</code>. Requires that the node has been
+	 * started, i.e. is in state <code>NodeState.RUNNING</code>, and is
+	 * configured to provide this hidden service.
+	 * 
+	 * @return The onion address string consisting of 16 base32 chars plus
+	 *         ".onion" for hidden service versions 0 and 1 or 16 base32 chars
+	 *         plus "." plus 24 base32 chars plus ".onion" for hidden service
+	 *         version 2.
+	 * @throws IllegalArgumentException
+	 *             Thrown if <code>null</code> or a zero-length string is
+	 *             passed as parameter.
+	 * @throws IllegalStateException
+	 *             Thrown if the node at which this hidden service is configured
+	 *             is not in state <code>NodeState.RUNNING</code>.
+	 * @throws PuppeTorException
+	 *             Thrown if the onion address of this hidden service could not
+	 *             be read, which is also the case when the node's configuration
+	 *             has not been written and the node has not been HUP'ed after
+	 *             configuring the hidden service.
+	 */
+	public abstract String determineOnionAddress() throws PuppeTorException;
+
+	/**
+	 * Returns the name of the hidden service.
+	 * 
+	 * @return The name of the hidden service.
+	 */
+	public String getServiceName();
+
+	/**
+	 * Returns the port on which the service listens for requests.
+	 * 
+	 * @return The port on which the service listens for requests.
+	 */
+	public int getServicePort();
+
+	/**
+	 * Returns the virtual port that this hidden service runs on as it is
+	 * announced to clients.
+	 * 
+	 * @return The virtual port of this hidden service.
+	 */
+	public int getVirtualPort();
+}

Copied: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/HiddenServiceEventType.java (from rev 11669, puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/AliceEventType.java)
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/HiddenServiceEventType.java	                        (rev 0)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/HiddenServiceEventType.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * Event types that can be fired by all Tor processes performing hidden-service
+ * operations.
+ */
+public class HiddenServiceEventType implements EventType {
+
+	/**
+	 * String identifying the type of the event type.
+	 */
+	String typeString;
+
+	/**
+	 * Creates a new event type with the given type string.
+	 * 
+	 * @param typeString
+	 *            String identifying the type of the event type.
+	 */
+	public HiddenServiceEventType(String typeString) {
+		this.typeString = typeString;
+	}
+
+	public String getTypeName() {
+		return this.typeString;
+	}
+
+	/**
+	 * Alice has received an onion request; this event is parsed from a log
+	 * statement in connection_ap_handshake_rewrite_and_attach().
+	 */
+	public static final HiddenServiceEventType ALICE_ONION_REQUEST_RECEIVED = new HiddenServiceEventType(
+			"ALICE_ONION_REQUEST_RECEIVED");
+
+	/**
+	 * Alice sends a fetch request for a hidden service descriptor to a
+	 * directory server; this event is parsed from a log statement in
+	 * rend_client_refetch_renddesc().
+	 */
+	public static final HiddenServiceEventType ALICE_SENDING_FETCH_DESC = new HiddenServiceEventType(
+			"ALICE_SENDING_FETCH_DESC");
+
+	/**
+	 * Alice receives a reply to a previous fetch request for a hidden service
+	 * descriptors from a directory server; this event is parsed from a log
+	 * statement in connection_dir_client_reached_eof().
+	 */
+	public static final HiddenServiceEventType ALICE_DESC_FETCHED_RECEIVED = new HiddenServiceEventType(
+			"ALICE_DESC_FETCHED_RECEIVED");
+
+	/**
+	 * Alice has built a circuit to a rendezvous point and sends an
+	 * ESTABLISH_RENDEZVOUS cell; this event is parsed from a log statement in
+	 * rend_client_send_establish_rendezvous().
+	 */
+	public static final HiddenServiceEventType ALICE_BUILT_REND_CIRC_SENDING_ESTABLISH_RENDEZVOUS = new HiddenServiceEventType(
+			"ALICE_BUILT_REND_CIRC_SENDING_ESTABLISH_RENDEZVOUS");
+
+	/**
+	 * Alice receives a RENDEZVOUS_ESTABLISHED cell from a rendezvous point;
+	 * this event is parsed from a log statement in
+	 * rend_client_rendezvous_acked().
+	 */
+	public static final HiddenServiceEventType ALICE_RENDEZVOUS_ESTABLISHED_RECEIVED = new HiddenServiceEventType(
+			"ALICE_RENDEZVOUS_ESTABLISHED_RECEIVED");
+
+	/**
+	 * Alice has built a circuit to an introduction point (which does not
+	 * automatically lead to sending an INTRODUCE1 cell, because the rendezvous
+	 * circuit might not be ready); this event is parsed from a log statement in
+	 * rend_client_introcirc_has_opened().
+	 */
+	public static final HiddenServiceEventType ALICE_BUILT_INTRO_CIRC = new HiddenServiceEventType(
+			"ALICE_BUILT_INTRO_CIRC");
+
+	/**
+	 * Alice sends an INTRODUCE1 cell to an introduction point; this event is
+	 * parsed from a log statement in rend_client_send_introduction().
+	 */
+	public static final HiddenServiceEventType ALICE_SENDING_INTRODUCE1 = new HiddenServiceEventType(
+			"ALICE_SENDING_INTRODUCE1");
+
+	/**
+	 * Alice has received an INTRODUCE_ACK cell as an acknowledgement to a
+	 * previously sent INTRODUCE1 cell; this event is parsed from a log
+	 * statement in rend_client_introduction_acked().
+	 */
+	public static final HiddenServiceEventType ALICE_INTRODUCE_ACK_RECEIVED = new HiddenServiceEventType(
+			"ALICE_INTRODUCE_ACK_RECEIVED");
+
+	/**
+	 * Alice has received a RENDEZVOUS2 cell and can now open an application
+	 * connection to the client; this event is parsed from a log statement in
+	 * rend_client_receive_rendezvous().
+	 */
+	public static final HiddenServiceEventType ALICE_RENDEZVOUS2_RECEIVED_APP_CONN_OPENED = new HiddenServiceEventType(
+			"ALICE_RENDEZVOUS2_RECEIVED_APP_CONN_OPENED");
+
+	/**
+	 * Bob has built a circuit to an introduction point and sends an
+	 * ESTABLISH_INTRO cell; this event is parsed from a log statement in
+	 * rend_service_intro_has_opened().
+	 */
+	public static final HiddenServiceEventType BOB_BUILT_INTRO_CIRC_SENDING_ESTABLISH_INTRO = new HiddenServiceEventType(
+			"BOB_BUILT_INTRO_CIRC_SENDING_ESTABLISH_INTRO");
+
+	/**
+	 * Bob has received an INTRO_ESTABLISHED cell, i.e. a node has confirmed to
+	 * work as introduction point; this event is parsed from a log statement in
+	 * rend_service_intro_established().
+	 */
+	public static final HiddenServiceEventType BOB_INTRO_ESTABLISHED_RECEIVED = new HiddenServiceEventType(
+			"BOB_INTRO_ESTABLISHED_RECEIVED");
+
+	/**
+	 * Bob posts a hidden service descriptor to the directory servers; this
+	 * event is parsed from a log statement in upload_service_descriptor().
+	 */
+	public static final HiddenServiceEventType BOB_SENDING_PUBLISH_DESC = new HiddenServiceEventType(
+			"BOB_SENDING_PUBLISH_DESC");
+
+	/**
+	 * Bob received a response from a directory server to a previous publish
+	 * request; this event is parsed from a log statement in
+	 * connection_dir_client_reached_eof().
+	 */
+	public static final HiddenServiceEventType BOB_DESC_PUBLISHED_RECEIVED = new HiddenServiceEventType(
+			"BOB_DESC_PUBLISHED_RECEIVED");
+
+	/**
+	 * Bob has received an INTRODUCE2 cell, i.e. a node wants to establish a
+	 * connection, and will now try to establish a circuit to the client's
+	 * rendezvous point; this event is parsed from a log statement in
+	 * rend_service_introduce().
+	 */
+	public static final HiddenServiceEventType BOB_INTRODUCE2_RECEIVED = new HiddenServiceEventType(
+			"BOB_INTRODUCE2_RECEIVED");
+
+	/**
+	 * Bob has built a circuit to a rendezvous point and sends a RENDEZVOUS1
+	 * cell; this event is parsed from a log statement in
+	 * rend_service_rendezvous_has_opened().
+	 */
+	public static final HiddenServiceEventType BOB_BUILT_REND_CIRC_SENDING_RENDEZVOUS1 = new HiddenServiceEventType(
+			"BOB_BUILT_REND_CIRC_SENDING_RENDEZVOUS1");
+
+	/**
+	 * Bob opens a connection to the actual hidden server; this event is parsed
+	 * from a log statement in connection_exit_begin_conn().
+	 */
+	public static final HiddenServiceEventType BOB_APP_CONN_OPENED = new HiddenServiceEventType(
+			"BOB_APP_CONN_OPENED");
+
+	/**
+	 * The directory server has received a descriptor post request; this event
+	 * is parsed from a log statement in directory_handle_command_post().
+	 */
+	public static final HiddenServiceEventType DIR_PUBLISH_DESC_RECEIVED = new HiddenServiceEventType(
+			"DIR_PUBLISH_DESC_RECEIVED");
+
+	/**
+	 * The directory server has received a descriptor fetch request; this event
+	 * is parsed from a log statement in directory_handle_command_get().
+	 */
+	public static final HiddenServiceEventType DIR_FETCH_DESC_RECEIVED = new HiddenServiceEventType(
+			"DIR_FETCH_DESC_RECEIVED");
+
+	/**
+	 * The node received an ESTABLISH_INTRO cell, i.e. was requested to work as
+	 * introduction point, and replies with an INTRO_ESTABLISHED cell; this
+	 * event is parsed from a log statement in rend_mid_establish_intro().
+	 */
+	public static final HiddenServiceEventType IPO_RECEIVED_ESTABLISH_INTRO_SENDING_INTRO_ESTABLISHED = new HiddenServiceEventType(
+			"IPO_RECEIVED_ESTABLISH_INTRO_SENDING_INTRO_ESTABLISHED");
+
+	/**
+	 * The introduction point received an INTRODUCE1 cell and reacts by sending
+	 * an INTRODUCE2 cell to Bob and an INTRODUCE_ACK cell to Alice; this event
+	 * is parsed from a log statement in rend_mid_introduce().
+	 */
+	public static final HiddenServiceEventType IPO_RECEIVED_INTRODUCE1_SENDING_INTRODUCE2_AND_INTRODUCE_ACK = new HiddenServiceEventType(
+			"IPO_RECEIVED_INTRODUCE1_SENDING_INTRODUCE2_AND_INTRODUCE_ACK");
+
+	/**
+	 * The node received an ESTABLISH_RENDEZVOUS cell, i.e. was requested to
+	 * work as rendezvous point, and replies with a RENDEZVOUS_ESTABLISHED cell;
+	 * this event is parsed from a log statement in
+	 * rend_mid_establish_rendezvous().
+	 */
+	public static final HiddenServiceEventType RPO_RECEIVED_ESTABLISH_RENDEZVOUS_SENDING_RENDEZVOUS_ESTABLISHED = new HiddenServiceEventType(
+			"RPO_RECEIVED_ESTABLISH_RENDEZVOUS_SENDING_RENDEZVOUS_ESTABLISHED");
+
+	/**
+	 * The rendezvous point received a RENDEZVOUS1 cell and reacts by sending a
+	 * RENDEZVOUS2 cell to Alice; this event is parsed from a log statement in
+	 * rend_mid_rendezvous().
+	 */
+	public static final HiddenServiceEventType RPO_RECEIVING_RENDEZVOUS1_SENDING_RENDEZVOUS2 = new HiddenServiceEventType(
+			"RPO_RECEIVING_RENDEZVOUS1_SENDING_RENDEZVOUS2");
+
+}
\ No newline at end of file

Deleted: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/MiscEventType.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/MiscEventType.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/MiscEventType.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * 
- *     * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-public class MiscEventType implements EventType {
-
-	/**
-	 * The hidden service directory has stored a v2 descriptor; this event can
-	 * only be parsed from a log statement in a modified Tor!
-	 */
-	public static final MiscEventType HSDIR_DESC_STORED = new MiscEventType();
-
-	/**
-	 * The directory server has received a descriptor post request; this event
-	 * is parsed from a log statement in directory_handle_command_post().
-	 */
-	public static final MiscEventType DIR_PUBLISH_DESC_RECEIVED = new MiscEventType();
-
-	/**
-	 * The directory server has received a descriptor fetch request; this event
-	 * is parsed from a log statement in directory_handle_command_get().
-	 */
-	public static final MiscEventType DIR_FETCH_DESC_RECEIVED = new MiscEventType();
-
-	/**
-	 * The node received an ESTABLISH_INTRO cell, i.e. was requested to work as
-	 * introduction point, and replies with an INTRO_ESTABLISHED cell; this
-	 * event is parsed from a log statement in rend_mid_establish_intro().
-	 */
-	public static final MiscEventType IPO_RECEIVED_ESTABLISH_INTRO_SENDING_INTRO_ESTABLISHED = new MiscEventType();
-
-	/**
-	 * The introduction point received an INTRODUCE1 cell and reacts by sending
-	 * an INTRODUCE2 cell to Bob and an INTRODUCE_ACK cell to Alice; this event
-	 * is parsed from a log statement in rend_mid_introduce().
-	 */
-	public static final MiscEventType IPO_RECEIVED_INTRODUCE1_SENDING_INTRODUCE2_AND_INTRODUCE_ACK = new MiscEventType();
-
-	/**
-	 * The node received an ESTABLISH_RENDEZVOUS cell, i.e. was requested to
-	 * work as rendezvous point, and replies with an RENDEZVOUS_ESTABLISHED
-	 * cell; this event is parsed from a log statement in
-	 * rend_mid_establish_rendezvous().
-	 */
-	public static final MiscEventType RPO_RECEIVED_ESTABLISH_RENDEZVOUS_SENDING_RENDEZVOUS_ESTABLISHED = new MiscEventType();
-
-	/**
-	 * The rendezvous point received a RENDEZVOUS1 cell and reacts by sending a
-	 * RENDEZVOUS2 cell to Alice; this event is parsed from a log statement in
-	 * rend_mid_rendezvous().
-	 */
-	public static final MiscEventType RPO_RECEIVING_RENDEZVOUS1_SENDING_RENDEZVOUS2 = new MiscEventType();
-}

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -32,13 +32,9 @@
 package de.uniba.wiai.lspi.puppetor;
 
 import java.io.File;
-import java.net.MalformedURLException;
-import java.rmi.NotBoundException;
 import java.rmi.Remote;
 import java.rmi.RemoteException;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * A Network instance constitutes the central object of any test run and is
@@ -53,76 +49,119 @@
 
 	/**
 	 * <p>
-	 * Configures the nodes in this network so that they can run in a private
-	 * network and don't require public directory servers or onion routers from
-	 * the Internet. This configuration should be done after configuring the
-	 * nodes and before writing configurations to disk. This operation can only
-	 * be invoked, if network status is
-	 * <code>NetworkState.CONFIGURING_NODES</code>.
+	 * Configures this network as private Tor network by exchanging directory
+	 * strings and router fingerprints between the nodes of this network.
+	 * Afterwards, the nodes will be able to run a private Tor network,
+	 * separated from public directory servers and onion routers.
 	 * </p>
 	 * 
 	 * <p>
-	 * The main requirement for this method lies in the fact that all nodes need
-	 * to be configured so that they accept our own directory nodes instead of
-	 * the pre-configured directory nodes from the public Tor network. This
-	 * configuration requires the fingerprints of all directory nodes. These
-	 * fingerprints are written to disk by the directory nodes as soon as they
-	 * are started. But the directories need to be configured before being
-	 * started, too, in order to prevent them from becoming part of the public
-	 * Tor network. And now we have the chicken or the egg dilemma.
+	 * The configuration is done in two steps:
+	 * <ol>
+	 * <li>Directory strings of directory nodes are added to the configurations
+	 * of all nodes in the other network.</li>
+	 * <li>Router fingerprints of all router and directory nodes are added to
+	 * the <code>approved-routers</code> files of the directory nodes.</li>
+	 * </ol>
 	 * </p>
 	 * 
 	 * <p>
-	 * The non-trivial solution is to configure the directory nodes with a fake
-	 * directory configuration and start them using the
-	 * <code>--list-fingerprint</code> option. Hence they write a
-	 * <code>fingerprint</code> file to disk and shut down immediately. This
-	 * fingerprint can be read, and all nodes can be configured to use the
-	 * directory using this fingerprint.
+	 * This operation may be invoked in any state of the contained nodes.
+	 * However, a network that does not have directory nodes of its own but
+	 * relies on directory nodes of a merged network <b>should not be started
+	 * before being configured as private network!</b> Otherwise it would
+	 * connect to the public Tor network before being merged with the other
+	 * private Tor network. However, it may also be invoked at a later time,
+	 * e.g. to admit new nodes.
 	 * </p>
 	 * 
 	 * <p>
-	 * A second, non-trivial task is to authorize routers and directory nodes.
-	 * Therefore, an authoritative directory needs to know all fingerprints of
-	 * authorized nodes. They are stored in the <code>approved-routers</code>
-	 * file in the working directory of the directory node.
+	 * This operation does not write any configurations to disk and neither
+	 * starts a nodes nor sends HUP signals to running nodes. These operations
+	 * are left to the application, so that they have more control over the
+	 * network behavior.
 	 * </p>
 	 * 
 	 * <p>
-	 * The complete task is encapsulated in this method for convenience.
-	 * However, all operations could also be performed directly on the nodes, if
-	 * required.
+	 * Applications need to ensure that there are enough directory nodes (2) and
+	 * router nodes (3) in the network to allow normal operation.
 	 * </p>
 	 * 
-	 * TODO check if we have enough directory and router nodes to build a
-	 * private network? How many are required? 2 dirs and 3 routers?
+	 * @throws PuppeTorException
+	 *             Thrown if an I/O problem occurs while determining the nodes'
+	 *             fingerprints.
+	 * @throws RemoteException
+	 *             Thrown if an error occurs when accessed remotely.
+	 */
+	public abstract void configureAsPrivateNetwork() throws PuppeTorException,
+			RemoteException;
+
+	/**
+	 * <p>
+	 * Merges this network with another private Tor network by exchanging
+	 * directory strings and router fingerprints. Afterwards, the nodes in both
+	 * networks will consider the two networks a single, larger private Tor
+	 * network.
+	 * </p>
 	 * 
-	 * @throws IllegalStateException
-	 *             Thrown if network is not in state
-	 *             <code>NetworkState.CONFIGURING_NODES</code>.
-	 * @throws TorProcessException
-	 *             Thrown if an I/O problem occurs while starting nodes with the
-	 *             <code>--list-fingerprint</code> option, reading files from
-	 *             the nodes' working directories, or writing the
-	 *             <code>approved-routers</code> files.
+	 * <p>
+	 * The configuration is done in two steps:
+	 * <ol>
+	 * <li>Directory strings of all directory nodes in this network are added
+	 * to the configurations of all nodes in the other network and vice versa.</li>
+	 * <li>Router fingerprints of all router and directory nodes in this
+	 * network are added to the <code>approved-routers</code> files of all
+	 * directory nodes in the other network and vice versa.</li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * <p>
+	 * This operation may be invoked in any state of the contained nodes.
+	 * However, a network that does not have directory nodes of its own but
+	 * relies on directory nodes of a merged network <b>should not be started
+	 * before merging!</b> Otherwise it would connect to the public Tor network
+	 * before being merged with the other private Tor network. However, it may
+	 * also be invoked at a later time, e.g. to admit new nodes.
+	 * </p>
+	 * 
+	 * <p>
+	 * This operation does not write any configurations to disk and neither
+	 * starts a nodes nor sends HUP signals to running nodes. These operations
+	 * are left to the application, so that they have more control over the
+	 * network behavior.
+	 * </p>
+	 * 
+	 * <p>
+	 * Note that this operation is only effective if there are directory nodes
+	 * in either of the two networks. Otherwise, no information will be
+	 * exchanged between the two networks. Applications need to ensure that
+	 * there are in total enough directory nodes (2) and router nodes (3) in
+	 * both networks to allow normal operation.
+	 * </p>
+	 * 
+	 * @param remoteNetwork
+	 *            The remote network to merge this network with.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
+	 * @throws PuppeTorException
+	 *             Thrown if an I/O problem occurs while reading directory
+	 *             strings or router fingerprints, while writing the new
 	 */
-	public abstract void configureAsPrivateNetwork()
-			throws TorProcessException, RemoteException;
+	public abstract void configureAsInterconnectedPrivateNetwork(
+			Network remoteNetwork) throws RemoteException, PuppeTorException;
 
 	/**
 	 * Creates a new client application, but does not yet perform a request.
 	 * 
 	 * @param clientApplicationName
 	 *            The name for this client application, which is used for
-	 *            logging purposes only. May neither be <code>null</code> or a
-	 *            zero-length string. The name needs to be unique in this
-	 *            network.
+	 *            logging purposes and as event source. May neither be
+	 *            <code>null</code> or a zero-length string. The name needs to
+	 *            be unique in this network.
 	 * @param targetAddress
 	 *            The target for requests sent by this client application. Can
-	 *            be an IP address, a domain name, or an onion address. May
-	 *            neither be <code>null</code> or a zero-length string.
+	 *            be a publicly available URL or an onion address. May neither
+	 *            be <code>null</code> or a zero-length string.
 	 * @param targetPort
 	 *            The TCP port for requests sent by this client application. If
 	 *            the target address is an onion address, this port is the
@@ -146,19 +185,15 @@
 	/**
 	 * Creates a new directory node with automatically assigned ports and adds
 	 * it to the network, but does not yet write its configuration to disk or
-	 * start the corresponding Tor process. This operation can only be invoked,
-	 * if network status is <code>NetworkState.CONFIGURING_NODES</code>.
+	 * start the corresponding Tor process.
 	 * 
 	 * @param nodeName
 	 *            The name for this node, which is used as name for the working
-	 *            directory, for logging purposes, and as node nickname. May
-	 *            neither be <code>null</code> or have zero or more than 19
-	 *            alpha-numeric characters. The node name needs to be unique in
-	 *            this network.
+	 *            directory, for logging purposes, as node nickname, and as
+	 *            event source. May neither be <code>null</code> or have zero
+	 *            or more than 19 alpha-numeric characters. The node name needs
+	 *            to be unique in this network.
 	 * @return Reference to the created directory node.
-	 * @throws IllegalStateException
-	 *             Thrown if network is not in state
-	 *             <code>NetworkState.CONFIGURING_NODES</code>.
 	 * @throws IllegalArgumentException
 	 *             Thrown if an invalid value is given as node name.
 	 * @throws RemoteException
@@ -171,23 +206,19 @@
 	 * Creates a new directory node with automatically assigned ports that will
 	 * listen on the given IP address and adds it to the network, but does not
 	 * yet write its configuration to disk or start the corresponding Tor
-	 * process. This operation can only be invoked, if network status is
-	 * <code>NetworkState.CONFIGURING_NODES</code>.
+	 * process.
 	 * 
 	 * @param nodeName
 	 *            The name for this node, which is used as name for the working
-	 *            directory, for logging purposes, and as node nickname. May
-	 *            neither be <code>null</code> or have zero or more than 19
-	 *            alpha-numeric characters. The node name needs to be unique in
-	 *            this network.
+	 *            directory, for logging purposes, as node nickname, and as
+	 *            event source. May neither be <code>null</code> or have zero
+	 *            or more than 19 alpha-numeric characters. The node name needs
+	 *            to be unique in this network.
 	 * @param serverIpAddress
 	 *            The IP address on which the node will listen. Must be a valid
 	 *            IP v4 address in dotted decimal notation. May not be
 	 *            <code>null</code>.
 	 * @return Reference to the created directory node.
-	 * @throws IllegalStateException
-	 *             Thrown if network is not in state
-	 *             <code>NetworkState.CONFIGURING_NODES</code>.
 	 * @throws IllegalArgumentException
 	 *             Thrown if an invalid value is given as node name.
 	 * @throws RemoteException
@@ -199,15 +230,13 @@
 	/**
 	 * Creates a new directory node and adds it to the network, but does not yet
 	 * write its configuration to disk or start the corresponding Tor process.
-	 * This operation can only be invoked, if network status is
-	 * <code>NetworkState.CONFIGURING_NODES</code>.
 	 * 
 	 * @param nodeName
 	 *            The name for this node, which is used as name for the working
-	 *            directory, for logging purposes, and as node nickname. May
-	 *            neither be <code>null</code> or have zero or more than 19
-	 *            alpha-numeric characters. The node name needs to be unique in
-	 *            this network.
+	 *            directory, for logging purposes, as node nickname, and as
+	 *            event source. May neither be <code>null</code> or have zero
+	 *            or more than 19 alpha-numeric characters. The node name needs
+	 *            to be unique in this network.
 	 * @param controlPort
 	 *            The TCP port on which the corresponding Tor process will wait
 	 *            for a controller. May not be negative or greater than 65535.
@@ -224,9 +253,6 @@
 	 *            for incoming directory requests. May not be negative or
 	 *            greater than 65535.
 	 * @return Reference to the created directory node.
-	 * @throws IllegalStateException
-	 *             Thrown if network is not in state
-	 *             <code>NetworkState.CONFIGURING_NODES</code>.
 	 * @throws IllegalArgumentException
 	 *             Thrown if an invalid value is given for either of the
 	 *             parameters.
@@ -240,15 +266,14 @@
 	/**
 	 * Creates a new directory node that will listen on the given IP address and
 	 * adds it to the network, but does not yet write its configuration to disk
-	 * or start the corresponding Tor process. This operation can only be
-	 * invoked, if network status is <code>NetworkState.CONFIGURING_NODES</code>.
+	 * or start the corresponding Tor process.
 	 * 
 	 * @param nodeName
 	 *            The name for this node, which is used as name for the working
-	 *            directory, for logging purposes, and as node nickname. May
-	 *            neither be <code>null</code> or have zero or more than 19
-	 *            alpha-numeric characters. The node name needs to be unique in
-	 *            this network.
+	 *            directory, for logging purposes, as node nickname, and as
+	 *            event source. May neither be <code>null</code> or have zero
+	 *            or more than 19 alpha-numeric characters. The node name needs
+	 *            to be unique in this network.
 	 * @param controlPort
 	 *            The TCP port on which the corresponding Tor process will wait
 	 *            for a controller. May not be negative or greater than 65535.
@@ -269,9 +294,6 @@
 	 *            IP v4 address in dotted decimal notation. May not be
 	 *            <code>null</code>.
 	 * @return Reference to the created directory node.
-	 * @throws IllegalStateException
-	 *             Thrown if network is not in state
-	 *             <code>NetworkState.CONFIGURING_NODES</code>.
 	 * @throws IllegalArgumentException
 	 *             Thrown if an invalid value is given for either of the
 	 *             parameters.
@@ -288,10 +310,11 @@
 	 * disk or start the corresponding Tor process.
 	 * 
 	 * @param nodeName
-	 *            The name for this node, which is only used as name for the
-	 *            working directory and for logging purposes. May neither be
-	 *            <code>null</code> or have zero or more than 19 alpha-numeric
-	 *            characters. The node name needs to be unique in this network.
+	 *            The name for this node, which is used as name for the working
+	 *            directory, for logging purposes, and as event source. May
+	 *            neither be <code>null</code> or have zero or more than 19
+	 *            alpha-numeric characters. The node name needs to be unique in
+	 *            this network.
 	 * @return Reference to the created proxy node.
 	 * @throws IllegalArgumentException
 	 *             Thrown if an invalid value is given as node name.
@@ -307,10 +330,11 @@
 	 * Tor process.
 	 * 
 	 * @param nodeName
-	 *            The name for this node, which is only used as name for the
-	 *            working directory and for logging purposes. May neither be
-	 *            <code>null</code> or have zero or more than 19 alpha-numeric
-	 *            characters. The node name needs to be unique in this network.
+	 *            The name for this node, which is used as name for the working
+	 *            directory, for logging purposes, and as event source. May
+	 *            neither be <code>null</code> or have zero or more than 19
+	 *            alpha-numeric characters. The node name needs to be unique in
+	 *            this network.
 	 * @param controlPort
 	 *            The TCP port on which the corresponding Tor process will wait
 	 *            for a controller. May not be negative or greater than 65535.
@@ -335,10 +359,10 @@
 	 * 
 	 * @param nodeName
 	 *            The name for this node, which is used as name for the working
-	 *            directory, for logging purposes, and as node nickname. May
-	 *            neither be <code>null</code> or have zero or more than 19
-	 *            alpha-numeric characters. The node name needs to be unique in
-	 *            this network.
+	 *            directory, for logging purposes, as node nickname, and as
+	 *            event source. May neither be <code>null</code> or have zero
+	 *            or more than 19 alpha-numeric characters. The node name needs
+	 *            to be unique in this network.
 	 * @return Reference to the created router node.
 	 * @throws IllegalArgumentException
 	 *             Thrown if an invalid value is given as node name.
@@ -355,10 +379,10 @@
 	 * 
 	 * @param nodeName
 	 *            The name for this node, which is used as name for the working
-	 *            directory, for logging purposes, and as node nickname. May
-	 *            neither be <code>null</code> or have zero or more than 19
-	 *            alpha-numeric characters. The node name needs to be unique in
-	 *            this network.
+	 *            directory, for logging purposes, as node nickname, and as
+	 *            event source. May neither be <code>null</code> or have zero
+	 *            or more than 19 alpha-numeric characters. The node name needs
+	 *            to be unique in this network.
 	 * @param controlPort
 	 *            The TCP port on which the corresponding Tor process will wait
 	 *            for a controller. May not be negative or greater than 65535.
@@ -393,10 +417,10 @@
 	 * 
 	 * @param nodeName
 	 *            The name for this node, which is used as name for the working
-	 *            directory, for logging purposes, and as node nickname. May
-	 *            neither be <code>null</code> or have zero or more than 19
-	 *            alpha-numeric characters. The node name needs to be unique in
-	 *            this network.
+	 *            directory, for logging purposes, as node nickname, and as
+	 *            event source. May neither be <code>null</code> or have zero
+	 *            or more than 19 alpha-numeric characters. The node name needs
+	 *            to be unique in this network.
 	 * @param serverIpAddress
 	 *            The IP address on which the node will listen. Must be a valid
 	 *            IP v4 address in dotted decimal notation. May not be
@@ -417,10 +441,10 @@
 	 * 
 	 * @param nodeName
 	 *            The name for this node, which is used as name for the working
-	 *            directory, for logging purposes, and as node nickname. May
-	 *            neither be <code>null</code> or have zero or more than 19
-	 *            alpha-numeric characters. The node name needs to be unique in
-	 *            this network.
+	 *            directory, for logging purposes, as node nickname, and as
+	 *            event source. May neither be <code>null</code> or have zero
+	 *            or more than 19 alpha-numeric characters. The node name needs
+	 *            to be unique in this network.
 	 * @param controlPort
 	 *            The TCP port on which the corresponding Tor process will wait
 	 *            for a controller. May not be negative or greater than 65535.
@@ -458,9 +482,9 @@
 	 * 
 	 * @param serverApplicationName
 	 *            The name for this server application, which is used for
-	 *            logging purposes only. May neither be <code>null</code> or a
-	 *            zero-length string. The name needs to be unique in this
-	 *            network.
+	 *            logging purposes and as event source. May neither be
+	 *            <code>null</code> or a zero-length string. The name needs to
+	 *            be unique in this network.
 	 * @return Reference to the created server application.
 	 * @throws IllegalArgumentException
 	 *             Thrown if an invalid value is given as server application
@@ -477,9 +501,9 @@
 	 * 
 	 * @param serverApplicationName
 	 *            The name for this server application, which is used for
-	 *            logging purposes only. May neither be <code>null</code> or a
-	 *            zero-length string. The name needs to be unique in this
-	 *            network.
+	 *            logging purposes and as event source. May neither be
+	 *            <code>null</code> or a zero-length string. The name needs to
+	 *            be unique in this network.
 	 * @param serverPort
 	 *            The TCP port on which the server will wait for incoming
 	 *            requests. May not be negative or greater than 65535.
@@ -504,15 +528,6 @@
 	public abstract EventManager getEventManager() throws RemoteException;
 
 	/**
-	 * Returns the current network state.
-	 * 
-	 * @return Current network state.
-	 * @throws RemoteException
-	 *             Thrown if an error occurs when accessed remotely.
-	 */
-	public abstract NetworkState getNetworkState() throws RemoteException;
-
-	/**
 	 * Returns (a copy of) the map containing the names of all directory nodes
 	 * as keys and the corresponding directory nodes as values.
 	 * 
@@ -528,65 +543,46 @@
 	 * (only those that are not acting as directory nodes at the same time) as
 	 * keys and the corresponding router nodes as values.
 	 * 
-	 * TODO is this important: returns the stubs for remote access.
-	 * 
 	 * @return Map containing all router nodes.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public Map<String, RouterNode> getAllRouterNodes() throws RemoteException;
+	public abstract Map<String, RouterNode> getAllRouterNodes()
+			throws RemoteException;
 
 	/**
 	 * Returns (a copy of) the map containing the names of all proxy nodes (only
 	 * those that are not acting as router or directory nodes at the same time)
 	 * as keys and the corresponding proxy nodes as values.
 	 * 
-	 * TODO is this important: returns the stubs for remote access.
-	 * 
 	 * @return Map containing all proxy nodes.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public Map<String, ProxyNode> getAllProxyNodes() throws RemoteException;
-
-	/**
-	 * Returns the <code>ProxyNode</code> with name <code>nodeName</code> or
-	 * <code>null</code> if no such node exists.
-	 * 
-	 * @param nodeName
-	 *            The node name to look up.
-	 * @return The <code>ProxyNode</code> with name <code>nodeName</code>.
-	 * @throws RemoteException
-	 *             Thrown if an error occurs when accessed remotely.
-	 */
-	public abstract ProxyNode getProxyNode(String nodeName)
+	public abstract Map<String, ProxyNode> getAllProxyNodes()
 			throws RemoteException;
 
 	/**
-	 * Returns the <code>RouterNode</code> with name <code>nodeName</code>
-	 * or <code>null</code> if no such node exists.
+	 * Returns (a copy of) the map containing the names of all nodes as keys and
+	 * the corresponding proxy nodes as values.
 	 * 
-	 * @param nodeName
-	 *            The node name to look up.
-	 * @return The <code>RouterNode</code> with name <code>nodeName</code>.
+	 * @return Map containing all nodes.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract RouterNode getRouterNode(String nodeName)
-			throws RemoteException;
+	public abstract Map<String, ProxyNode> getAllNodes() throws RemoteException;
 
 	/**
-	 * Returns the <code>DirectoryNode</code> with name <code>nodeName</code>
-	 * or <code>null</code> if no such node exists.
+	 * Returns the node with name <code>nodeName</code> or <code>null</code>
+	 * if no such node exists.
 	 * 
 	 * @param nodeName
 	 *            The node name to look up.
-	 * @return The <code>DirectoryNode</code> with name <code>nodeName</code>.
+	 * @return The node with name <code>nodeName</code>.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract DirectoryNode getDirectoryNode(String nodeName)
-			throws RemoteException;
+	public abstract ProxyNode getNode(String nodeName) throws RemoteException;
 
 	/**
 	 * <p>
@@ -596,37 +592,37 @@
 	 * </p>
 	 * 
 	 * <p>
-	 * First, the method waits for <code>hupInterval</code> millis for the
-	 * nodes to have successfully opened a circuit. If they do not succeed
+	 * First, the method waits for <code>hupInterval</code> milliseconds for
+	 * the nodes to have successfully opened a circuit. If they do not succeed
 	 * within this time, a HUP signal is sent to all nodes and the method waits
-	 * for another <code>hupInterval</code> millis. In total, the method sends
-	 * at most <code>tries</code> HUP signals before giving up and returning
-	 * with <code>false</code>. Thus, the maximum waiting time is
+	 * for another <code>hupInterval</code> milliseconds. In total, the method
+	 * sends at most <code>tries</code> HUP signals before giving up and
+	 * returning with <code>false</code>. Thus, the maximum waiting time is
 	 * <code>(tries + 1)</code> times <code>hupInterval</code>. As soon as
 	 * all nodes have successfully opened circuits, the method returns with
-	 * <code>true</code>. This operation can only be invoked, if network
-	 * status is <code>NetworkState.NODES_STARTED</code>.
+	 * <code>true</code>. This operation can only be invoked, if all nodes in
+	 * the network are in state <code>NodeState.RUNNING</code>.
 	 * </p>
 	 * 
 	 * @param tries
 	 *            The maximum number of HUP signals that are sent to the Tor
 	 *            processes. Negative values are not allowed. A value of zero
 	 *            means to wait only for the given time of
-	 *            <code>hupInterval</code> millis without sending a HUP
+	 *            <code>hupInterval</code> milliseconds without sending a HUP
 	 *            signal. Typical values depend on the network being a public or
 	 *            private Tor network and range about 3 to 5 tries.
 	 * @param hupInterval
-	 *            The time in millis that the method will wait between sending
-	 *            HUP signals. Negative values are not allowed. Typically,
-	 *            values should not be smaller than 10 seconds to permit Tor to
-	 *            stabilize.
+	 *            The time in milliseconds that the method will wait between
+	 *            sending HUP signals. Negative values are not allowed.
+	 *            Typically, values should not be smaller than 5 seconds to
+	 *            permit Tor to stabilize.
 	 * @throws IllegalStateException
-	 *             Thrown if network is not in state
-	 *             <code>NetworkState.NODES_STARTED</code>.
+	 *             Thrown if at least one node is not in state
+	 *             <code>NodeState.RUNNING</code>.
 	 * @throws IllegalArgumentException
 	 *             Thrown if a negative value is given for either
 	 *             <code>tries</code> or <code>hupInterval</code>.
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if an I/O problem occurs while sending HUP signals.
 	 * @return <code>true</code> if all nodes have reported to have
 	 *         successfully opened a circuit, <code>false</code> otherwise.
@@ -634,35 +630,48 @@
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
 	public abstract boolean hupUntilUp(int tries, long hupInterval)
-			throws TorProcessException, RemoteException;
+			throws PuppeTorException, RemoteException;
 
 	/**
+	 * Sends a HUP signal to all nodes in the network once. This operation can
+	 * only be invoked, if all nodes in the network are in state
+	 * <code>NodeState.RUNNING</code>.
+	 * 
+	 * @throws IllegalStateException
+	 *             Thrown if at least one node is not in state
+	 *             <code>NodeState.RUNNING</code>.
+	 * @throws PuppeTorException
+	 *             Thrown if an I/O problem occurs while sending HUP signals.
+	 * @throws RemoteException
+	 *             Thrown if an error occurs when accessed remotely.
+	 */
+	public abstract void hupAllNodes() throws PuppeTorException,
+			RemoteException;
+
+	/**
 	 * Attempts to shut down all running nodes. The method blocks until all
 	 * shutdown requests have been sent and either returns, or throws the first
 	 * exception that has been observed when shutting down nodes. The method can
-	 * be assumed to return very quickly. This operation can only be invoked, if
-	 * network status is <code>NetworkState.NODES_STARTED</code>.
+	 * be assumed to return very quickly. If there are no running nodes in this
+	 * network, this operation has no effect.
 	 * 
-	 * @throws IllegalStateException
-	 *             Thrown if network is not in state
-	 *             <code>NetworkState.NODES_STARTED</code>.
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if an I/O problem occurs while shutting down the
 	 *             nodes.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract void shutdownNodes() throws TorProcessException,
+	public abstract void shutdownNodes() throws PuppeTorException,
 			RemoteException;
 
 	/**
 	 * Attempts to start all nodes within a given timeout of
-	 * <code>maximumTimeToWaitInMillis</code> millis. The method returns as
-	 * soon as all nodes have started and opened their control port so that we
-	 * can connect to them. It returns a boolean that states whether the
+	 * <code>maximumTimeToWaitInMillis</code> milliseconds. The method returns
+	 * as soon as all nodes have started and opened their control port so that
+	 * we can connect to them. It returns a boolean that states whether the
 	 * operation was either successful or has timed out. This operation can only
-	 * be invoked, if network status is
-	 * <code>NetworkState.CONFIGURATIONS_WRITTEN</code>.
+	 * be invoked, if all nodes in the network have written their configuration,
+	 * i.e. are not in state <code>NodeState.CONFIGURING</code> anymore.
 	 * 
 	 * @param maximumTimeToWaitInMillis
 	 *            The maximum time to wait in milliseconds. A positive value or
@@ -670,41 +679,39 @@
 	 *            allowed. Typical values are in the range of a few seconds.
 	 * @return <code>true</code> if all nodes could be started successfully,
 	 *         <code>false</code> if a timeout has occured.
+	 * @throws IllegalStateException
+	 *             Thrown if at least one node in the network is still in state
+	 *             <code>NodeState.CONFIGURING</code>.
 	 * @throws IllegalArgumentException
 	 *             Thrown if a negative value is given for
 	 *             <code>maximumTimeToWaitInMillis</code>.
-	 * @throws IllegalStateException
-	 *             Thrown if network is not in state
-	 *             <code>NetworkState.CONFIGURATIONS_WRITTEN</code>.
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if an I/O problem occurs while starting the nodes.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
 	public abstract boolean startNodes(long maximumTimeToWaitInMillis)
-			throws TorProcessException, RemoteException;
+			throws PuppeTorException, RemoteException;
 
 	/**
 	 * Writes the configurations for all nodes in the network to disk, including
 	 * <code>torrc</code> and <code>approved-routers</code> files. This
 	 * method is assumed to return very quickly. In case of a private network,
-	 * <code>configureAsPrivateNetwork</code> must be invoked in advance to
-	 * this method! If network status is
-	 * <code>NetworkState.CONFIGURING_NODES</code>, it will be changed to
-	 * <code>NetworkState.CONFIGURATIONS_WRITTEN</code>.
+	 * <code>configureAsPrivateNetwork</code> should be invoked in advance to
+	 * this method!
 	 * 
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if an I/O problem occurs while writing to the nodes'
 	 *             working directories.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract void writeConfigurations() throws TorProcessException,
+	public abstract void writeConfigurations() throws PuppeTorException,
 			RemoteException;
 
 	/**
 	 * Returns the working directory of this network configuration which is in
-	 * test-env/networkName/.
+	 * <code>test-env/networkName/</code>.
 	 * 
 	 * @return Working directory of this network.
 	 * @throws RemoteException
@@ -713,100 +720,43 @@
 	public abstract File getWorkingDirectory() throws RemoteException;
 
 	/**
-	 * Returns whether all nodes in this network are up, or not.
+	 * Adds a configuration string to the template of a node class, so that it
+	 * will be added to future instances of this node class and its subclasses.
 	 * 
-	 * @return <code>true</code> if all nodes are up, <code>false</code> if
-	 *         at least one node is not up.
-	 */
-	public abstract boolean allNodesUp() throws RemoteException;
-
-	/**
-	 * Appends the given directory server strings to the configurations of all
-	 * nodes in this network. This enables the nodes to use the directories of a
-	 * remote network.
-	 * 
-	 * TODO when is the TorProcessException thrown?
-	 * 
-	 * TODO in which state is this method permitted?
-	 * 
-	 * @param dirServerStrings
-	 *            A list containing the directory server strings of the nodes
-	 *            which should be added to the network.
+	 * @param nodeClass
+	 *            The class of nodes of which future instances will have the
+	 *            given configuration string.
+	 * @param templateConfigurationString
+	 *            The configuration string.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract void addDirectoryStrings(List<String> dirServerStrings)
-			throws RemoteException, TorProcessException;
+	public abstract void addTemplateConfiguration(
+			Class<? extends ProxyNode> nodeClass,
+			String templateConfigurationString) throws RemoteException;
 
 	/**
-	 * Appends the fingerprints of the given routers to the approved routers
-	 * file of all directory nodes of this network, thereby approving these
-	 * router nodes.
+	 * Returns the name of this network.
 	 * 
-	 * TODO in which state is this method permitted?
-	 * 
-	 * @param approvedRoutersStrings
-	 *            A set containing the fingerprints of the router nodes which
-	 *            should be approved by the Network.
+	 * @return The name of this network.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
-	 * @throws TorProcessException
-	 *             Thrown if an <code>approved-routers</code> file cannot be
-	 *             written to disk.
 	 */
-	public abstract void addApprovedRouters(Set<String> approvedRoutersStrings)
-			throws RemoteException, TorProcessException;
+	public abstract String getNetworkName() throws RemoteException;
 
 	/**
-	 * TODO Unconfirmed!
+	 * Binds the network at the local <code>rmiregistry</code> to make it
+	 * remotely available and returns whether binding was successful.
 	 * 
-	 * Enables to bind a network to the groovy shell.
-	 * 
-	 * @param remoteHost
-	 *            The IpAddress where the RMI registry is running.
-	 * @param bindingName
-	 *            The name whereby the stub is bound.
-	 * @return Network Stub
-	 * @throws MalformedURLException
-	 *             Thrown to indicate that a malformed URL has occurred. Either
-	 *             no legal protocol could be found in a specification string or
-	 *             the string could not be parsed.
+	 * @return <code>true</code> if binding was successful, <code>false</code>
+	 *         otherwise.
+	 * @throws PuppeTorException
+	 *             Thrown if an error occurs while binding to the
+	 *             <code>rmiregistry</code>.
 	 * @throws RemoteException
-	 *             Thrown if an error occurs when accessed remotely.
-	 * @throws NotBoundException
-	 *             A NotBoundException is thrown if an attempt is made to lookup
-	 *             or unbind in the registry a name that has no associated
-	 *             binding.
+	 *             Thrown if an error occurs when accessed remotely (though this
+	 *             might never happen as the object is not bound, yet).
 	 */
-	public abstract Network connectNetwork(String remoteHost, String bindingName)
-			throws MalformedURLException, RemoteException, NotBoundException;
+	public abstract boolean bindAtRmiregistry() throws RemoteException;
 
-	/**
-	 * TODO Unconfirmed!
-	 * 
-	 * Merges two separate networks.
-	 * 
-	 * @param remoteNetwork
-	 *            The stub of the network to merge.
-	 * @throws RemoteException
-	 *             Thrown if an error occurs when accessed remotely.
-	 */
-	public abstract void mergeNetworks(Network remoteNetwork)
-			throws RemoteException;
-
-	/**
-	 * Adds a configuration string to the template of a node class, so that it
-	 * will be added to future instances of this node class.
-	 * 
-	 * @param nodeClass
-	 *            The class of nodes of which future instances will have the
-	 *            given configuration string.
-	 * @param templateConfigurationString
-	 *            The configuration string.
-	 * @throws RemoteException
-	 *             Thrown if an error occurs when accessed remotely.
-	 */
-	public abstract void addTemplateConfiguration(
-			Class<? extends ProxyNode> nodeClass,
-			String templateConfigurationString) throws RemoteException;
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -31,12 +31,15 @@
  */
 package de.uniba.wiai.lspi.puppetor;
 
+import java.net.MalformedURLException;
+import java.rmi.Naming;
+import java.rmi.NotBoundException;
 import java.rmi.RemoteException;
 
 import de.uniba.wiai.lspi.puppetor.impl.NetworkImpl;
 
 /**
- * The <code>NetworkFactory</code> is an abstract factory that can create
+ * The <code>NetworkFactory</code> is a concrete factory that can create
  * <code>Network</code> instances.
  * 
  * TODO At the moment, this class uses the concrete class NetworkImpl to
@@ -53,7 +56,8 @@
 	/**
 	 * Creates a new network that is required for a test run. The new network is
 	 * initially unpopulated and creates its own working directory at
-	 * test-env/randomTestID/.
+	 * test-env/randomTestID/. The network automatically assigns port numbers to
+	 * newly created nodes starting at <code>7000</code>.
 	 * 
 	 * @param networkName
 	 *            Name of this network configuration.
@@ -69,14 +73,18 @@
 	/**
 	 * Creates a new network that is required for a test run. The new network is
 	 * initially unpopulated and creates its own working directory at
-	 * test-env/randomTestID/.
+	 * test-env/randomTestID/. The network automatically assigns port numbers to
+	 * newly created nodes starting at <code>startPort</code>.
 	 * 
 	 * @param networkName
 	 *            Name of this network configuration.
 	 * @param startPort
 	 *            The initial value for automatically assigned port numbers of
 	 *            nodes created by this <code>Network</code>; must be a value
-	 *            between 1024 and 65535.
+	 *            between <code>1024</code> and <code>65535</code>.
+	 *            Applications need to ensure that there are enough ports left
+	 *            to the maximum number port <code>65535</code> for all
+	 *            created nodes.
 	 * @return A new network instance.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
@@ -85,4 +93,37 @@
 			throws RemoteException {
 		return new NetworkImpl(networkName, startPort);
 	}
+
+	/**
+	 * Connects to a remote network and returns a reference on it.
+	 * 
+	 * @param remoteHost
+	 *            The IP address and port of the remote <code>rmiregistry</code>.
+	 * @param bindingName
+	 *            The name under which the other network is bound at the remote
+	 *            <code>rmiregistry</code>.
+	 * @return Reference on the remote network.
+	 * @throws PuppeTorException
+	 *             Thrown if an error occurs when looking up the remote network
+	 *             at the remote <code>rmiregistry</code>.
+	 * @throws RemoteException
+	 *             Thrown if an error occurs when accessed remotely.
+	 */
+	public static Network connectToNetwork(String remoteHost, String bindingName)
+			throws PuppeTorException, RemoteException {
+		Network remoteNetwork = null;
+		try {
+			remoteNetwork = (Network) Naming.lookup("rmi://" + remoteHost + "/"
+					+ bindingName);
+		} catch (MalformedURLException e) {
+			PuppeTorException ex = new PuppeTorException(
+					"Cannot connect to remote network!", e);
+			throw ex;
+		} catch (NotBoundException e) {
+			PuppeTorException ex = new PuppeTorException(
+					"Cannot connect to remote network!", e);
+			throw ex;
+		}
+		return remoteNetwork;
+	}
 }

Deleted: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * 
- *     * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-/**
- * The <code>NetworkState</code> constitutes the single state of a network
- * that is the result of the <code>NodeState</code>s of all nodes in the
- * network. In contrast to <code>EventType</code> the network (and node)
- * states depend only on the methods that have been invoked on these objects,
- * and not on asynchronous state changes. Most operations of
- * <code>Network</code> require a certain <code>NetworkState</code> as
- * precondition and may ensure another <code>NetworkState</code> as
- * postcondition. There is a prescribed order of states.
- * 
- * @author kloesing
- */
-public enum NetworkState {
-
-	/**
-	 * The configurations of the nodes in the network have not been written to
-	 * disk and can be changed. This is the initial state of a
-	 * <code>Network</code>.
-	 */
-	CONFIGURING_NODES,
-
-	/**
-	 * The configurations of all nodes in the network have been written to disk
-	 * and cannot be changed anymore, but the Tor processes have not been
-	 * started, yet. This state could be useful to review the configurations
-	 * that have been written to disk.
-	 */
-	CONFIGURATIONS_WRITTEN,
-
-	/**
-	 * The nodes in the network have been started and are running.
-	 */
-	NODES_STARTED,
-
-	/**
-	 * The nodes in the network had been started and shut down.
-	 */
-	NODES_SHUT_DOWN
-
-}

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeEventType.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeEventType.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeEventType.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -31,36 +31,55 @@
  */
 package de.uniba.wiai.lspi.puppetor;
 
-public class NodeEventType implements EventType{
+/**
+ * Event types that can be fired by all Tor processes.
+ */
+public class NodeEventType implements EventType {
 
 	/**
-	 * The node has reported that its routing table for v2 hidden services has
-	 * changed; this event can only be parsed from a log statement in a modified
-	 * Tor!
+	 * String identifying the type of the event type.
 	 */
-	public static final NodeEventType NODE_ROUTING_TABLE_CHANGED = new NodeEventType();
-	
+	String typeString;
+
 	/**
+	 * Creates a new event type with the given type string.
+	 * 
+	 * @param typeString
+	 *            String identifying the type of the event type.
+	 */
+	public NodeEventType(String typeString) {
+		this.typeString = typeString;
+	}
+
+	public String getTypeName() {
+		return this.typeString;
+	}
+
+	/**
 	 * The node was started and we managed to connect to its control port; this
 	 * event is fired internally and not parsed from a log statement from Tor.
 	 */
-	public static final NodeEventType NODE_STARTED = new NodeEventType();
-	
+	public static final NodeEventType NODE_STARTED = new NodeEventType(
+			"NODE_STARTED");
+
 	/**
 	 * The node has opened its control port; this event is parsed from a log
 	 * statement in connection_create_listener().
 	 */
-	public static final NodeEventType NODE_CONTROL_PORT_OPENED = new NodeEventType();
-	
+	public static final NodeEventType NODE_CONTROL_PORT_OPENED = new NodeEventType(
+			"NODE_CONTROL_PORT_OPENED");
+
 	/**
 	 * The node which has successfully opened a circuit; this event is parsed
 	 * from a log statement in circuit_send_next_onion_skin().
 	 */
-	public static final NodeEventType NODE_CIRCUIT_OPENED = new NodeEventType();
-	
+	public static final NodeEventType NODE_CIRCUIT_OPENED = new NodeEventType(
+			"NODE_CIRCUIT_OPENED");
+
 	/**
 	 * The node was stopped; this event is fired internally and not parsed from
 	 * a log statement from Tor.
 	 */
-	public static final NodeEventType NODE_STOPPED = new NodeEventType();
+	public static final NodeEventType NODE_STOPPED = new NodeEventType(
+			"NODE_STOPPED");
 }
\ No newline at end of file

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -33,29 +33,27 @@
 
 /**
  * The <code>NodeState</code> constitutes the state of a single Tor node. In
- * contrast to <code>EventType</code> the node (and network) states depend
- * only on the methods that have been invoked on these objects, and not on
- * asynchronous state changes. Most operations of <code>ProxyNode</code> and
- * its subclasses require a certain <code>NodeState</code> as precondition and
- * may ensure another <code>NodeState</code> as postcondition. There is a
- * prescribed order of states.
+ * contrast to <code>EventType</code> the node states depend only on the
+ * methods that have been invoked on these objects, and not on asynchronous
+ * state changes. Most operations of <code>ProxyNode</code> and its subclasses
+ * require a certain <code>NodeState</code> as precondition and may ensure
+ * another <code>NodeState</code> as postcondition. There is a prescribed
+ * order of states.
  * 
  * @author kloesing
  */
 public enum NodeState {
 
 	/**
-	 * The configuration of this node has not been written to disk and can be
-	 * changed. This is the initial state of a <code>ProxyNode</code> or one
-	 * of its subclasses.
+	 * The configuration of this node has not been written to disk. This is the
+	 * initial state of a <code>ProxyNode</code> or one of its subclasses.
 	 */
 	CONFIGURING,
 
 	/**
-	 * The configuration of this node has been written to disk and cannot be
-	 * changed anymore, but the Tor process has not been started, yet. This
-	 * state could be useful to review the configuration that has been written
-	 * to disk.
+	 * The configuration of this node has been written to disk, but the Tor
+	 * process has not been started, yet. This state could be useful to review
+	 * the configuration that has been written to disk.
 	 */
 	CONFIGURATION_WRITTEN,
 
@@ -65,7 +63,8 @@
 	RUNNING,
 
 	/**
-	 * The node had been started and shut down.
+	 * The node had been started and shut down. It cannot be started at a later
+	 * time anymore.
 	 */
 	SHUT_DOWN
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -34,7 +34,6 @@
 import java.rmi.Remote;
 import java.rmi.RemoteException;
 import java.util.List;
-import java.util.Set;
 
 /**
  * <p>
@@ -64,9 +63,9 @@
  * <li>start proxy nodes with a delay of at least 10 minutes to be sure that
  * the router descriptors stored at directory authorities will be accepted by
  * directory clients, or</li>
- * <li>change the constants ESTIMATED_PROPAGATION_TIME and
- * NETWORKSTATUS_CLIENT_DL_INTERVAL in Tor to values smaller than your overall
- * HUP time for starting the network.</li>
+ * <li>change the constants <code>ESTIMATED_PROPAGATION_TIME</code> and
+ * <code>NETWORKSTATUS_CLIENT_DL_INTERVAL</code> in Tor to values smaller than
+ * your overall HUP time for starting the network.</li>
  * </ul>
  * 
  * @author kloesing
@@ -75,12 +74,7 @@
 
 	/**
 	 * Adds the entries for a hidden service to the configuration of this node.
-	 * This method can only be invoked while the node is in state
-	 * <code>NodeState.CONFIGURING</code>.
 	 * 
-	 * TODO Should this operation also be possible while the process is running?
-	 * We could easily change the configuration via the controller.
-	 * 
 	 * @param serviceName
 	 *            Name of the hidden service that will be used as name for the
 	 *            hidden service directory. May neither be <code>null</code>
@@ -94,37 +88,60 @@
 	 *            The virtual TCP port that this hidden service runs on as it is
 	 *            announced to clients. May not be negative or greater than
 	 *            65535.
-	 * @throws IllegalStateException
-	 *             Thrown if node is not in state
-	 *             <code>NodeState.CONFIGURING</code>.
+	 * @return <code>HiddenService</code> object containing the configuration
+	 *         of the created hidden service.
 	 * @throws IllegalArgumentException
 	 *             Thrown if an invalid value is given for either of the
 	 *             parameters.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract void addHiddenService(String serviceName, int servicePort,
-			int virtualPort) throws RemoteException;
+	public abstract HiddenService addHiddenService(String serviceName,
+			int servicePort, int virtualPort) throws RemoteException;
 
 	/**
-	 * Adds the given set of DirServer configuration entries to the
-	 * configuration of this node. Note that as soon as one DirServer is
-	 * configured, the node does not connect to an outside directory server of
-	 * the public network any more!
+	 * Adds the entries for a hidden service with virtual port 80 to the
+	 * configuration of this node.
 	 * 
-	 * TODO allow invocation of this method only in correct state
+	 * @param serviceName
+	 *            Name of the hidden service that will be used as name for the
+	 *            hidden service directory. May neither be <code>null</code>
+	 *            or a zero-length string.
+	 * @param servicePort
+	 *            The TCP port on which the service will be available for
+	 *            requests. This can, but need not be different from the virtual
+	 *            port that is announced to clients. May not be negative or
+	 *            greater than 65535.
+	 * @return <code>HiddenService</code> object containing the configuration
+	 *         of the created hidden service.
+	 * @throws IllegalArgumentException
+	 *             Thrown if an invalid value is given for either of the
+	 *             parameters.
+	 * @throws RemoteException
+	 *             Thrown if an error occurs when accessed remotely.
+	 */
+	public abstract HiddenService addHiddenService(String serviceName,
+			int servicePort) throws RemoteException;
+
+	/**
+	 * Adds the entries for a hidden service with an automatically assigned
+	 * service port and virtual port 80 to the configuration of this node.
 	 * 
-	 * @param authorizedDirServerStrings
-	 *            A set of DirServer configuration entries that each contain one
-	 *            directory server that this node shall connect to. May not be
-	 *            <code>null</code>, but may be an empty set.
+	 * service port automatically assigned virtual port 80
+	 * 
+	 * @param serviceName
+	 *            Name of the hidden service that will be used as name for the
+	 *            hidden service directory. May neither be <code>null</code>
+	 *            or a zero-length string.
+	 * @return <code>HiddenService</code> object containing the configuration
+	 *         of the created hidden service.
 	 * @throws IllegalArgumentException
-	 *             Thrown if <code>null</code> is passed as parameter.
+	 *             Thrown if an invalid value is given for the parameter.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract void configureDirServers(
-			Set<String> authorizedDirServerStrings) throws RemoteException;
+	public abstract HiddenService addHiddenService(String serviceName)
+			throws RemoteException;
 
 	/**
 	 * Adds the given configuration string, consisting of "<configuration key>
@@ -165,17 +182,14 @@
 	 * <code>configurationString</code> by this new configuration string; if
 	 * multiple occurrences of the given configuration key are found, only the
 	 * first occurrence is replaced; if no configuration can be found, the
-	 * configuration string is added.
+	 * configuration string is appended.
 	 * 
 	 * @param configurationString
 	 *            The replacing configuration string.
 	 * @throws IllegalArgumentException
-	 *             Thrown if the given configurationString is either
+	 *             Thrown if the given configuration string is either
 	 *             <code>null</code>, a zero-length string, or does not
 	 *             consist of configuration key and value.
-	 * @throws IllegalStateException
-	 *             Thrown if not invoked in state
-	 *             <code>NodeState.CONFIGURING</code>.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
@@ -183,6 +197,22 @@
 			throws RemoteException;
 
 	/**
+	 * Removes all configuration strings containing the given configuration key
+	 * in "<configuration key> <configuration value>", regardless of their
+	 * configuration value.
+	 * 
+	 * @param configurationKey
+	 *            The configuration key to remove.
+	 * @throws IllegalArgumentException
+	 *             Thrown if the given configuration key is either
+	 *             <code>null</code> or a zero-length key.
+	 * @throws RemoteException
+	 *             Thrown if an error occurs when accessed remotely.
+	 */
+	public abstract void deleteConfiguration(String configurationKey)
+			throws RemoteException;
+
+	/**
 	 * Returns the name of this node.
 	 * 
 	 * @return The name of this node.
@@ -201,48 +231,18 @@
 	public abstract NodeState getNodeState() throws RemoteException;
 
 	/**
-	 * Determines the onion address for a previously added hidden service with
-	 * name <code>serviceName</code>. Requires that the node has been
-	 * started, i.e. is in state <code>NodeState.RUNNING</code>.
-	 * 
-	 * @param serviceName
-	 *            Name of the hidden service that has been used before to add
-	 *            the hidden service. May neither be <code>null</code> or a
-	 *            zero-length string.
-	 * @param version
-	 *            Hidden service version; can be either 0, 1, or 2. Note that
-	 *            version 2 may not be implemented in the regular Tor sources!
-	 * @return The onion address string consisting of 16 base32 chars plus
-	 *         ".onion" for hidden service versions 0 and 1 or 16 base32 chars
-	 *         plus "." plus 24 base32 chars plus ".onion" for hidden service
-	 *         version 2.
-	 * @throws IllegalArgumentException
-	 *             Thrown if <code>null</code> or a zero-length string is
-	 *             passed as parameter.
-	 * @throws TorProcessException
-	 *             Thrown if either there does not exist a hidden service with
-	 *             the given <code>serviceName</code> as directory, if the
-	 *             given <code>version</code> is invalid, or if the
-	 *             <code>hostname</code> file could not be read.
-	 * @throws RemoteException
-	 *             Thrown if an error occurs when accessed remotely.
-	 */
-	public abstract String getOnionAddress(String serviceName, int version)
-			throws TorProcessException, RemoteException;
-
-	/**
 	 * Sends a HUP command to the process via its control port to restart it;
 	 * can only be done if the node has already been started, i.e. is in state
 	 * <code>NodeState.RUNNING</code>!
 	 * 
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if an I/O problem occurs while sending the HUP signal.
 	 * @throws IllegalStateException
 	 *             Thrown if node is not in state <code>NodeState.RUNNING</code>.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract void hup() throws TorProcessException, RemoteException;
+	public abstract void hup() throws PuppeTorException, RemoteException;
 
 	/**
 	 * Shuts down the Tor process corresponding to this node immediately. This
@@ -253,13 +253,13 @@
 	 * @throws IllegalStateException
 	 *             Thrown if this node is not in state
 	 *             <code>NodeState.RUNNING</code>.
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if an I/O problem occurs while sending the
 	 *             <code>SHUTDOWN</code> signal.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract void shutdown() throws TorProcessException, RemoteException;
+	public abstract void shutdown() throws PuppeTorException, RemoteException;
 
 	/**
 	 * Starts the Tor process for this node and connects to the control port as
@@ -269,12 +269,9 @@
 	 * output is parsed to see when the control port is opened.</b>
 	 * 
 	 * @param maximumTimeToWaitInMillis
-	 *            Maximum time in millis we will wait for the Tor process to be
-	 *            started and the control port being opened. If this value is
-	 *            negative or zero, we will wait potentially forever. TODO
-	 *            should we normalize behavior for negative timeouts to either
-	 *            throw an exception or wait forever consistently for the whole
-	 *            framework?
+	 *            Maximum time in milliseconds we will wait for the Tor process
+	 *            to be started and the control port being opened. If this value
+	 *            is negative or zero, we will wait potentially forever.
 	 * @return <code>true</code> if the node could be started successfully,
 	 *         <code>false</code> otherwise.
 	 * @throws IllegalStateException
@@ -282,14 +279,14 @@
 	 *             <code>NodeState.CONFIGURATION_WRITTEN</code>, i.e. if
 	 *             either configuration has not been written or the process has
 	 *             already been started.
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if either the process could not be started, or the
 	 *             connection to the control port could not be established.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
 	public abstract boolean startNode(long maximumTimeToWaitInMillis)
-			throws TorProcessException, RemoteException;
+			throws PuppeTorException, RemoteException;
 
 	/**
 	 * Writes the configuration of this node to the <code>torrc</code> file in
@@ -297,13 +294,13 @@
 	 * <code>NodeState.CONFIGURATION_WRITTEN</code>, if it was in state
 	 * <code>NodeState.CONFIGURING</code> before.
 	 * 
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if the configuration file <code>torrc</code> cannot
 	 *             be written to disk.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract void writeConfiguration() throws TorProcessException,
+	public abstract void writeConfiguration() throws PuppeTorException,
 			RemoteException;
 
 	/**

Copied: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/PuppeTorException.java (from rev 11669, puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java)
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/PuppeTorException.java	                        (rev 0)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/PuppeTorException.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2007, Karsten Loesing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>PuppeTorException</code> comprises all kinds of checked
+ * exceptions that occur when interacting with the JVM-external Tor processes or
+ * with the local file system. Any occurence of this exception denotes either a
+ * configuration problem that can only be solved outside of the JVM, or an
+ * unexpected problem. In contrast to this, all kinds of programming errors of
+ * an application using this API (invoking a method with wrong parameter values,
+ * in wrong state, etc.) will instead cause appropriate runtime exceptions from
+ * the Java API.
+ * 
+ * @author kloesing
+ */
+ at SuppressWarnings("serial")
+public class PuppeTorException extends Exception {
+
+	/**
+	 * Creates a <code>PuppeTorException</code> without detail message or
+	 * cause.
+	 */
+	public PuppeTorException() {
+		super();
+	}
+
+	/**
+	 * Creates a <code>PuppeTorException</code> with the given detail
+	 * <code>message</code> and <code>cause</code>.
+	 * 
+	 * @param message
+	 *            The detail message of this exception.
+	 * @param cause
+	 *            The cause for this exception.
+	 */
+	public PuppeTorException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+	/**
+	 * Creates a <code>PuppeTorException</code> with the given detail
+	 * <code>message</code>, but without a <code>cause</code>.
+	 * 
+	 * @param message
+	 *            The detail message of this exception.
+	 */
+	public PuppeTorException(String message) {
+		super(message);
+	}
+
+	/**
+	 * Creates a <code>PuppeTorException</code> with the given
+	 * <code>cause</code>, but without a detail message.
+	 * 
+	 * @param cause
+	 *            The cause for this exception.
+	 */
+	public PuppeTorException(Throwable cause) {
+		super(cause);
+	}
+}

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -45,64 +45,6 @@
 public interface RouterNode extends ProxyNode {
 
 	/**
-	 * <p>
-	 * Determines the directory fingerprint of this node. If the Tor process has
-	 * not been started before, it is started and immediately stopped
-	 * afterwards, so that the Tor process creates a new onion key pair and
-	 * fingerprint for it. This is done using a temporary configuration file and
-	 * with the command-line option <code>--list-fingerprint</code>. Tor then
-	 * generates a new onion key and writes its fingerprint to the
-	 * <code>fingerprint</code> file in its working directory, but does not
-	 * start routing traffic.
-	 * </p>
-	 * 
-	 * <p>
-	 * The temporary <code>torrc</code> file contains a fake entry as
-	 * DirServer, so that the node thinks that it is in a private network. The
-	 * reason for this is that some configuration entries might only work in a
-	 * private network, but the node cannot be configured with valid DirServer
-	 * entries that would make it believe to be in a private network, because
-	 * they can only be generated after invoking this method. Therefore, a
-	 * single DirServer entry is configured with this node as directory server
-	 * having a fingerprint of
-	 * <code>0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code>.
-	 * </p>
-	 * 
-	 * <p>
-	 * The fingerprint is returned and locally stored so that further
-	 * invocations of this method can be answered immediately. This method can
-	 * only be invoked in state <code>NodeState.CONFIGURING</code> and does
-	 * not change this state. It should be invoked after most of the other
-	 * configurations for this node have been completed, but can be invoked
-	 * before specifying directory servers for this node.
-	 * </p>
-	 * 
-	 * @return The content of the fingerprint file of this node.
-	 * @throws TorProcessException
-	 *             Thrown if either the temporary <code>torrc</code>
-	 *             configuration file cannot be written, the Tor process cannot
-	 *             be started temporarily, or the fingerprint file cannot be
-	 *             read.
-	 * @throws RemoteException
-	 *             Thrown if an error occurs when accessed remotely.
-	 */
-	public abstract String determineFingerprint() throws TorProcessException,
-			RemoteException;
-
-	/**
-	 * Returns the base32-encoded fingerprint of this node.
-	 * 
-	 * @return The base32-encoded fingerprint of this node.
-	 * @throws IllegalStateException
-	 *             Thrown if node is neither in state
-	 *             <code>NodeState.RUNNING</code> or
-	 *             <code>NodeState.SHUT_DOWN</code>.
-	 * @throws RemoteException
-	 *             Thrown if an error occurs when accessed remotely.
-	 */
-	public abstract String getFingerprintBase32() throws RemoteException;
-
-	/**
 	 * Returns the dir port of this node.
 	 * 
 	 * @return The dir port of this node.
@@ -121,16 +63,28 @@
 	public abstract int getOrPort() throws RemoteException;
 
 	/**
-	 * Returns the fingerprint of this node formatted like
-	 * <code>0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code>.
+	 * <p>
+	 * Returns the fingerprint string of this node, formatted like
+	 * <code>nickname 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code>.
+	 * </p>
 	 * 
+	 * <p>
+	 * The fingerprint is determined by a background thread that is started as
+	 * soon as the node is instantiated. If this background thread has not
+	 * finished when this method is invoked, the invoking thread will be blocked
+	 * until the fingerprint is available (or determining it has failed,
+	 * whereupon an exception will be thrown).
+	 * </p>
+	 * 
 	 * @return The fingerprint of this node.
-	 * @throws IllegalStateException
-	 *             Thrown if node is neither in state
-	 *             <code>NodeState.RUNNING</code> or
-	 *             <code>NodeState.SHUT_DOWN</code>.
+	 * @throws PuppeTorException
+	 *             Thrown if either the temporary <code>torrc.temp</code>
+	 *             configuration file cannot be written, the Tor process cannot
+	 *             be started temporarily, or the fingerprint file cannot be
+	 *             read.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public abstract String getFingerprint() throws RemoteException;
+	public abstract String getFingerprint() throws PuppeTorException,
+			RemoteException;
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -44,26 +44,41 @@
 	/**
 	 * Starts listening for incoming <code>HTTP GET</code> requests from
 	 * clients. Any incoming request is answered by an empty
-	 * <code>HTTP OK</code> reply. This method may only be invoked once!
+	 * <code>HTTP OK</code> reply. This method may only be invoked when the
+	 * server is currently not in listening state!
 	 * 
 	 * @throws IllegalStateException
-	 *             Thrown if <code>listen</code> has already been invoked
-	 *             before.
+	 *             Thrown if the server is currently not in listening state.
 	 */
-	public abstract void listen();
+	public abstract void startListening();
 
 	/**
-	 * Stops listening for requests.
+	 * Stops listening for requests. This method may only be invoked when the
+	 * server is currently in listening state!
 	 * 
 	 * @throws IllegalStateException
-	 *             Thrown if <code>listen</code> has not been invoked before.
+	 *             Thrown if the server is currently in listening state.
 	 */
 	public abstract void stopListening();
 
 	/**
+	 * Returns whether this server is currently in listening state.
+	 * 
+	 * @return The listening state of this server.
+	 */
+	public abstract boolean isListening();
+
+	/**
 	 * Returns the name of this server.
 	 * 
 	 * @return The name of this server.
 	 */
 	public abstract String getServerApplicationName();
+
+	/**
+	 * Returns the port on which this server listens.
+	 * 
+	 * @return The port on which this server listens.
+	 */
+	public abstract int getServerPort();
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerEventType.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerEventType.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerEventType.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -31,12 +31,36 @@
  */
 package de.uniba.wiai.lspi.puppetor;
 
-public class ServerEventType implements EventType{
+/**
+ * Event types that can be fired by a server application running as thread in
+ * the background.
+ */
+public class ServerEventType implements EventType {
 
 	/**
+	 * String identifying the type of the event type.
+	 */
+	String typeString;
+
+	/**
+	 * Creates a new event type with the given type string.
+	 * 
+	 * @param typeString
+	 *            String identifying the type of the event type.
+	 */
+	public ServerEventType(String typeString) {
+		this.typeString = typeString;
+	}
+
+	public String getTypeName() {
+		return this.typeString;
+	}
+
+	/**
 	 * The server application has received a request and sent a reply to it;
 	 * this event is fired internally and not parsed from a log statement from
 	 * Tor.
 	 */
-	public static final ServerEventType SERVER_RECEIVING_REQUEST_SENDING_REPLY = new ServerEventType();
+	public static final ServerEventType SERVER_RECEIVING_REQUEST_SENDING_REPLY = new ServerEventType(
+			"SERVER_RECEIVING_REQUEST_SENDING_REPLY");
 }
\ No newline at end of file

Deleted: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -1,92 +0,0 @@
-/*
- * Copyright (c) 2007, Karsten Loesing
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * 
- *     * Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package de.uniba.wiai.lspi.puppetor;
-
-/**
- * The <code>TorProcessException</code> comprises all kinds of checked
- * exceptions that occur when interacting with the JVM-external Tor processes or
- * with the local file system. Any occurence of this exception denotes either a
- * configuration problem that can only be solved outside of the JVM, or an
- * unexpected problem. In contrast to this, all kinds of programming errors of
- * an application using this API (invoking a method with wrong parameter values,
- * in wrong state, etc.) will instead cause appropriate runtime exceptions from
- * the Java API.
- * 
- * @author kloesing
- */
- at SuppressWarnings("serial")
-public class TorProcessException extends Exception {
-
-	/**
-	 * Creates a <code>TorProcessException</code> without detail message or
-	 * cause.
-	 */
-	public TorProcessException() {
-		super();
-	}
-
-	/**
-	 * Creates a <code>TorProcessException</code> with the given detail
-	 * <code>message</code> and <code>cause</code>.
-	 * 
-	 * @param message
-	 *            The detail message of this exception.
-	 * @param cause
-	 *            The cause for this exception.
-	 */
-	public TorProcessException(String message, Throwable cause) {
-		super(message, cause);
-	}
-
-	/**
-	 * Creates a <code>TorProcessException</code> with the given detail
-	 * <code>message</code>, but without a <code>cause</code>.
-	 * 
-	 * @param message
-	 *            The detail message of this exception.
-	 */
-	public TorProcessException(String message) {
-		super(message);
-	}
-
-	/**
-	 * Creates a <code>TorProcessException</code> with the given
-	 * <code>cause</code>, but without a detail message.
-	 * 
-	 * @param cause
-	 *            The cause for this exception.
-	 */
-	public TorProcessException(Throwable cause) {
-		super(cause);
-	}
-
-}

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -41,11 +41,11 @@
 import de.uniba.wiai.lspi.puppetor.Network;
 import de.uniba.wiai.lspi.puppetor.NetworkFactory;
 import de.uniba.wiai.lspi.puppetor.ProxyNode;
-import de.uniba.wiai.lspi.puppetor.TorProcessException;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
 
 /**
- * Example for accessing a public Web server (here: <code>www.google.com</code>)
- * over Tor to measure access times.
+ * Example for accessing a public web server (here: <code>www.google.com</code>)
+ * over Tor to measure the access time.
  * 
  * @author kloesing
  */
@@ -56,23 +56,19 @@
 	 * 
 	 * @param args
 	 *            Command-line arguments (ignored).
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if there is a problem with the JVM-external Tor
 	 *             processes that we cannot handle.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public static void main(String[] args) throws TorProcessException,
+	public static void main(String[] args) throws PuppeTorException,
 			RemoteException {
 
 		// though we only need a single proxy, we always need to create a
 		// network to initialize a test case.
 		Network network = NetworkFactory.createNetwork("example1");
 
-		// configure firewall restrictions for all newly created nodes
-		network.addTemplateConfiguration(ProxyNode.class,
-				"ReachableAddresses accept *:80, accept *:443, reject *:*");
-
 		// create a single proxy node with name "proxy"
 		ProxyNode proxy = network.createProxy("proxy");
 
@@ -84,16 +80,20 @@
 		if (!network.startNodes(5000)) {
 
 			// failed to start the proxy
+			System.out.println("Failed to start the node!");
 			return;
 		}
+		System.out.println("Successfully started the node!");
 
 		// hup until proxy has built circuits (5 retries, 10 seconds timeout
 		// each)
 		if (!network.hupUntilUp(5, 10000)) {
 
 			// failed to build circuits
+			System.out.println("Failed to build circuits!");
 			return;
 		}
+		System.out.println("Successfully built circuits!");
 
 		// create client application
 		ClientApplication client = network.createClient("client",
@@ -109,10 +109,9 @@
 				if (event.getType() == ClientEventType.CLIENT_SENDING_REQUEST) {
 					before = System.currentTimeMillis();
 				} else if (event.getType() == ClientEventType.CLIENT_REPLY_RECEIVED) {
-					System.out
-							.println("Request took "
-									+ (System.currentTimeMillis() - before)
-									+ " millis");
+					System.out.println("Request took "
+							+ (System.currentTimeMillis() - before)
+							+ " milliseconds");
 				}
 			}
 		};
@@ -125,7 +124,7 @@
 				clientEventListener);
 
 		// perform at most three request with a timeout of 20 seconds each
-		client.performRequest(3, 20000, true);
+		client.startRequests(3, 20000, true);
 
 		// block this thread as long as client requests are running
 		manager.waitForAnyOccurence(client.getClientApplicationName(),
@@ -146,5 +145,8 @@
 		} catch (InterruptedException e) {
 		}
 
+		// Shut down the JVM
+		System.out.println("Goodbye.");
+		System.exit(0);
 	}
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -33,17 +33,18 @@
 
 import java.rmi.RemoteException;
 
-import de.uniba.wiai.lspi.puppetor.BobEventType;
 import de.uniba.wiai.lspi.puppetor.ClientApplication;
 import de.uniba.wiai.lspi.puppetor.ClientEventType;
 import de.uniba.wiai.lspi.puppetor.Event;
 import de.uniba.wiai.lspi.puppetor.EventListener;
 import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.HiddenService;
+import de.uniba.wiai.lspi.puppetor.HiddenServiceEventType;
 import de.uniba.wiai.lspi.puppetor.Network;
 import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
 import de.uniba.wiai.lspi.puppetor.RouterNode;
 import de.uniba.wiai.lspi.puppetor.ServerApplication;
-import de.uniba.wiai.lspi.puppetor.TorProcessException;
 
 /**
  * Example for advertising and accessing a hidden service over a private Tor
@@ -58,13 +59,13 @@
 	 * 
 	 * @param args
 	 *            Command-line arguments (ignored).
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if there is a problem with the JVM-external Tor
 	 *             processes that we cannot handle.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public static void main(String[] args) throws TorProcessException,
+	public static void main(String[] args) throws PuppeTorException,
 			RemoteException {
 
 		// create a network to initialize a test case
@@ -72,18 +73,17 @@
 
 		// create three router nodes with parameters (router name, control port,
 		// SOCKS port, OR port, dir mirror port)
-		RouterNode router1 = network.createRouter("router1", 7021, 7022, 7023,
-				7024);
-		network.createRouter("router2", 7031, 7032, 7033, 7034);
-		network.createRouter("router3", 7041, 7042, 7043, 7044);
+		RouterNode router1 = network.createRouter("router1");
+		network.createRouter("router2");
+		RouterNode router3 = network.createRouter("router3");
 
 		// create two directory nodes with parameters (router name, control
 		// port, SOCKS port, OR port, dir port)
-		network.createDirectory("dir1", 7051, 7052, 7053, 7054);
-		network.createDirectory("dir2", 7061, 7062, 7063, 7064);
+		network.createDirectory("dir1");
+		network.createDirectory("dir2");
 
 		// add hidden service
-		router1.addHiddenService("hidServ", 7025, 80);
+		HiddenService hidServ1 = router1.addHiddenService("hidServ");
 
 		// configure nodes of this network to be part of a private network
 		network.configureAsPrivateNetwork();
@@ -95,7 +95,7 @@
 		// timeout of 15 seconds
 		if (!network.startNodes(15000)) {
 
-			// failed to start the proxy
+			// failed to start the nodes
 			System.out.println("Failed to start nodes!");
 			return;
 		}
@@ -115,28 +115,29 @@
 		EventManager manager = network.getEventManager();
 
 		// wait for 3 minutes that the proxy has published its first RSD
-
 		if (!manager.waitForAnyOccurence(router1.getNodeName(),
-				BobEventType.BOB_DESC_PUBLISHED_RECEIVED, 3L * 60L * 1000L)) {
+				HiddenServiceEventType.BOB_DESC_PUBLISHED_RECEIVED,
+				3L * 60L * 1000L)) {
 			// failed to publish an RSD
 			System.out.println("Failed to publish an RSD!");
 			return;
 		}
-		System.out.println("All RSDs published!");
+		System.out.println("Successfully published an RSD!");
 
+		// create server application
+		ServerApplication server = network.createServer("server", hidServ1
+				.getServicePort());
+
 		// determine onion address for hidden service
-		String onionAddress = router1.getOnionAddress("hidServ", 1);
+		String onionAddress = hidServ1.determineOnionAddress();
 
-		// create server application
-		ServerApplication server = network.createServer("server", 7025);
-
 		// start server
-		server.listen();
+		server.startListening();
 		System.out.println("Started server");
 
 		// create client application
 		ClientApplication client = network.createClient("client", onionAddress,
-				80, 7042);
+				hidServ1.getVirtualPort(), router3.getSocksPort());
 
 		// register event listener
 		EventListener clientAndServerEventListener = new EventListener() {
@@ -152,7 +153,7 @@
 				clientAndServerEventListener);
 
 		// perform at most five request with a timeout of 45 seconds each
-		client.performRequest(5, 45000, true);
+		client.startRequests(5, 45000, true);
 
 		// wait for request to be performed
 		manager.waitForAnyOccurence(client.getClientApplicationName(),
@@ -161,5 +162,8 @@
 		// shut down nodes
 		network.shutdownNodes();
 
+		// Shut down the JVM
+		System.out.println("Goodbye.");
+		System.exit(0);
 	}
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -33,18 +33,19 @@
 
 import java.rmi.RemoteException;
 
-import de.uniba.wiai.lspi.puppetor.BobEventType;
 import de.uniba.wiai.lspi.puppetor.ClientApplication;
 import de.uniba.wiai.lspi.puppetor.ClientEventType;
 import de.uniba.wiai.lspi.puppetor.Event;
 import de.uniba.wiai.lspi.puppetor.EventListener;
 import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.HiddenService;
+import de.uniba.wiai.lspi.puppetor.HiddenServiceEventType;
 import de.uniba.wiai.lspi.puppetor.Network;
 import de.uniba.wiai.lspi.puppetor.NetworkFactory;
 import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
 import de.uniba.wiai.lspi.puppetor.ServerApplication;
 import de.uniba.wiai.lspi.puppetor.ServerEventType;
-import de.uniba.wiai.lspi.puppetor.TorProcessException;
 
 /**
  * Example for advertising and accessing a hidden service over the public Tor
@@ -59,25 +60,24 @@
 	 * 
 	 * @param args
 	 *            Command-line arguments (ignored).
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if there is a problem with the JVM-external Tor
 	 *             processes that we cannot handle.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public static void main(String[] args) throws TorProcessException,
+	public static void main(String[] args) throws PuppeTorException,
 			RemoteException {
 
 		// create a network to initialize a test case
 		Network network = NetworkFactory.createNetwork("example3");
 
-		// create two proxy nodes with names "proxy1"/"proxy2", control ports
-		// 7001/7011, and SOCKS ports 7002/7012
-		ProxyNode proxy1 = network.createProxy("proxy1", 7001, 7002);
-		network.createProxy("proxy2", 7011, 7012);
+		// create two proxy nodes
+		ProxyNode proxy1 = network.createProxy("proxy1");
+		ProxyNode proxy2 = network.createProxy("proxy2");
 
 		// add hidden service to the configuration of proxy1
-		proxy1.addHiddenService("hidServ", 7005, 80);
+		HiddenService hidServ1 = proxy1.addHiddenService("hidServ");
 
 		// write configuration of proxy node
 		network.writeConfigurations();
@@ -87,11 +87,12 @@
 		if (!network.startNodes(5000)) {
 
 			// failed to start the proxy
-			System.out.println("Failed to start the proxy!");
+			System.out.println("Failed to start nodes!");
 			return;
 		}
+		System.out.println("Successfully started nodes!");
 
-		// hup until proxy has built circuits (5 retries, 10 seconds timeout
+		// hup until nodes have built circuits (5 retries, 10 seconds timeout
 		// each)
 		if (!network.hupUntilUp(5, 10000)) {
 
@@ -99,28 +100,32 @@
 			System.out.println("Failed to build circuits!");
 			return;
 		}
+		System.out.println("Successfully built circuits!");
 
 		// obtain reference to event manager to be able to respond to events
 		EventManager manager = network.getEventManager();
 
 		// wait for 3 minutes that the proxy has published its first RSD
 		if (!manager.waitForAnyOccurence(proxy1.getNodeName(),
-				BobEventType.BOB_DESC_PUBLISHED_RECEIVED, 3L * 60L * 1000L)) {
+				HiddenServiceEventType.BOB_DESC_PUBLISHED_RECEIVED,
+				3L * 60L * 1000L)) {
 
 			// failed to publish an RSD
 			System.out.println("Failed to publish an RSD!");
 			return;
 		}
+		System.out.println("Successfully published an RSD!");
 
 		// create server application
-		ServerApplication server = network.createServer("server", 7005);
+		ServerApplication server = network.createServer("server", hidServ1
+				.getServicePort());
 
 		// determine onion address for hidden service
-		String onionAddress = proxy1.getOnionAddress("hidServ", 1);
+		String onionAddress = hidServ1.determineOnionAddress();
 
 		// create client application
 		ClientApplication client = network.createClient("client", onionAddress,
-				80, 7012);
+				hidServ1.getVirtualPort(), proxy2.getSocksPort());
 
 		// create event listener to listen for client and server application
 		// events
@@ -138,14 +143,14 @@
 					requestReceivedAtServer = event.getOccurrenceTime();
 					System.out.println("Request took "
 							+ (requestReceivedAtServer - requestSentFromClient)
-							+ " millis from client to server!");
+							+ " milliseconds from client to server!");
 				} else if (event.getType() == ClientEventType.CLIENT_REPLY_RECEIVED) {
 					System.out
 							.println("Request took "
 									+ (event.getOccurrenceTime() - requestSentFromClient)
-									+ " millis for the round-trip and "
+									+ " milliseconds for the round-trip and "
 									+ (event.getOccurrenceTime() - requestReceivedAtServer)
-									+ " millis from server to client!");
+									+ " milliseconds from server to client!");
 				}
 			}
 		};
@@ -157,10 +162,10 @@
 				clientAndServerEventListener);
 
 		// start server
-		server.listen();
+		server.startListening();
 
 		// perform at most five request with a timeout of 45 seconds each
-		client.performRequest(5, 45000, true);
+		client.startRequests(5, 45000, true);
 
 		// block this thread as long as client requests are running
 		manager.waitForAnyOccurence(client.getClientApplicationName(),
@@ -169,6 +174,8 @@
 		// shut down proxy
 		network.shutdownNodes();
 
-		System.out.println("Exiting...");
+		// Shut down the JVM
+		System.out.println("Goodbye.");
+		System.exit(0);
 	}
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -33,19 +33,19 @@
 
 import java.rmi.RemoteException;
 
-import de.uniba.wiai.lspi.puppetor.BobEventType;
 import de.uniba.wiai.lspi.puppetor.Event;
 import de.uniba.wiai.lspi.puppetor.EventListener;
 import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.HiddenServiceEventType;
 import de.uniba.wiai.lspi.puppetor.Network;
 import de.uniba.wiai.lspi.puppetor.NetworkFactory;
 import de.uniba.wiai.lspi.puppetor.NodeEventType;
 import de.uniba.wiai.lspi.puppetor.ProxyNode;
-import de.uniba.wiai.lspi.puppetor.TorProcessException;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
 
 /**
  * Example for advertising a hidden service to the public Tor network and
- * observe publication of rendezvous service descriptors.
+ * observing the publication of rendezvous service descriptors.
  * 
  * @author kloesing
  */
@@ -56,22 +56,21 @@
 	 * 
 	 * @param args
 	 *            Command-line arguments (ignored).
-	 * @throws TorProcessException
+	 * @throws PuppeTorException
 	 *             Thrown if there is a problem with the JVM-external Tor
 	 *             processes that we cannot handle.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 */
-	public static void main(String[] args) throws TorProcessException,
+	public static void main(String[] args) throws PuppeTorException,
 			RemoteException {
 
 		// though we only need one proxy, we always need to create a network
 		// to initialize a test case
 		Network network = NetworkFactory.createNetwork("example2");
 
-		// create a single proxy node with name "proxy", control port 7001,
-		// and SOCKS port 7002
-		ProxyNode proxy = network.createProxy("proxy", 7001, 7002);
+		// create a single proxy node
+		ProxyNode proxy = network.createProxy("proxy");
 
 		// add hidden service to the configuration
 		proxy.addHiddenService("hidServ", 7005, 80);
@@ -90,10 +89,10 @@
 					if (circuitOpened == -1) {
 						circuitOpened = System.currentTimeMillis();
 					}
-				} else if (event.getType() == BobEventType.BOB_DESC_PUBLISHED_RECEIVED) {
+				} else if (event.getType() == HiddenServiceEventType.BOB_DESC_PUBLISHED_RECEIVED) {
 					System.out.println("RSD published "
 							+ (System.currentTimeMillis() - circuitOpened)
-							+ " millis after first circuit was opened");
+							+ " milliseconds after first circuit was opened");
 				}
 			}
 		};
@@ -109,9 +108,10 @@
 		if (!network.startNodes(5000)) {
 
 			// failed to start the proxy
-			System.out.println("Failed to start the proxy!");
+			System.out.println("Failed to start the node!");
 			return;
 		}
+		System.out.println("Successfully started the node!");
 
 		// hup until proxy has built circuits (5 retries, 10 seconds timeout
 		// each)
@@ -121,6 +121,7 @@
 			System.out.println("Failed to build circuits!");
 			return;
 		}
+		System.out.println("Successfully built circuits!");
 
 		// let it run for 2 minutes and observe when RSDs are published...
 		System.out
@@ -135,6 +136,8 @@
 		// shut down proxy
 		network.shutdownNodes();
 
-		System.out.println("Exiting...");
+		// Shut down the JVM
+		System.out.println("Goodbye.");
+		System.exit(0);
 	}
 }

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/LongRunningNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/LongRunningNetwork.java	                        (rev 0)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/LongRunningNetwork.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2007, Patrick Geyer, Johannes Jungkunst, Karsten Loesing,
+ * Stefan Schilling
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import java.rmi.RemoteException;
+
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
+
+/**
+ * Example for starting and running a private network of routers and directories
+ * that may be accessed via RMI, e.g. to be merged with another network. Note
+ * that the <code>rmiregistry</code> must be running in order to run this
+ * example.
+ * 
+ * @author pgeyer, jjungkunst, sschilling
+ */
+public class LongRunningNetwork {
+
+	/**
+	 * Sets up and runs the test.
+	 * 
+	 * @param args
+	 *            Command-line arguments (ignored).
+	 * @throws PuppeTorException
+	 *             Thrown if there is a problem with the JVM-external Tor
+	 *             processes that we cannot handle.
+	 * @throws RemoteException
+	 *             Thrown if an error occurs when accessed remotely.
+	 */
+	public static void main(String[] args) throws PuppeTorException,
+			RemoteException {
+
+		// create a network to initialize a test case (networkName, startPort)
+		Network network = NetworkFactory.createNetwork("longRunning");
+
+		// create two directory nodes
+		network.createDirectory("dir1");
+		network.createDirectory("dir2");
+
+		// create six router nodes
+		network.createRouter("router1");
+		network.createRouter("router2");
+		network.createRouter("router3");
+		network.createRouter("router4");
+		network.createRouter("router5");
+		network.createRouter("router6");
+
+		// obtain reference to event manager to be able to respond to events
+		EventManager manager = network.getEventManager();
+		EventListener listener = new EventListener() {
+			public void handleEvent(Event event) {
+				System.out.println(event);
+			}
+		};
+		manager.addEventListener(listener);
+
+		// configure nodes of this network to be part of a private network
+		network.configureAsPrivateNetwork();
+		System.out.println("Network written ...");
+
+		// write configuration of proxy node
+		network.writeConfigurations();
+		System.out.println("Config written ...");
+
+		// start proxy node and wait until it has opened a circuit with a
+		// timeout of 5 seconds
+		if (!network.startNodes(5000)) {
+			System.out.println("Failed to start the nodes!");
+			return;
+		}
+		System.out.println("Nodes started ...");
+
+		// hup until the nodes have built circuits (5 retries, 10 seconds timeout
+		// each)
+		if (!network.hupUntilUp(5, 10000)) {
+
+			// failed to build circuits
+			System.out.println("Failed to build circuits!");
+			return;
+		}
+		System.out.println("Successfully built circuits!");
+
+		// bind the network to rmiregistry
+		if (!network.bindAtRmiregistry()) {
+
+			// failed to bind at rmiregistry
+			System.out.println("Failed to bind at rmiregistry! "
+					+ "Is rmiregistry running?");
+			return;
+		}
+		System.out.println("Bound at rmiregistry to name \""
+				+ network.getNetworkName() + "\"!");
+
+		// wait until the end of time
+		System.out.println("Waiting until the end of time... "
+				+ "Interrupt with Ctrl-C!");
+		try {
+			Thread.sleep(Long.MAX_VALUE);
+		} catch (InterruptedException e) {
+			// do nothing
+		}
+	}
+}
\ No newline at end of file

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/MergingProxiesWithLongRunningNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/MergingProxiesWithLongRunningNetwork.java	                        (rev 0)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/MergingProxiesWithLongRunningNetwork.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2007, Patrick Geyer, Johannes Jungkunst, Karsten Loesing,
+ * Stefan Schilling
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * 
+ * WARNING! This class is untested, yet! Don't rely on it!
+ *
+ */
+
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import java.rmi.RemoteException;
+
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
+
+/**
+ * Example for a private network containing only proxies that is merged with
+ * another, long-running private network that contains routers and directories.
+ * Note that <code>LongRunningNetwork</code> must be running in order to run
+ * this example.
+ * 
+ * @author pgeyer, jjungkunst, sschilling
+ */
+public class MergingProxiesWithLongRunningNetwork {
+
+	/**
+	 * Sets up and runs the test.
+	 * 
+	 * @param args
+	 *            Command-line arguments (ignored).
+	 * @throws PuppeTorException
+	 *             Thrown if there is a problem with the JVM-external Tor
+	 *             processes that we cannot handle.
+	 * @throws RemoteException
+	 *             Thrown if an error occurs when accessed remotely.
+	 */
+	public static void main(String[] args) throws PuppeTorException,
+			RemoteException {
+
+		// create a network to initialize a test case
+		Network network = NetworkFactory.createNetwork("proxies", 8000);
+
+		// create two proxy nodes
+		network.createProxy("client", 9001, 9050);
+		network.createProxy("server", 9051, 9002);
+
+		// obtain reference to event manager to be able to respond to events
+		EventManager manager = network.getEventManager();
+		EventListener listener = new EventListener() {
+			public void handleEvent(Event event) {
+				System.out.println(event);
+			}
+		};
+		manager.addEventListener(listener);
+
+		// obtain a reference on the remote long-running network
+		Network remoteNetwork = NetworkFactory.connectToNetwork("127.0.0.1",
+				"longRunning");
+
+		// merge configuration with long-running network
+		System.out.println("Merging network configurations...");
+		network.configureAsInterconnectedPrivateNetwork(remoteNetwork);
+		System.out.println("Network configurations merged!");
+
+		// write configuration of proxy node
+		network.writeConfigurations();
+		System.out.println("Config written ...");
+
+		// start proxy node and wait until it has opened a circuit with a
+		// timeout of 5 seconds
+		if (!network.startNodes(5000)) {
+			System.out.println("Failed to start the nodes!");
+			return;
+		}
+		System.out.println("Nodes started ...");
+
+		// hup until proxy has built circuits (20 retries, 5 seconds timeout
+		// each)
+		if (!network.hupUntilUp(20, 5000)) {
+
+			// failed to build circuits
+			System.out.println("Failed to build circuits!");
+			return;
+		}
+		System.out.println("Successfully built circuits!");
+
+		// wait for an hour
+		try {
+			Thread.sleep(60 * 60 * 1000);
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+
+		// shutdown after hours
+		network.shutdownNodes();
+
+		// Shut down the JVM
+		System.out.println("Goodbye.");
+		System.exit(0);
+	}
+}
\ No newline at end of file

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/MergingRoutersWithLongRunningNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/MergingRoutersWithLongRunningNetwork.java	                        (rev 0)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/MergingRoutersWithLongRunningNetwork.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2007, Patrick Geyer, Johannes Jungkunst, Karsten Loesing,
+ * Stefan Schilling
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the names of the copyright owners nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import java.rmi.RemoteException;
+
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
+
+/**
+ * Example for a private network of routers without directories that is merged
+ * with another, long-running private network that contains routers and
+ * directories. Note that <code>LongRunningNetwork</code> must be running in
+ * order to run this example.
+ * 
+ * @author pgeyer, jjungkunst, sschilling
+ */
+public class MergingRoutersWithLongRunningNetwork {
+
+	/**
+	 * Sets up and runs the test.
+	 * 
+	 * @param args
+	 *            Command-line arguments (ignored).
+	 * @throws PuppeTorException
+	 *             Thrown if there is a problem with the JVM-external Tor
+	 *             processes that we cannot handle.
+	 * @throws RemoteException
+	 *             Thrown if an error occurs when accessed remotely.
+	 */
+	public static void main(String[] args) throws PuppeTorException,
+			RemoteException {
+
+		// create a network to initialize a test case (networkName)
+		Network network = NetworkFactory.createNetwork("routers", 9000);
+
+		// create six router nodes
+		network.createRouter("router10");
+		network.createRouter("router11");
+		network.createRouter("router12");
+		network.createRouter("router13");
+		network.createRouter("router14");
+		network.createRouter("router15");
+
+		// obtain reference to event manager to be able to respond to events
+		EventManager manager = network.getEventManager();
+		EventListener listener = new EventListener() {
+			public void handleEvent(Event event) {
+				System.out.println(event);
+			}
+		};
+		manager.addEventListener(listener);
+
+		// configure nodes of this network to be part of a private network
+		network.configureAsPrivateNetwork();
+		System.out.println("Network written ...");
+
+		// obtain a reference on the remote long-running network
+		Network remoteNetwork = NetworkFactory.connectToNetwork("127.0.0.1",
+				"longRunning");
+
+		// merge configuration with long-running network
+		network.configureAsInterconnectedPrivateNetwork(remoteNetwork);
+		System.out.println("Network configurations merged ...");
+
+		// write configurations and hup all nodes in the other network
+		remoteNetwork.writeConfigurations();
+		remoteNetwork.hupAllNodes();
+
+		// write configuration of proxy node
+		network.writeConfigurations();
+		System.out.println("Config written ...");
+
+		// start proxy node and wait until it has opened a circuit with a
+		// timeout of 5 seconds
+		if (!network.startNodes(5000)) {
+			System.out.println("Failed to start the nodes!");
+			return;
+		}
+		System.out.println("Nodes started ...");
+
+		// hup until proxy has built circuits (20 retries, 5 seconds timeout
+		// each)
+		if (!network.hupUntilUp(20, 5000)) {
+
+			// failed to build circuits
+			System.out.println("Failed to build circuits!");
+			return;
+		}
+		System.out.println("Successfully built circuits!");
+
+		// bind the network to rmiregistry
+		if (!network.bindAtRmiregistry()) {
+
+			// failed to bind at rmiregistry
+			System.out.println("Failed to bind at rmiregistry! "
+					+ "Is rmiregistry running?");
+			return;
+		}
+		System.out.println("Bound at rmiregistry to name \""
+				+ network.getNetworkName() + "\"!");
+
+		// wait for an hour
+		try {
+			Thread.sleep(60 * 60 * 1000);
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+
+		// shutdown after hours
+		network.shutdownNodes();
+
+		// Shut down the JVM
+		System.out.println("Goodbye.");
+		System.exit(0);
+	}
+}
\ No newline at end of file

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/groovy/RmiPuppetzShell.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/groovy/RmiPuppetzShell.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/groovy/RmiPuppetzShell.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -30,16 +30,6 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
-/*
- *  
- * WARNING! This class is untested, yet! Don't rely on it!
- *
- */
-
-/**
- * @author pgeyer, jjungkunst, sschilling
- */
 package de.uniba.wiai.lspi.puppetor.groovy;
 
 import groovy.lang.GroovyShell;
@@ -49,6 +39,10 @@
 import java.awt.Dimension;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintStream;
@@ -59,6 +53,7 @@
 import java.rmi.server.UnicastRemoteObject;
 import java.util.HashMap;
 import java.util.LinkedList;
+import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -69,155 +64,135 @@
 import javax.swing.JSplitPane;
 import javax.swing.JTabbedPane;
 import javax.swing.JTextArea;
+import javax.swing.JTextField;
 
 import de.uniba.wiai.lspi.puppetor.Event;
 import de.uniba.wiai.lspi.puppetor.EventListener;
 import de.uniba.wiai.lspi.puppetor.Network;
 
- at SuppressWarnings("serial")
 /**
- * A <code>RmiPuppetzShell</code> represents a command shell to PuppeTor. By
- * the use of a groovy interpreter, the different nodes of a Tor network can be
- * manipulated.
+ * The <code>RmiPuppetzShell</code> constitutes a simple graphical interface
+ * to PuppeTor networks. It can connect to already running PuppeTor networks via
+ * RMI and inspect or manipulate those networks using the Java-based script
+ * language Groovy.
  * 
  * @author pgeyer, jjungkunst, sschilling
  */
+ at SuppressWarnings("serial")
 public class RmiPuppetzShell extends JFrame {
 
 	/**
-	 * The shell that evaluates the groovy commands.
+	 * Main method to start the GUI.
+	 * 
+	 * @param args
+	 *            (ignored)
 	 */
-	private GroovyShell shell;
+	public static void main(String args[]) {
+		new RmiPuppetzShell();
+	}
 
-	private Logger logger;
-
 	/**
-	 * The ContentPane to which all other graphical components are added.
+	 * The southern panel for the buttons.
 	 */
-	private JPanel jContentPane = null;
+	private JPanel buttonJPanel = null;
 
 	/**
-	 * The text area to which the groovy commands are entered
+	 * Command history.
 	 */
-	private JTextArea inputJTextArea = null;
+	private LinkedList<String> commandHistory = null;
 
 	/**
-	 * The OK button
+	 * Counter that referes to the current entry in the command history.
 	 */
-	private JButton okJButton = null;
+	private int commandHistoryIndex = 0;
 
 	/**
-	 * The sothern panel for the buttons
+	 * Scroll pane for the output text area.
 	 */
-	private JPanel buttonJPanel = null;
+	private JScrollPane ConsoleJScrollPane = null;
 
 	/**
-	 * The Console where local evants can be printed to.
+	 * The console where local events can be printed to.
 	 */
 	private JTextArea consoleJTextArea = null;
 
 	/**
-	 * Splitpane to devide the main window into a input field for groovy
-	 * commands and an output area for the different networks.
+	 * Map to store the EventListeners under the corresponding network name.
 	 */
-	private JSplitPane jSplitPane = null;
+	private Map<String, EventListener> eventListeners;
 
 	/**
-	 * The tabbedpane that provides tabs for the different networks and the
-	 * console
+	 * Scroll pane for the input text field for Groovy commands.
 	 */
-	private JTabbedPane jTabbedPane = null;
+	private JScrollPane inputJScrollPane = null;
 
 	/**
-	 * Scrollpane for the input jTextArea for the goovy commands
+	 * The text field to which Groovy commands may be entered.
 	 */
-	private JScrollPane inputJScrollPane = null;
+	private JTextField inputJTextField = null;
 
 	/**
-	 * Scrollpane for the output jTextArea.
+	 * The 'next' button.
 	 */
-	private JScrollPane ConsoleJScrollPane = null;
+	private JButton jButtonNext = null;
 
 	/**
-	 * A hash map to store the EventListeners with the coresponding network name
+	 * The 'previous' button.
 	 */
-	private HashMap<String, EventListener> eventListeners = new HashMap<String, EventListener>();
+	private JButton jButtonPrevious = null;
 
 	/**
-	 * A hash map to store the entiries that have been entered into the shell.
+	 * The content pane to which all other graphical components are added.
 	 */
-	private LinkedList<String> shellEntries = null; // @jve:decl-index=0:
+	private JPanel jContentPane = null;
 
-	private int listCounter = 0;
+	/**
+	 * Splitpane to divide the main window into an input field for Groovy
+	 * commands and an output area for the different networks.
+	 */
+	private JSplitPane jSplitPane = null;
 
-	private JButton jButtonNext = null;
+	/**
+	 * The tabbed pane that provides tabs for the different networks and the
+	 * console.
+	 */
+	private JTabbedPane jTabbedPane = null;
 
-	private JButton jButtonPrevious = null;
-
 	/**
-	 * This method initializes jButtonNext
-	 * 
-	 * @return javax.swing.JButton
+	 * Logger for this shell which is simply called "shell".
 	 */
-	private JButton getJButtonNext() {
-		if (jButtonNext == null) {
-			jButtonNext = new JButton();
-			jButtonNext.setText(">");
-			jButtonNext.addMouseListener(new java.awt.event.MouseAdapter() {
-				public void mouseReleased(java.awt.event.MouseEvent e) {
-					displayNextInput();
-				}
-			});
-		}
-		return jButtonNext;
-	}
+	private Logger logger;
 
 	/**
-	 * This method initializes jButtonPrevious
-	 * 
-	 * @return javax.swing.JButton
+	 * The 'OK' button.
 	 */
-	private JButton getJButtonPrevious() {
-		if (jButtonPrevious == null) {
-			jButtonPrevious = new JButton();
-			jButtonPrevious.setText("<");
-			jButtonPrevious.addMouseListener(new java.awt.event.MouseAdapter() {
-				public void mouseReleased(java.awt.event.MouseEvent e) {
-					displayPreviousInput();
-				}
-			});
-		}
-		return jButtonPrevious;
-	}
+	private JButton okJButton = null;
 
 	/**
-	 * Main method to start the Gui.
-	 * 
-	 * @param args
+	 * The shell that executes Groovy commands.
 	 */
-	public static void main(String args[]) {
+	private GroovyShell shell;
 
-		new RmiPuppetzShell();
-	}
-
 	/**
-	 * Initializes the Gui and registers it as a listener to the netaNetwork
+	 * Creates a new instance and initializes the GUI.
 	 */
-	public RmiPuppetzShell() {
+	private RmiPuppetzShell() {
 
 		// create logger
-		this.logger = Logger.getLogger("RmiPuppetzShell" + "."
-				+ this.getClass().getName());
-
+		this.logger = Logger.getLogger("shell");
 		this.logger.setLevel(Level.ALL);
 
 		// log entering
-		this.logger.entering(this.getClass().getName(), "RmiPuppetzShell",
-				new Object[] {});
+		this.logger.entering(this.getClass().getName(), "RmiPuppetzShell");
 
+		// initialize data structures
+		this.eventListeners = new HashMap<String, EventListener>();
+		this.commandHistory = new LinkedList<String>();
+
+		// initialize the GUI
 		initialize();
 
-		// binds network on the shell
+		// bind this instance to the shell
 		try {
 			shell.setVariable("shell", this);
 			consoleJTextArea.append("Welcome, type \"shell.[method]\" to use!"
@@ -233,12 +208,58 @@
 	}
 
 	/**
-	 * Lookup an already running network.
+	 * Adds a tab to the network.
 	 * 
+	 * A tab is created in the lower area of the Gui that is supposed to print
+	 * the output of an <code>EventManager<code> of a Network. To do this an 
+	 * <code>EventListener<code> is created and its stub is returned.
+	 * 
+	 * @param tabName The name of the new tab.
+	 * @return The RMI stub of the event listener added to the output tab.
+	 */
+	public EventListener addLogTab(String tabName) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "addLogTab", tabName);
+
+		// create a scroll pane containing a text area
+		JScrollPane jScrollPane = new JScrollPane();
+		final JTextArea jTextArea = new JTextArea();
+		jScrollPane.getViewport().add(jTextArea);
+
+		// add the scroll pane to the tabbed pane
+		jTabbedPane.add(tabName, jScrollPane);
+
+		// Creating EventListener
+		EventListener el = new EventListener() {
+			public void handleEvent(Event event) {
+				jTextArea.append(event.getMessage() + "\n");
+			}
+		};
+		this.eventListeners.put(tabName, el);
+
+		// creating an EventListener stub
+		EventListener elStub = null;
+		try {
+			elStub = (EventListener) UnicastRemoteObject.exportObject(el, 0);
+		} catch (RemoteException e) {
+			this.logger.throwing(this.getClass().getName(), "addLogTab", e);
+			e.printStackTrace();
+		}
+
+		// log exiting and return stub
+		this.logger.exiting(this.getClass().getName(), "addLogTab");
+		return elStub;
+	}
+
+	/**
+	 * Lookup an already running PuppeTor network.
+	 * 
 	 * @param remoteHost
-	 *            The IpAddress where the RMI registry is running.
+	 *            The IP address where the RMI registry is running.
 	 * @param bindingName
-	 *            The name whereby the stub is bound.
+	 *            The name to which the network is bound.
+	 * @return Network reference.
 	 * @throws MalformedURLException
 	 *             Thrown to indicate that a malformed URL has occurred. Either
 	 *             no legal protocol could be found in a specification string or
@@ -246,11 +267,10 @@
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
 	 * @throws NotBoundException
-	 *             A NotBoundException is thrown if an attempt is made to lookup
-	 *             or unbind in the registry a name that has no associated
-	 *             binding.
+	 *             Thrown if an attempt is made to lookup a name that has no
+	 *             associated binding.
 	 */
-	public void connectNetwork(String remoteHost, String bindingName)
+	public Network connectNetwork(String remoteHost, String bindingName)
 			throws MalformedURLException, RemoteException, NotBoundException {
 
 		// log entering
@@ -264,7 +284,7 @@
 		// register eventlistener
 		network.getEventManager().addEventListener(addLogTab(bindingName));
 
-		// binds network on the shell
+		// binds network to its name on the shell
 		try {
 			shell.setVariable(bindingName, network);
 			getConsoleJTextArea().append(
@@ -275,249 +295,214 @@
 			e.printStackTrace();
 		}
 
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "connectNetwork");
+		// log exiting and return reference to the network
+		this.logger.exiting(this.getClass().getName(), "connectNetwork",
+				network);
+		return network;
 	}
 
 	/**
-	 * An output stream that writes its output to a javax.swing.JTextArea
-	 * control.
+	 * Displays the next command in the command area.
 	 */
-	private class TextAreaOutputStream extends OutputStream {
-		private JTextArea textControl;
-
-		/**
-		 * Creates a new instance of TextAreaOutputStream which writes to the
-		 * specified instance of javax.swing.JTextArea control.
-		 * 
-		 * @param control
-		 *            A reference to the javax.swing.JTextArea control to which
-		 *            the output must be redirected to.
-		 */
-		public TextAreaOutputStream(JTextArea control) {
-			textControl = control;
+	private void displayNextInput() {
+		if (!(commandHistory.size() == 0)) {
+			commandHistoryIndex--;
+			if (commandHistoryIndex < 0) {
+				commandHistoryIndex = commandHistory.size() - 1;
+			}
+			inputJTextField.setText(commandHistory.get(commandHistoryIndex));
 		}
+	}
 
-		/**
-		 * Writes the specified byte as a character to the
-		 * javax.swing.JTextArea.
-		 * 
-		 * @param b
-		 *            The byte to be written as character to the JTextArea.
-		 */
-		public void write(int b) throws IOException {
-			// append the data as characters to the JTextArea control
-			textControl.append(String.valueOf((char) b));
+	/**
+	 * Displays the previous command in the command area.
+	 */
+	private void displayPreviousInput() {
+		if (!(commandHistory.size() == 0)) {
+			commandHistoryIndex++;
+			if (commandHistoryIndex >= commandHistory.size()) {
+				commandHistoryIndex = 0;
+			}
+			inputJTextField.setText(commandHistory.get(commandHistoryIndex));
 		}
 	}
 
 	/**
-	 * This method initializes the window.
+	 * Executes the command that is written to the command text field.
 	 */
-	private void initialize() {
+	private void executeCommand() {
 
-		// log entering
-		this.logger.entering(this.getClass().getName(), "initialize");
-		this.shellEntries = new LinkedList<String>();
-		this.setSize(new Dimension(531, 256));
-		this.setPreferredSize(new Dimension(100, 150));
-		this.setContentPane(getJContentPane());
-		this.setTitle("Rmi-Puppetz Shell");
-		this.setVisible(true);
-		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
-		shell = new GroovyShell();
+		Thread evaluationThread = new Thread() {
+			@Override
+			public void run() {
 
-		// Create Printstream that writes to JTextArea
-		PrintStream out = new PrintStream(new TextAreaOutputStream(
-				getConsoleJTextArea()));
+				// log entering
+				logger.entering(this.getClass().getName(), "run");
 
-		// redirect standard output stream to the TextAreaOutputStream
-		System.setOut(out);
+				Script script = null;
+				try {
+					String input = inputJTextField.getText();
+					commandHistoryIndex = 0;
+					commandHistory.addFirst(input);
+					script = shell.parse(input);
+				} catch (Exception e1) {
+					consoleJTextArea.append("In correct input\n");
+					e1.printStackTrace();
+					logger.log(Level.WARNING, e1.getMessage());
+					return;
+				}
+				try {
+					script.run();
+				} catch (Exception e2) {
+					consoleJTextArea
+							.append("An error occured while performing script"
+									+ "\n");
+					e2.printStackTrace();
+					logger.log(Level.WARNING, e2.getMessage());
+					return;
+				}
 
-		// redirect error output stream to the TextAreaOutputStream
-		System.setErr(out);
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "initialize");
+				// log exiting
+				logger.exiting(this.getClass().getName(), "run");
+			}
+		};
+		evaluationThread.setName("evaluationThread");
+		evaluationThread.start();
 	}
 
 	/**
-	 * This method initializes jContentPane
+	 * This method initializes ConsoleJScrollPane
 	 * 
-	 * @return javax.swing.JPanel
+	 * @return JScrollPane
 	 */
-	private JPanel getJContentPane() {
-		// log entering
-		this.logger.entering(this.getClass().getName(), "getJContentPane");
-		if (jContentPane == null) {
-			jContentPane = new JPanel();
-			jContentPane.setLayout(new BorderLayout());
-			jContentPane.add(getJPanel(), BorderLayout.SOUTH);
-			jContentPane.add(getJSplitPane(), BorderLayout.CENTER);
+	private JScrollPane getConsoleJScrollPane() {
+		if (ConsoleJScrollPane == null) {
+			ConsoleJScrollPane = new JScrollPane();
+			ConsoleJScrollPane.getViewport().add(getConsoleJTextArea());
 		}
+		return ConsoleJScrollPane;
+	}
 
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "getJContentPane");
-		return jContentPane;
+	/**
+	 * This method initializes consoleJTextArea
+	 * 
+	 * @return JTextArea
+	 */
+	private JTextArea getConsoleJTextArea() {
+		if (consoleJTextArea == null) {
+			consoleJTextArea = new JTextArea();
+			consoleJTextArea.setEditable(false);
+			consoleJTextArea.setName("Console");
+		}
+		return consoleJTextArea;
 	}
 
 	/**
 	 * This method initializes inputJScrollPane
 	 * 
-	 * @return javax.swing.JTextArea
+	 * @return JTextArea
 	 */
 	private JScrollPane getInputJScrollPane() {
-		// log entering
-		this.logger.entering(this.getClass().getName(), "getInputJScrollPane");
 		if (inputJScrollPane == null) {
 			inputJScrollPane = new JScrollPane();
 			inputJScrollPane.getViewport().add(getInputJTextArea());
 		}
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "getInputJScrollPane");
-
 		return inputJScrollPane;
 	}
 
 	/**
-	 * This method initializes inputJTextArea
+	 * This method initializes inputJTextField
 	 * 
-	 * @return javax.swing.JTextArea
+	 * @return JTextArea
 	 */
-	private JTextArea getInputJTextArea() {
-
-		// log entering
-		this.logger.entering(this.getClass().getName(), "getInputJTextArea");
-		if (inputJTextArea == null) {
-			inputJTextArea = new JTextArea();
-			inputJTextArea.setEditable(true);
+	private JTextField getInputJTextArea() {
+		if (inputJTextField == null) {
+			inputJTextField = new JTextField();
+			inputJTextField.setEditable(true);
+			inputJTextField.addActionListener(new ActionListener() {
+				public void actionPerformed(ActionEvent e) {
+					executeCommand();
+				}
+			});
 		}
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "getInputJTextArea");
-		return inputJTextArea;
+		return inputJTextField;
 	}
 
 	/**
-	 * This method initializes ConsoleJScrollPane
+	 * This method initializes okJButton.
 	 * 
-	 * @return javax.swing.JScrollPane
+	 * @return JButton
 	 */
-	private JScrollPane getConsoleJScrollPane() {
-
-		// log entering
-		this.logger.entering(this.getClass().getName(),
-				"getConsoleJScrollPane", new Object[] {});
-
-		if (ConsoleJScrollPane == null) {
-			ConsoleJScrollPane = new JScrollPane();
-			ConsoleJScrollPane.getViewport().add(getConsoleJTextArea());
+	private JButton getJButton() {
+		if (okJButton == null) {
+			okJButton = new JButton();
+			okJButton.setText("OK");
+			okJButton.addMouseListener(new MouseAdapter() {
+				public void mouseReleased(MouseEvent e) {
+					executeCommand();
+				}
+			});
 		}
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "getConsoleJScrollPane");
-
-		return ConsoleJScrollPane;
+		return okJButton;
 	}
 
 	/**
-	 * This method initializes consoleJTextArea
+	 * This method initializes jButtonNext
 	 * 
-	 * @return javax.swing.JTextArea
+	 * @return JButton
 	 */
-	private JTextArea getConsoleJTextArea() {
-
-		// log entering
-		this.logger.entering(this.getClass().getName(), "getConsoleJTextArea");
-
-		if (consoleJTextArea == null) {
-			consoleJTextArea = new JTextArea();
-			consoleJTextArea.setEditable(false);
-			consoleJTextArea.setName("Console");
+	private JButton getJButtonNext() {
+		if (jButtonNext == null) {
+			jButtonNext = new JButton();
+			jButtonNext.setText(">");
+			jButtonNext.addMouseListener(new MouseAdapter() {
+				public void mouseReleased(MouseEvent e) {
+					displayNextInput();
+				}
+			});
 		}
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "getConsoleJTextArea");
-
-		return consoleJTextArea;
+		return jButtonNext;
 	}
 
 	/**
-	 * This method initializes okJButton.
+	 * This method initializes jButtonPrevious
 	 * 
-	 * It also creates and adds an EventListener that evaluates the content of
-	 * the inputJTextArea in groovy shell.
-	 * 
 	 * @return javax.swing.JButton
 	 */
-	private JButton getJButton() {
-
-		// log entering
-		this.logger.entering(this.getClass().getName(), "getJButton");
-
-		if (okJButton == null) {
-			okJButton = new JButton();
-			okJButton.setText("OK");
-			okJButton.addMouseListener(new java.awt.event.MouseAdapter() {
-				public void mouseReleased(java.awt.event.MouseEvent e) {
-
-					Thread evaluationThread = new Thread() {
-						@Override
-						public void run() {
-							// log entering
-							logger.entering(this.getClass().getName(), "run");
-							Script script = null;
-							try {
-								String input = inputJTextArea.getText();
-								listCounter = 0;
-								shellEntries.addFirst(input);
-								script = shell.parse(input);
-							} catch (Exception e1) {
-								consoleJTextArea.append("In correct input"
-										+ "\n");
-								e1.printStackTrace();
-								logger.log(Level.WARNING, e1.getMessage());
-								return;
-							}
-							try {
-								script.run();
-							} catch (Exception e2) {
-								consoleJTextArea
-										.append("An error occured while performing script"
-												+ "\n");
-								e2.printStackTrace();
-								logger.log(Level.WARNING, e2.getMessage());
-								return;
-							}
-
-							// log exiting
-							logger.exiting(this.getClass().getName(), "run");
-						}
-					};
-					evaluationThread.setName("evaluationThread");
-					evaluationThread.start();
-
+	private JButton getJButtonPrevious() {
+		if (jButtonPrevious == null) {
+			jButtonPrevious = new JButton();
+			jButtonPrevious.setText("<");
+			jButtonPrevious.addMouseListener(new MouseAdapter() {
+				public void mouseReleased(MouseEvent e) {
+					displayPreviousInput();
 				}
 			});
 		}
+		return jButtonPrevious;
+	}
 
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "getJButton");
-
-		return okJButton;
+	/**
+	 * This method initializes jContentPane
+	 * 
+	 * @return JPanel
+	 */
+	private JPanel getJContentPane() {
+		if (jContentPane == null) {
+			jContentPane = new JPanel();
+			jContentPane.setLayout(new BorderLayout());
+			jContentPane.add(getJPanel(), BorderLayout.SOUTH);
+			jContentPane.add(getJSplitPane(), BorderLayout.CENTER);
+		}
+		return jContentPane;
 	}
 
 	/**
 	 * This method initializes buttonJPanel
 	 * 
-	 * @return javax.swing.JPanel
+	 * @return JPanel
 	 */
 	private JPanel getJPanel() {
-
-		// log entering
-		this.logger.entering(this.getClass().getName(), "getJPanel");
-
 		if (buttonJPanel == null) {
 			GridBagConstraints gridBagConstraints2 = new GridBagConstraints();
 			gridBagConstraints2.gridx = 0;
@@ -535,130 +520,70 @@
 			buttonJPanel.add(getJButtonNext(), gridBagConstraints1);
 			buttonJPanel.add(getJButtonPrevious(), gridBagConstraints2);
 		}
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "getJPanel");
-
 		return buttonJPanel;
 	}
 
 	/**
 	 * This method initializes jSplitPane
 	 * 
-	 * @return javax.swing.JSplitPane
+	 * @return JSplitPane
 	 */
 	private JSplitPane getJSplitPane() {
-
-		// log entering
-		this.logger.entering(this.getClass().getName(), "getJSplitPane");
-
 		if (jSplitPane == null) {
 			jSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
 					getInputJScrollPane(), getJTabbedPane());
 			jSplitPane.setContinuousLayout(true);
 		}
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "getJSplitPane");
-
 		return jSplitPane;
 	}
 
 	/**
 	 * This method initializes jTabbedPane
 	 * 
-	 * @return javax.swing.JTabbedPane
+	 * @return JTabbedPane
 	 */
 	private JTabbedPane getJTabbedPane() {
-
-		// log entering
-		this.logger.entering(this.getClass().getName(), "getJTabbedPane");
-
 		if (jTabbedPane == null) {
 			jTabbedPane = new JTabbedPane();
 			jTabbedPane.add("Console", getConsoleJScrollPane());
 		}
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "getJTabbedPane");
-
 		return this.jTabbedPane;
 	}
 
 	/**
-	 * Adds a tab to the network.
-	 * 
-	 * A tab is created in the lower area of the Gui that is supposed to print
-	 * the output of an <code>EventManager<code> of a Network. To do this an 
-	 * <code>EventListener<code> is created and its stub is returned.
-	 *  
-	 * @return the stub of the event Listener added to the output tab of a 
-	 * registered network
+	 * This method initializes the main window.
 	 */
-	public EventListener addLogTab(String tabName) {
+	private void initialize() {
 
 		// log entering
-		this.logger.entering(this.getClass().getName(), "addLogTab", tabName);
+		this.logger.entering(this.getClass().getName(), "initialize");
 
-		// Creating ScrollPane & TextArea
-		JScrollPane jScrollPane = new JScrollPane();
-		final JTextArea jTextArea = new JTextArea();
-		// Adding TextArea to ScrollPane's Viewport
-		jScrollPane.getViewport().add(jTextArea);
-		// add ScrollPane to TabbedPane
-		jTabbedPane.add(tabName, jScrollPane);
-		// Creating EventListener
-		EventListener el = new EventListener() {
+		// initialize GUI elements
+		this.setSize(new Dimension(531, 256));
+		this.setPreferredSize(new Dimension(100, 150));
+		this.setContentPane(getJContentPane());
+		this.setTitle("Rmi-Puppetz Shell");
+		this.setVisible(true);
+		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
 
-			public void handleEvent(Event event) {
+		this.shell = new GroovyShell();
 
-				jTextArea.append(event.getMessage() + "\n");
+		// create Printstream that writes to JTextArea
+		final JTextArea textArea = getConsoleJTextArea();
+		PrintStream out = new PrintStream(new OutputStream() {
+			@Override
+			public void write(int b) throws IOException {
+				textArea.append(String.valueOf((char) b));
 			}
+		});
 
-		};
-		this.eventListeners.put(tabName, el);
-		// creating an EventListener stub
-		EventListener elStub = null;
-		try {
-			elStub = (EventListener) UnicastRemoteObject.exportObject(el, 0);
-		} catch (RemoteException e) {
-			this.logger.throwing(this.getClass().getName(), "addLogTab", e);
-			e.printStackTrace();
-		}
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "addLogTab");
+		// redirect standard output stream to our text area
+		System.setOut(out);
 
-		return elStub;
-	}
+		// redirect error output stream to our text area
+		System.setErr(out);
 
-	/**
-	 * This method cycles throught the previous inputs and displays them to the
-	 * gui.
-	 */
-	private void displayPreviousInput() {
-
-		if (!(shellEntries.size() == 0)) {
-			listCounter++;
-			if (listCounter >= shellEntries.size()) {
-				listCounter = 0;
-			}
-			inputJTextArea.setText(shellEntries.get(listCounter));
-		}
+		// log exiting and return reference to the network
+		this.logger.exiting(this.getClass().getName(), "initialize");
 	}
-
-	/**
-	 * This method cycles throught the previous inputs in reverse order and
-	 * displays them to the gui.
-	 */
-	private void displayNextInput() {
-
-		if (!(shellEntries.size() == 0)) {
-			listCounter--;
-			if (listCounter < 0) {
-				listCounter = shellEntries.size() - 1;
-			}
-			inputJTextArea.setText(shellEntries.get(listCounter));
-		}
-	}
-
-} // @jve:decl-index=0:visual-constraint="28,41"
+}

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -311,13 +311,13 @@
 	private RequestThread clientThread;
 
 	/**
-	 * Event manager to which all events concerning this client application are
-	 * notified.
+	 * Event manager that handles all events concerning this client application.
 	 */
 	private EventManagerImpl eventManager;
 
 	/**
-	 * Logger for this client.
+	 * Logger for this client which is called "client." plus the name of this
+	 * client application.
 	 */
 	private Logger logger;
 
@@ -327,8 +327,8 @@
 	private int socksPort;
 
 	/**
-	 * Target name for the requests sent by this client; can be either a server
-	 * name/address or an onion address.
+	 * Target name for the requests sent by this client; can be a publicly
+	 * available URL or an onion address.
 	 */
 	private String targetName;
 
@@ -370,7 +370,7 @@
 		}
 
 		// create logger
-		this.logger = Logger.getLogger("application." + clientApplicationName);
+		this.logger = Logger.getLogger("client." + clientApplicationName);
 
 		// log entering
 		this.logger.entering(this.getClass().getName(),
@@ -401,7 +401,7 @@
 		this.logger.exiting(this.getClass().getName(), "ClientApplicationImpl");
 	}
 
-	public synchronized void performRequest(int retries,
+	public synchronized void startRequests(int retries,
 			long timeoutForEachRetry, boolean stopOnSuccess) {
 
 		// log entering
@@ -471,4 +471,16 @@
 	public String getClientApplicationName() {
 		return clientApplicationName;
 	}
+
+	public int getSocksPort() {
+		return socksPort;
+	}
+
+	public String getTargetName() {
+		return targetName;
+	}
+
+	public int getTargetPort() {
+		return targetPort;
+	}
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -41,12 +41,12 @@
 import java.util.TreeSet;
 
 import de.uniba.wiai.lspi.puppetor.DirectoryNode;
-import de.uniba.wiai.lspi.puppetor.TorProcessException;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
 
 /**
  * Implementation of <code>DirectoryNode</code>.
  * 
- * @author karsten
+ * @author kloesing
  */
 @SuppressWarnings("serial")
 public class DirectoryNodeImpl extends RouterNodeImpl implements DirectoryNode {
@@ -90,6 +90,9 @@
 	 *             has an invalid value.
 	 * @throws RemoteException
 	 *             Thrown if an error occurs when accessed remotely.
+	 * @throws PuppeTorException
+	 *             Thrown if an I/O problem occurs while writing the temporary
+	 *             <code>approved-routers</code> file.
 	 */
 	DirectoryNodeImpl(NetworkImpl network, String nodeName, int controlPort,
 			int socksPort, int orPort, int dirPort, String serverIpAddress)
@@ -111,10 +114,9 @@
 		// configure this node as an authoritative directory
 		this.configuration.add("AuthoritativeDirectory 1");
 
-		// TODO make this a little bit more configurable---same as to
-		// location of tor executable?
+		// TODO make this a little bit more configurable
 		this.configuration
-				.add("RecommendedVersions 0.1.2.12-rc,0.1.2.7-alpha-dev,0.2.0.0-alpha-dev,0.2.0.2-alpha-dev,0.2.0.4-alpha-dev,0.2.0.4-alpha");
+				.add("RecommendedVersions 0.2.0.7-alpha,0.2.0.7-alpha-dev");
 
 		this.configuration.add("VersioningAuthoritativeDirectory 1");
 
@@ -136,15 +138,14 @@
 		this.logger.exiting(this.getClass().getName(), "DirectoryNodeImpl");
 	}
 
-	public synchronized String determineDirServerString()
-			throws TorProcessException {
+	public synchronized String getDirServerString() throws PuppeTorException,
+			RemoteException {
 
 		// log entering
-		this.logger.entering(this.getClass().getName(),
-				"determineDirServerString");
+		this.logger.entering(this.getClass().getName(), "getDirServerString");
 
 		// determine fingerprint
-		String fingerprint = determineFingerprint();
+		String fingerprint = this.getFingerprint();
 
 		// cut off router nickname
 		fingerprint = fingerprint.substring(fingerprint.indexOf(" ") + 1);
@@ -155,32 +156,14 @@
 				+ " " + fingerprint;
 
 		// log exiting and return dir server string
-		this.logger.exiting(this.getClass().getName(),
-				"determineDirServerString", dirServerString);
+		this.logger.exiting(this.getClass().getName(), "getDirServerString",
+				dirServerString);
 		return dirServerString;
 	}
 
-	public synchronized void writeApprovedRouters(Set<String> routers)
-			throws TorProcessException, RemoteException {
+	public void addApprovedRouters(Set<String> routers) throws RemoteException {
 
 		// log entering
-		this.logger.entering(this.getClass().getName(), "writeApprovedRouters",
-				routers);
-
-		// clear the existing set of approved routers
-		this.approvedRouters.clear();
-
-		// invoke addApprovedRouters to perform the actual work
-		this.addApprovedRouters(routers);
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "writeApprovedRouters");
-	}
-
-	public void addApprovedRouters(Set<String> routers)
-			throws TorProcessException, RemoteException {
-
-		// log entering
 		this.logger.entering(this.getClass().getName(), "addApprovedRouters",
 				routers);
 
@@ -196,8 +179,39 @@
 		// known strings (if any)
 		this.approvedRouters.addAll(routers);
 
-		// store the complete set of approved router strings to file
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "addApprovedRouters");
+	}
+
+	@Override
+	protected void determineFingerprint() {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "determineFingerprint");
+
+		// create an empty approved-routers file to make Tor happy
 		try {
+			new File(this.workingDir.getAbsolutePath() + File.separator
+					+ "approved-routers").createNewFile();
+		} catch (IOException e) {
+			PuppeTorException ex = new PuppeTorException(
+					"Could not write empty approved-routers file!", e);
+			this.caughtException = ex;
+			return;
+		}
+
+		// invoke overwritten method
+		super.determineFingerprint();
+	}
+
+	@Override
+	public synchronized void writeConfiguration() throws PuppeTorException {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "writeConfiguration");
+
+		// write approved-routers file
+		try {
 			File approvedRoutersFile = new File(this.workingDir
 					.getAbsolutePath()
 					+ File.separator + "approved-routers");
@@ -208,39 +222,16 @@
 			}
 			bw.close();
 		} catch (IOException e) {
-			TorProcessException ex = new TorProcessException(e);
+			PuppeTorException ex = new PuppeTorException(e);
 			this.logger.throwing(this.getClass().getName(),
-					"addApprovedRouters", ex);
+					"writeConfiguration", ex);
 			throw ex;
 		}
 
+		// invoke overridden method
+		super.writeConfiguration();
+
 		// log exiting
-		this.logger.exiting(this.getClass().getName(), "addApprovedRouters");
+		this.logger.exiting(this.getClass().getName(), "writeConfiguration");
 	}
-
-	public synchronized String determineFingerprint()
-			throws TorProcessException {
-
-		// log entering
-		this.logger.entering(this.getClass().getName(), "determineFingerprint");
-
-		// check if the fingerprint has not been determined before
-		if (this.fingerprint == null) {
-
-			// create an empty approved-routers file to make Tor happy
-			try {
-				new File(this.workingDir.getAbsolutePath() + File.separator
-						+ "approved-routers").createNewFile();
-			} catch (IOException e) {
-				TorProcessException ex = new TorProcessException(
-						"Could not write empty approved-routers file!", e);
-				this.logger.throwing(this.getClass().getName(),
-						"determineFingerprint", ex);
-				throw ex;
-			}
-		}
-
-		// invoke overwritten method
-		return super.determineFingerprint();
-	}
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventImpl.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventImpl.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -79,15 +79,27 @@
 	 *            Either the log message that led to firing this event, or an
 	 *            internal message.
 	 */
-	EventImpl(long occurrenceTime, String source, EventType type,
-			String message) {
-		// time may be 0 and type null
+	EventImpl(long occurrenceTime, String source, EventType type, String message) {
 		this.occurrenceTime = occurrenceTime;
 		this.source = source;
 		this.type = type;
 		this.message = message;
 	}
 
+	/**
+	 * Creates a new <code>EventImpl</code>.
+	 * 
+	 * @param source
+	 *            The source of this event.
+	 * @param message
+	 *            Either the log message that led to firing this event, or an
+	 *            internal message.
+	 */
+	EventImpl(String source, String message) {
+		this.source = source;
+		this.message = message;
+	}
+
 	public String getSource() {
 		return this.source;
 	}
@@ -117,9 +129,9 @@
 	@Override
 	public String toString() {
 		return this.getClass().getSimpleName() + ": occurenceTime="
-				+ new Date(this.occurrenceTime) + ", source=\""
-				+ this.source.toString() + "\", type=" + this.type.toString()
-				+ ", message=\"" + this.message + "\"";
+				+ new Date(this.occurrenceTime) + ", source=\"" + this.source
+				+ "\", type=" + this.type.getTypeName() + ", message=\""
+				+ this.message + "\"";
 	}
 
 	/**
@@ -129,7 +141,7 @@
 	 *            The occurrence time of the event or of the corresponding log
 	 *            statement.
 	 */
-	public void setOccurenceTime(long occurrenceTime) {
+	void setOccurenceTime(long occurrenceTime) {
 		this.occurrenceTime = occurrenceTime;
 	}
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -42,6 +42,7 @@
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.Map.Entry;
@@ -50,17 +51,17 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import de.uniba.wiai.lspi.puppetor.AliceEventType;
-import de.uniba.wiai.lspi.puppetor.BobEventType;
 import de.uniba.wiai.lspi.puppetor.Event;
 import de.uniba.wiai.lspi.puppetor.EventListener;
 import de.uniba.wiai.lspi.puppetor.EventManager;
 import de.uniba.wiai.lspi.puppetor.EventType;
-import de.uniba.wiai.lspi.puppetor.MiscEventType;
+import de.uniba.wiai.lspi.puppetor.HiddenServiceEventType;
 import de.uniba.wiai.lspi.puppetor.NodeEventType;
 
 /**
  * Implementation of <code>EventManager</code>.
+ * 
+ * @author kloesing
  */
 @SuppressWarnings("serial")
 public class EventManagerImpl extends UnicastRemoteObject implements
@@ -77,8 +78,8 @@
 	private Set<EventListener> eventHandlersForAllSources;
 
 	/**
-	 * Logger for this <code>EventManagerImpl</code> instance which is called
-	 * "event." plus the name of the network.
+	 * Logger for this event manager which is called "event." plus the name of
+	 * the network.
 	 */
 	private Logger logger;
 
@@ -88,8 +89,8 @@
 	private Map<String, List<Event>> observedEvents;
 
 	/**
-	 * Set of all registered event sources. This is required to cross-check
-	 * requests for events from a given source to avoid typos.
+	 * Set of all registered event sources. This is required to ensure that
+	 * requests for events from a given source specify valid event sources.
 	 */
 	private Set<String> eventSources;
 
@@ -103,6 +104,8 @@
 	 * @throws IllegalArgumentException
 	 *             Thrown if the given <code>networkName</code> is either
 	 *             <code>null</code> or a zero-length string.
+	 * @throws RemoteException
+	 *             Thrown if an error occurs when accessed remotely.
 	 */
 	EventManagerImpl(String networkName) throws RemoteException {
 
@@ -266,19 +269,17 @@
 	}
 
 	/**
-	 * An ordered list of all log statements that are yet unparsed.
+	 * An ordered list of all log statements that are still unparsed.
 	 */
 	private List<EventImpl> unparsedLogStatements = new LinkedList<EventImpl>();
 
 	/**
-	 * TODO document me
+	 * Stores the occurrence of an unparsed Tor log events that might result in
+	 * an event. All such unparsed events are later parsed by a background
+	 * thread in invocation order. Then, the occurrence time and the event type
+	 * will be parsed from the log message; if the log message does not contain
+	 * anything of interest, the event will be discarded.
 	 * 
-	 * stores either events produced by ourself or unparsed Tor log events for
-	 * later parsing by a background thread in invocation order
-	 * 
-	 * time and type will be parsed from Tor log message; if message does not
-	 * contain anything of interest, the event will be discarded
-	 * 
 	 * @param source
 	 *            The event source.
 	 * @param logMessage
@@ -287,16 +288,7 @@
 	 *             Thrown if the source is unknown.
 	 */
 	synchronized void observeUnparsedEvent(String source, String logMessage) {
-
-		if (!this.eventSources.contains(source)) {
-			IllegalArgumentException e = new IllegalArgumentException();
-			this.logger.throwing(this.getClass().getName(),
-					"observeInternalEvent", e);
-			throw e;
-		}
-
-		this.unparsedLogStatements.add(new EventImpl(0, source, null,
-				logMessage));
+		this.unparsedLogStatements.add(new EventImpl(source, logMessage));
 		notifyAll();
 	}
 
@@ -330,10 +322,12 @@
 	}
 
 	/**
-	 * Parse a log statement coming from Tor and see if it is interesting for
-	 * us.
+	 * Parses a log statement coming from Tor and decides whether it is
+	 * interesting for us.
 	 */
 	void parseNextEvent() {
+
+		// wait for the next event in the queue
 		EventImpl event = null;
 		synchronized (this) {
 			while (this.unparsedLogStatements.isEmpty()) {
@@ -342,15 +336,21 @@
 				} catch (InterruptedException e) {
 				}
 			}
-
 			event = this.unparsedLogStatements.remove(0);
 		}
 
+		// does the event contain a known source? if not, discard it
+		if (!this.eventSources.contains(event.getSource())) {
+			this.logger.log(Level.WARNING,
+					"Unknown event source while parsing an event: "
+							+ event.getSource());
+			return;
+		}
+
 		// does the event require parsing? if not, process immediately
 		if (event.getType() != null) {
 			observeEvent(event);
 		} else {
-
 			String line = event.getMessage();
 
 			/*
@@ -362,11 +362,12 @@
 			Calendar c = Calendar.getInstance();
 			int currentYear = c.get(Calendar.YEAR);
 
+			// try to apply one of the event type patterns
 			for (Entry<Pattern, EventType> entry : eventTypePatterns.entrySet()) {
 				Matcher matcher = entry.getKey().matcher(line);
 				if (matcher.find()) {
 					SimpleDateFormat sdf = new SimpleDateFormat(
-							"MMM dd HH:mm:ss.SSS");
+							"MMM dd HH:mm:ss.SSS", Locale.US);
 					Date logTime = sdf.parse(line, new ParsePosition(0));
 					c.setTimeInMillis(logTime.getTime());
 					c.set(Calendar.YEAR, currentYear);
@@ -378,12 +379,12 @@
 				}
 			}
 		}
-
 	}
 
 	/**
 	 * Map of all patterns, that should be included when parsing log statements
-	 * coming from Tor, and the respective event types, that should be fired.
+	 * coming from Tor, and the respective event types of the events that should
+	 * be fired.
 	 */
 	Map<Pattern, EventType> eventTypePatterns;
 
@@ -394,7 +395,7 @@
 
 	/**
 	 * Initializes the parsing engine with the standard log message patterns
-	 * that should be included in current Tor. Any further patterns should be
+	 * that should be included in current Tor. Any further patterns need to be
 	 * added by the test application manually.
 	 */
 	private void initializeEventTypePatterns() {
@@ -404,60 +405,61 @@
 		registerEventTypePattern("Tor has successfully opened a circuit. "
 				+ "Looks like client functionality is working.",
 				NodeEventType.NODE_CIRCUIT_OPENED);
-		registerEventTypePattern("Established circuit .* as introduction "
-				+ "point for service .*",
-				BobEventType.BOB_BUILT_INTRO_CIRC_SENDING_ESTABLISH_INTRO);
+		registerEventTypePattern(
+				"Established circuit .* as introduction "
+						+ "point for service .*",
+				HiddenServiceEventType.BOB_BUILT_INTRO_CIRC_SENDING_ESTABLISH_INTRO);
 		registerEventTypePattern("Received INTRO_ESTABLISHED cell on "
 				+ "circuit .* for service .*",
-				BobEventType.BOB_INTRO_ESTABLISHED_RECEIVED);
+				HiddenServiceEventType.BOB_INTRO_ESTABLISHED_RECEIVED);
 		registerEventTypePattern("Sending publish request for hidden "
-				+ "service .*", BobEventType.BOB_SENDING_PUBLISH_DESC);
+				+ "service .*", HiddenServiceEventType.BOB_SENDING_PUBLISH_DESC);
 		registerEventTypePattern("Uploaded rendezvous descriptor",
-				BobEventType.BOB_DESC_PUBLISHED_RECEIVED);
+				HiddenServiceEventType.BOB_DESC_PUBLISHED_RECEIVED);
 		registerEventTypePattern("Received INTRODUCE2 cell for service .* "
-				+ "on circ .*", BobEventType.BOB_INTRODUCE2_RECEIVED);
+				+ "on circ .*", HiddenServiceEventType.BOB_INTRODUCE2_RECEIVED);
 		registerEventTypePattern("Done building circuit .* to rendezvous "
 				+ "with cookie .* for service .*",
-				BobEventType.BOB_BUILT_REND_CIRC_SENDING_RENDEZVOUS1);
+				HiddenServiceEventType.BOB_BUILT_REND_CIRC_SENDING_RENDEZVOUS1);
 		registerEventTypePattern("begin is for rendezvous",
-				BobEventType.BOB_APP_CONN_OPENED);
+				HiddenServiceEventType.BOB_APP_CONN_OPENED);
 		registerEventTypePattern("Got a hidden service request for ID '.*'",
-				AliceEventType.ALICE_ONION_REQUEST_RECEIVED);
+				HiddenServiceEventType.ALICE_ONION_REQUEST_RECEIVED);
 		registerEventTypePattern("Fetching rendezvous descriptor for "
-				+ "service .*", AliceEventType.ALICE_SENDING_FETCH_DESC);
+				+ "service .*", HiddenServiceEventType.ALICE_SENDING_FETCH_DESC);
 		registerEventTypePattern("Received rendezvous descriptor",
-				AliceEventType.ALICE_DESC_FETCHED_RECEIVED);
+				HiddenServiceEventType.ALICE_DESC_FETCHED_RECEIVED);
 		registerEventTypePattern(
 				"Sending an ESTABLISH_RENDEZVOUS cell",
-				AliceEventType.ALICE_BUILT_REND_CIRC_SENDING_ESTABLISH_RENDEZVOUS);
+				HiddenServiceEventType.ALICE_BUILT_REND_CIRC_SENDING_ESTABLISH_RENDEZVOUS);
 		registerEventTypePattern("Got rendezvous ack. This circuit is now "
 				+ "ready for rendezvous",
-				AliceEventType.ALICE_RENDEZVOUS_ESTABLISHED_RECEIVED);
+				HiddenServiceEventType.ALICE_RENDEZVOUS_ESTABLISHED_RECEIVED);
 		registerEventTypePattern("introcirc is open",
-				AliceEventType.ALICE_BUILT_INTRO_CIRC);
+				HiddenServiceEventType.ALICE_BUILT_INTRO_CIRC);
 		registerEventTypePattern("Sending an INTRODUCE1 cell",
-				AliceEventType.ALICE_SENDING_INTRODUCE1);
+				HiddenServiceEventType.ALICE_SENDING_INTRODUCE1);
 		registerEventTypePattern("Received ack. Telling rend circ",
-				AliceEventType.ALICE_INTRODUCE_ACK_RECEIVED);
-		registerEventTypePattern("Got RENDEZVOUS2 cell from hidden service",
-				AliceEventType.ALICE_RENDEZVOUS2_RECEIVED_APP_CONN_OPENED);
+				HiddenServiceEventType.ALICE_INTRODUCE_ACK_RECEIVED);
+		registerEventTypePattern(
+				"Got RENDEZVOUS2 cell from hidden service",
+				HiddenServiceEventType.ALICE_RENDEZVOUS2_RECEIVED_APP_CONN_OPENED);
 		registerEventTypePattern("Handling rendezvous descriptor post",
-				MiscEventType.DIR_PUBLISH_DESC_RECEIVED);
+				HiddenServiceEventType.DIR_PUBLISH_DESC_RECEIVED);
 		registerEventTypePattern("Handling rendezvous descriptor get",
-				MiscEventType.DIR_FETCH_DESC_RECEIVED);
+				HiddenServiceEventType.DIR_FETCH_DESC_RECEIVED);
 		registerEventTypePattern(
 				"Received an ESTABLISH_INTRO request on circuit .*",
-				MiscEventType.IPO_RECEIVED_ESTABLISH_INTRO_SENDING_INTRO_ESTABLISHED);
+				HiddenServiceEventType.IPO_RECEIVED_ESTABLISH_INTRO_SENDING_INTRO_ESTABLISHED);
 		registerEventTypePattern(
 				"Received an INTRODUCE1 request on circuit .*",
-				MiscEventType.IPO_RECEIVED_INTRODUCE1_SENDING_INTRODUCE2_AND_INTRODUCE_ACK);
+				HiddenServiceEventType.IPO_RECEIVED_INTRODUCE1_SENDING_INTRODUCE2_AND_INTRODUCE_ACK);
 		registerEventTypePattern(
 				"Received an ESTABLISH_RENDEZVOUS request on circuit .*",
-				MiscEventType.RPO_RECEIVED_ESTABLISH_RENDEZVOUS_SENDING_RENDEZVOUS_ESTABLISHED);
+				HiddenServiceEventType.RPO_RECEIVED_ESTABLISH_RENDEZVOUS_SENDING_RENDEZVOUS_ESTABLISHED);
 		registerEventTypePattern(
 				"Got request for rendezvous from circuit .* to cookie .*",
-				MiscEventType.RPO_RECEIVING_RENDEZVOUS1_SENDING_RENDEZVOUS2);
-
+				HiddenServiceEventType.RPO_RECEIVING_RENDEZVOUS1_SENDING_RENDEZVOUS2);
 	}
 
 	/**
@@ -465,10 +467,8 @@
 	 * event history and propagates its occurrence to all registered event
 	 * handlers.
 	 * 
-	 * @param source
-	 *            The source of the given event.
 	 * @param event
-	 *            The event type.
+	 *            The observed event.
 	 */
 	private synchronized void observeEvent(Event event) {
 
@@ -494,7 +494,6 @@
 			// make a copy of the event handler set, because some event handlers
 			// might want to remove themselves from this set while handling the
 			// event
-
 			Set<EventListener> copyOfEventHandlers = new HashSet<EventListener>(
 					this.eventHandlers.get(source));
 
@@ -711,7 +710,7 @@
 					&& (timeLeft = endOfTime - System.currentTimeMillis()) > 0) {
 
 				this.logger.log(Level.FINEST, "We will wait for " + timeLeft
-						+ " millis for the next occurence of event type "
+						+ " milliseconds for the next occurence of event type "
 						+ type + " from source " + source + "...");
 
 				try {
@@ -744,12 +743,38 @@
 	}
 
 	/**
-	 * Adds the given <code>name</code> as possible event source.
+	 * Adds the given <code>source</code> as possible event source.
 	 * 
-	 * @param name
+	 * @param source
 	 *            The name of the node, client, or server to add.
+	 * @throws IllegalArgumentException
+	 *             Thrown if there is already an event source with this name.
 	 */
-	void addEventSource(String name) {
-		this.eventSources.add(name);
+	void addEventSource(String source) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "addEventSource",
+				source);
+
+		// check if source name is unique in this network
+		if (this.eventSources.contains(source)) {
+			IllegalArgumentException e = new IllegalArgumentException(
+					"There is already an event source with name " + source
+							+ " in this network!");
+			this.logger
+					.throwing(this.getClass().getName(), "addEventSource", e);
+			throw e;
+		}
+
+		// add event source name
+		this.eventSources.add(source);
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "addEventSource");
 	}
+
+	@Override
+	public String toString() {
+		return this.getClass().getSimpleName();
+	}
 }

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/HiddenServiceImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/HiddenServiceImpl.java	                        (rev 0)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/HiddenServiceImpl.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -0,0 +1,167 @@
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.uniba.wiai.lspi.puppetor.HiddenService;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
+
+/**
+ * Implementation of <code>HiddenService</code>.
+ * 
+ * @author kloesing
+ */
+public class HiddenServiceImpl implements HiddenService {
+
+	/**
+	 * Logger for this node which is called "hidserv." plus the name of this
+	 * hidden service.
+	 */
+	protected Logger logger;
+
+	/**
+	 * The node at which this hidden service is configured.
+	 */
+	private ProxyNodeImpl node;
+
+	/**
+	 * Name of the hidden service that will be used as name for the hidden
+	 * service directory.
+	 */
+	private String serviceName;
+
+	/**
+	 * The TCP port on which the service will be available for requests.
+	 */
+	private int servicePort;
+
+	/**
+	 * The virtual TCP port that this hidden service runs on as it is announced
+	 * to clients.
+	 */
+	private int virtualPort;
+
+	/**
+	 * Adds the entries for a hidden service to the configuration of this node.
+	 * 
+	 * Creates a new <code>HiddenServiceImpl</code>.
+	 * 
+	 * @param node
+	 *            The node at which this hidden service is configured.
+	 * @param serviceName
+	 *            Name of the hidden service that will be used as name for the
+	 *            hidden service directory. May neither be <code>null</code>
+	 *            or a zero-length string.
+	 * @param servicePort
+	 *            The TCP port on which the service will be available for
+	 *            requests. This can, but need not be different from the virtual
+	 *            port that is announced to clients. May not be negative or
+	 *            greater than 65535.
+	 * @param virtualPort
+	 *            The virtual TCP port that this hidden service runs on as it is
+	 *            announced to clients. May not be negative or greater than
+	 *            65535.
+	 * @throws IllegalArgumentException
+	 *             Thrown if an invalid value is given for either of the
+	 *             parameters.
+	 */
+	HiddenServiceImpl(ProxyNodeImpl node, String serviceName, int servicePort,
+			int virtualPort) {
+
+		// check if networkName can be used as logger name
+		if (serviceName == null || serviceName.length() == 0) {
+			throw new IllegalArgumentException("Invalid serviceName: "
+					+ serviceName);
+		}
+
+		// create logger
+		this.logger = Logger.getLogger("hidserv." + serviceName);
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "HiddenServiceImpl",
+				new Object[] { node, serviceName, servicePort, virtualPort });
+
+		// check parameters
+		if (serviceName == null || serviceName.length() == 0 || servicePort < 0
+				|| servicePort > 65535 || virtualPort < 0
+				|| virtualPort > 65535) {
+			this.logger.log(Level.SEVERE,
+					"Illegal argument when adding hidden service!");
+			IllegalArgumentException e = new IllegalArgumentException();
+			this.logger.throwing(this.getClass().getName(), "addHiddenService",
+					e);
+			throw e;
+		}
+
+		// store parameter values
+		this.node = node;
+		this.serviceName = serviceName;
+		this.servicePort = servicePort;
+		this.virtualPort = virtualPort;
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "HiddenServiceImpl");
+	}
+
+	public String determineOnionAddress() throws PuppeTorException {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "getOnionAddress");
+
+		// check if hidden service directory exists
+		File hiddenServiceFile = new File(this.node.getWorkingDir()
+				.getAbsolutePath()
+				+ File.separator
+				+ this.serviceName
+				+ File.separator
+				+ "hostname");
+		if (!hiddenServiceFile.exists()) {
+			this.logger.log(Level.SEVERE,
+					"Hidden service directory or hostname file does not exist: "
+							+ hiddenServiceFile.getAbsolutePath());
+
+			PuppeTorException e = new PuppeTorException(
+					"Hidden service directory or hostname file does not exist: "
+							+ hiddenServiceFile.getAbsolutePath());
+			this.logger.throwing(this.getClass().getName(), "getOnionAddress",
+					e);
+			throw e;
+		}
+
+		// read hostname from file
+		String address = null;
+		try {
+			BufferedReader br = new BufferedReader(new FileReader(
+					hiddenServiceFile));
+			address = br.readLine();
+			br.close();
+		} catch (IOException e) {
+			PuppeTorException ex = new PuppeTorException(
+					"Could not read hostname file!", e);
+			this.logger.throwing(this.getClass().getName(), "getOnionAddress",
+					ex);
+			throw ex;
+		}
+
+		// log exiting and return address
+		this.logger.exiting(this.getClass().getName(), "getOnionAddress",
+				address);
+		return address;
+	}
+
+	public String getServiceName() {
+		return serviceName;
+	}
+
+	public int getServicePort() {
+		return servicePort;
+	}
+
+	public int getVirtualPort() {
+		return virtualPort;
+	}
+}

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -34,7 +34,7 @@
 import java.io.File;
 import java.net.MalformedURLException;
 import java.rmi.Naming;
-import java.rmi.NotBoundException;
+import java.rmi.RMISecurityManager;
 import java.rmi.RemoteException;
 import java.rmi.server.UnicastRemoteObject;
 import java.util.ArrayList;
@@ -53,13 +53,12 @@
 import de.uniba.wiai.lspi.puppetor.EventListener;
 import de.uniba.wiai.lspi.puppetor.EventManager;
 import de.uniba.wiai.lspi.puppetor.Network;
-import de.uniba.wiai.lspi.puppetor.NetworkState;
 import de.uniba.wiai.lspi.puppetor.NodeEventType;
 import de.uniba.wiai.lspi.puppetor.NodeState;
 import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
 import de.uniba.wiai.lspi.puppetor.RouterNode;
 import de.uniba.wiai.lspi.puppetor.ServerApplication;
-import de.uniba.wiai.lspi.puppetor.TorProcessException;
 
 /**
  * Implementation of <code>Network</code>.
@@ -70,67 +69,6 @@
 public class NetworkImpl extends UnicastRemoteObject implements Network {
 
 	/**
-	 * Internal thread class that is used to determine fingerprints in parallel.
-	 */
-	private class FingerprintThread extends Thread {
-
-		/**
-		 * The exception, if one is caught while trying to determine the
-		 * fingerprint of the node.
-		 */
-		private TorProcessException caughtException = null;
-
-		/**
-		 * The node of which the fingerprint shall be determined.
-		 */
-		private RouterNode node = null;
-
-		/**
-		 * Creates a new thread to determine the fingerprint of
-		 * <code>node</code>.
-		 * 
-		 * @param node
-		 *            The node of which the fingerprint shall be determined.
-		 */
-		FingerprintThread(RouterNode node) {
-
-			// log entering
-			logger.entering(this.getClass().getName(), "FingerprintThread");
-
-			// remember node reference
-			this.node = node;
-
-			// log exiting
-			logger.exiting(this.getClass().getName(), "FingerprintThread");
-		}
-
-		@Override
-		public void run() {
-
-			// log entering
-			logger.entering(this.getClass().getName(), "run");
-
-			// determine fingerprint
-			try {
-				node.determineFingerprint();
-			} catch (TorProcessException e) {
-				logger.log(Level.SEVERE,
-						"Caught an exception while determining fingerprint for "
-								+ "node " + node.toString() + "!");
-				this.caughtException = e;
-			} catch (RemoteException e) {
-				logger.log(Level.SEVERE,
-						"Caught a remote exception while determining fingerprint for "
-								+ "node " + node.toString() + "!");
-				// TODO handle exception appropriately
-			}
-
-			// log exiting
-			logger.exiting(this.getClass().getName(), "run");
-		}
-	}
-
-	/**
 	 * Internal thread class that is used to start Tor processes in parallel.
 	 */
 	private class NodeStarter extends Thread {
@@ -138,7 +76,7 @@
 		/**
 		 * The exception, if one is caught while trying to start the node.
 		 */
-		TorProcessException caughtException;
+		Exception caughtException;
 
 		/**
 		 * The maximum time to wait for the Tor process to start in
@@ -191,15 +129,16 @@
 				// try to start node
 				this.success = this.node
 						.startNode(this.maximumTimeToWaitInMillis);
-			} catch (TorProcessException e) {
-				// if an exception is caught, store it, but don't throw it (the
-				// thread wouldn't care)
+			} catch (PuppeTorException e) {
+				logger.log(Level.SEVERE,
+						"Caught an exception while starting node "
+								+ node.toString() + "!");
 				this.caughtException = e;
 			} catch (RemoteException e) {
 				logger.log(Level.SEVERE,
-						"Caught a remote exception while starting " + "node "
+						"Caught a remote exception while starting node "
 								+ node.toString() + "!");
-				// TODO handle exception appropriately
+				this.caughtException = e;
 			}
 
 			// log exiting
@@ -208,24 +147,13 @@
 	}
 
 	/**
-	 * The fingerprints of all approved routers in the network configuration.
-	 */
-	private HashSet<String> approvedRoutersFingerprints;
-
-	/**
-	 * The fingerprints of all authoritative directories in the network
-	 * configuration.
-	 */
-	private Set<String> authorizedDirectoriesFingerprints;
-
-	/**
 	 * Event manager to which all events concerning this network are notified.
 	 */
 	private EventManagerImpl eventManager;
 
 	/**
-	 * Logger for this <code>NetworkImpl</code> instance which is called
-	 * "network." plus the name of this network.
+	 * Logger for this network which is called "network." plus the name of this
+	 * network.
 	 */
 	private Logger logger;
 
@@ -236,48 +164,11 @@
 	private String networkName;
 
 	/**
-	 * The state of this network.
+	 * All nodes contained in this network.
 	 */
-	private NetworkState networkState = NetworkState.CONFIGURING_NODES;
-
-	/**
-	 * All clients contained in this network. It is important that we store and
-	 * work only with interface types to assure that all operations could also
-	 * be performed by the application itself.
-	 */
-	private Map<String, ClientApplication> clients = new HashMap<String, ClientApplication>();
-
-	/**
-	 * All servers contained in this network. It is important that we store and
-	 * work only with interface types to assure that all operations could also
-	 * be performed by the application itself.
-	 */
-	private Map<String, ServerApplication> servers = new HashMap<String, ServerApplication>();
-
-	/**
-	 * All nodes contained in this network. It is important that we store and
-	 * work only with interface types to assure that all operations could also
-	 * be performed by the application itself.
-	 */
 	private Map<String, ProxyNode> nodes;
 
 	/**
-	 * Determines if the given <code>name</code> is unique in this network
-	 * (including the network name).
-	 * 
-	 * @param name
-	 *            Name to look up.
-	 * @return <code>true</code> if no entity was created with this name,
-	 *         <code>false</code> otherwise.
-	 */
-	private boolean isLocallyUnique(String name) {
-		return (!this.clients.containsKey(name)
-				&& !this.servers.containsKey(name)
-				&& !this.nodes.containsKey(name) && !(this.getName()
-				.equals(name)));
-	}
-
-	/**
 	 * Directory that contains status information of all nodes contained in this
 	 * network.
 	 */
@@ -387,20 +278,19 @@
 		this.logger.exiting(this.getClass().getName(), "NetworkImpl");
 	}
 
-	public boolean allNodesUp() throws RemoteException {
+	/**
+	 * Returns whether all nodes in this network are up, or not.
+	 * 
+	 * @return <code>true</code> if all nodes are up, <code>false</code> if
+	 *         at least one node is not up.
+	 * @throws RemoteException
+	 *             Thrown if an error occurs when accessed remotely.
+	 */
+	private boolean allNodesUp() throws RemoteException {
 
 		// log entering
 		this.logger.entering(this.getClass().getName(), "allNodesUp");
 
-		// nodes can only be up, when network is in state
-		// NetworkState.NODES_STARTED.
-		if (this.networkState != NetworkState.NODES_STARTED) {
-
-			// log exiting and return false
-			this.logger.exiting(this.getClass().getName(), "allNodesUp");
-			return false;
-		}
-
 		// fail on first node that is not up
 		for (ProxyNode node : this.nodes.values()) {
 			if (!eventManager.hasEventOccured(node.getNodeName(),
@@ -417,87 +307,48 @@
 		return true;
 	}
 
-	public void configureAsPrivateNetwork() throws TorProcessException,
+	public void configureAsPrivateNetwork() throws PuppeTorException,
 			RemoteException {
 
 		// log entering
 		this.logger.entering(this.getClass().getName(),
 				"configureAsPrivateNetwork");
 
-		// start threads to determine fingerprints for all directories and
-		// routers in parallel
-		Set<FingerprintThread> fingerprintThreads = new HashSet<FingerprintThread>();
-		for (ProxyNode node : nodes.values()) {
-			if (node instanceof RouterNode) {
-				RouterNode dirOrRouterNode = (RouterNode) node;
-				FingerprintThread fingerprintThread = new FingerprintThread(
-						dirOrRouterNode);
-				fingerprintThread.setName(node.getNodeName()
-						+ " Fingerprint Resolver");
-				fingerprintThreads.add(fingerprintThread);
-				fingerprintThread.start();
-			}
-		}
-
-		// wait for all fingerprints to be determined
-		for (FingerprintThread fingerprintThread : fingerprintThreads) {
-
-			// join fingerprint determination one after the other
-			try {
-				fingerprintThread.join();
-			} catch (InterruptedException e) {
-				// ignore; TODO really?!
-				logger.log(Level.WARNING,
-						"Joining fingerprint thread was interrupted.");
-			}
-
-			// if any thread has caught an exception, throw that exception now
-			if (fingerprintThread.caughtException != null) {
-				this.logger.throwing(this.getClass().getName(),
-						"configureAsPrivateNetwork",
-						fingerprintThread.caughtException);
-				throw fingerprintThread.caughtException;
-			}
-		}
-
-		// read DirServer strings for all directories from memory; they should
-		// have been read from disk before, so that this will perform really
-		// fast
-		this.authorizedDirectoriesFingerprints = new HashSet<String>();
+		// read DirServer strings for all directories
+		List<String> authorizedDirectoriesFingerprints = new ArrayList<String>();
 		for (ProxyNode node : this.nodes.values()) {
 			if (node instanceof DirectoryNode) {
 				DirectoryNode dirNode = (DirectoryNode) node;
-				this.authorizedDirectoriesFingerprints.add(dirNode
-						.determineDirServerString());
+				authorizedDirectoriesFingerprints.add(dirNode
+						.getDirServerString());
 			}
 		}
 
 		// configure nodes
 		for (ProxyNode node : this.nodes.values()) {
 			if (node.getNodeState() == NodeState.CONFIGURING) {
-				node
-						.configureDirServers(this.authorizedDirectoriesFingerprints);
+
+				// add to configuration
+				node.addConfigurations(authorizedDirectoriesFingerprints);
 			}
 		}
 
-		// read fingerprints for all directories and routers from memory; they
-		// should have been read from disk before, so that this will perform
-		// really fast
-		this.approvedRoutersFingerprints = new HashSet<String>();
+		// read fingerprints for all directories and routers
+		HashSet<String> approvedRoutersFingerprints = new HashSet<String>();
 		for (ProxyNode node : this.nodes.values()) {
 			if (node instanceof RouterNode) {
 				RouterNode routerOrDirNode = (RouterNode) node;
-				this.approvedRoutersFingerprints.add(routerOrDirNode
-						.determineFingerprint());
+				approvedRoutersFingerprints.add(routerOrDirNode
+						.getFingerprint());
 			}
 		}
 
-		// write fingerprints for all directories and routers to
+		// write fingerprints for all directories and routers to the
 		// approved-routers file
 		for (ProxyNode node : this.nodes.values()) {
 			if (node instanceof DirectoryNode) {
 				DirectoryNode dirNode = (DirectoryNode) node;
-				dirNode.writeApprovedRouters(this.approvedRoutersFingerprints);
+				dirNode.addApprovedRouters(approvedRoutersFingerprints);
 			}
 		}
 
@@ -514,22 +365,10 @@
 				new Object[] { clientApplicationName, targetAddress,
 						targetPort, socksPort });
 
-		// check if client name is locally unique
-		if (!this.isLocallyUnique(clientApplicationName)) {
-			IllegalArgumentException e = new IllegalArgumentException(
-					"There is already an entity with name "
-							+ clientApplicationName + " in this network!");
-			this.logger.throwing(this.getClass().getName(), "createClient", e);
-			throw e;
-		}
-
 		// create client; parameter checking is done in constructor
 		ClientApplicationImpl client = new ClientApplicationImpl(this,
 				clientApplicationName, targetAddress, targetPort, socksPort);
 
-		// add new client to clients collection
-		this.clients.put(clientApplicationName, client);
-
 		// add name to event manager as event source
 		this.eventManager.addEventSource(clientApplicationName);
 
@@ -547,24 +386,6 @@
 				new Object[] { nodeName, controlPort, socksPort, orPort,
 						dirPort, serverIpAddress });
 
-		// check state
-		if (this.networkState != NetworkState.CONFIGURING_NODES) {
-			IllegalStateException e = new IllegalStateException();
-			this.logger.throwing(this.getClass().getName(), "createDirectory",
-					e);
-			throw e;
-		}
-
-		// check if node name is locally unique
-		if (!this.isLocallyUnique(nodeName)) {
-			IllegalArgumentException e = new IllegalArgumentException(
-					"There is already an entity with name " + nodeName
-							+ " in this network!");
-			this.logger.throwing(this.getClass().getName(), "createDirectory",
-					e);
-			throw e;
-		}
-
 		// create directory node; parameter checking is done in constructor
 		DirectoryNode dir = new DirectoryNodeImpl(this, nodeName, controlPort,
 				socksPort, orPort, dirPort, serverIpAddress);
@@ -635,22 +456,13 @@
 		return dir;
 	}
 
-	public synchronized ProxyNode createProxy(String nodeName, int controlPort,
-			int socksPort) throws RemoteException {
+	public ProxyNode createProxy(String nodeName, int controlPort, int socksPort)
+			throws RemoteException {
 
 		// log entering
 		this.logger.entering(this.getClass().getName(), "createProxy",
 				new Object[] { nodeName, controlPort, socksPort });
 
-		// check if node name is locally unique
-		if (!this.isLocallyUnique(nodeName)) {
-			IllegalArgumentException e = new IllegalArgumentException(
-					"There is already an entity with name " + nodeName
-							+ " in this network!");
-			this.logger.throwing(this.getClass().getName(), "createProxy", e);
-			throw e;
-		}
-
 		// create proxy node; parameter checking is done in constructor
 		ProxyNode proxy = new ProxyNodeImpl(this, nodeName, controlPort,
 				socksPort);
@@ -694,14 +506,6 @@
 				new Object[] { nodeName, controlPort, socksPort, orPort,
 						dirPort, serverIpAddress });
 
-		// check if node name is locally unique
-		if (!this.isLocallyUnique(nodeName)) {
-			IllegalArgumentException e = new IllegalArgumentException(
-					"There is already an entity with name " + nodeName
-							+ " in this network!");
-			this.logger.throwing(this.getClass().getName(), "createRouter", e);
-			throw e;
-		}
 		// create router node; parameter checking is done in constructor
 		RouterNode router = new RouterNodeImpl(this, nodeName, controlPort,
 				socksPort, orPort, dirPort, serverIpAddress);
@@ -778,22 +582,10 @@
 		this.logger.entering(this.getClass().getName(), "createServer",
 				new Object[] { serverApplicationName, serverPort });
 
-		// check if server name is locally unique
-		if (!this.isLocallyUnique(serverApplicationName)) {
-			IllegalArgumentException e = new IllegalArgumentException(
-					"There is already an entity with name "
-							+ serverApplicationName + " in this network!");
-			this.logger.throwing(this.getClass().getName(), "createServer", e);
-			throw e;
-		}
-
 		// create server; parameter checking is done in constructor
 		ServerApplicationImpl server = new ServerApplicationImpl(this,
 				serverApplicationName, serverPort);
 
-		// add new server to servers collection
-		this.servers.put(serverApplicationName, server);
-
 		// add name to event manager as event source
 		this.eventManager.addEventSource(serverApplicationName);
 
@@ -830,28 +622,14 @@
 		return this.eventManager;
 	}
 
-	public NetworkState getNetworkState() {
-		return this.networkState;
-	}
-
 	public File getWorkingDirectory() {
 		return this.workingDir;
 	}
 
-	public ProxyNode getProxyNode(String nodeName) {
+	public ProxyNode getNode(String nodeName) {
 		return this.nodes.get(nodeName);
 	}
 
-	public RouterNode getRouterNode(String nodeName) {
-		ProxyNode node = this.nodes.get(nodeName);
-		return (node instanceof RouterNode ? (RouterNode) node : null);
-	}
-
-	public DirectoryNode getDirectoryNode(String nodeName) {
-		ProxyNode node = this.nodes.get(nodeName);
-		return (node instanceof DirectoryNode ? (DirectoryNode) node : null);
-	}
-
 	public Map<String, ProxyNode> getAllProxyNodes() {
 		Map<String, ProxyNode> result = new HashMap<String, ProxyNode>();
 		for (String nodeName : this.nodes.keySet()) {
@@ -885,18 +663,27 @@
 		return result;
 	}
 
+	public Map<String, ProxyNode> getAllNodes() {
+		return new HashMap<String, ProxyNode>(nodes);
+	}
+
 	public boolean hupUntilUp(int tries, long hupInterval)
-			throws TorProcessException, RemoteException {
+			throws PuppeTorException, RemoteException {
 
 		// log entering
 		this.logger.entering(this.getClass().getName(), "hupUntilUp",
 				new Object[] { tries, hupInterval });
 
-		// check state
-		if (this.networkState != NetworkState.NODES_STARTED) {
-			IllegalStateException e = new IllegalStateException();
-			this.logger.throwing(this.getClass().getName(), "hupUntilUp", e);
-			throw e;
+		// check if all nodes are running
+		for (ProxyNode node : this.nodes.values()) {
+			if (node.getNodeState() != NodeState.RUNNING) {
+				IllegalStateException e = new IllegalStateException(
+						"All nodes must be running before sending them HUP "
+								+ "commands!");
+				this.logger
+						.throwing(this.getClass().getName(), "hupUntilUp", e);
+				throw e;
+			}
 		}
 
 		// check if nodes are already up; if so, return immediately
@@ -966,27 +753,49 @@
 		return false;
 	}
 
-	public void shutdownNodes() throws TorProcessException, RemoteException {
+	public void hupAllNodes() throws PuppeTorException, RemoteException {
 
 		// log entering
-		this.logger.entering(this.getClass().getName(), "shutdownNodes");
+		this.logger.entering(this.getClass().getName(), "hupAllNodes");
 
-		// check state
-		if (this.networkState != NetworkState.NODES_STARTED) {
-			IllegalStateException e = new IllegalStateException();
-			this.logger.throwing(this.getClass().getName(), "shutdownNodes", e);
-			throw e;
+		// check if all nodes are running
+		for (ProxyNode node : this.nodes.values()) {
+			if (node.getNodeState() != NodeState.RUNNING) {
+				IllegalStateException e = new IllegalStateException(
+						"All nodes must be running before sending them HUP "
+								+ "commands!");
+				this.logger
+						.throwing(this.getClass().getName(), "hupUntilUp", e);
+				throw e;
+			}
 		}
 
+		// send a HUP signal to all nodes
+		for (ProxyNode node : this.nodes.values()) {
+			this.logger.log(Level.FINE, "Sending HUP to node "
+					+ node.toString());
+			node.hup();
+		}
+
+		// no retries left and not all nodes are up; log exiting and return
+		// failure
+		this.logger.exiting(this.getClass().getName(), "hupAllNodes");
+	}
+
+	public void shutdownNodes() throws PuppeTorException, RemoteException {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "shutdownNodes");
+
 		// iteratively shut down all running nodes; if an exception is caught,
 		// continue shutting down the other nodes and throw the first exception
 		// subsequently
-		TorProcessException firstCaughtException = null;
+		PuppeTorException firstCaughtException = null;
 		for (ProxyNode node : this.nodes.values()) {
 			if (node.getNodeState() == NodeState.RUNNING) {
 				try {
 					node.shutdown();
-				} catch (TorProcessException e) {
+				} catch (PuppeTorException e) {
 					if (firstCaughtException != null) {
 						firstCaughtException = e;
 					}
@@ -994,9 +803,6 @@
 			}
 		}
 
-		// change network state
-		this.networkState = NetworkState.NODES_SHUT_DOWN;
-
 		// if an exception was caught during shutting down nodes, throw the
 		// first caught exception
 		if (firstCaughtException != null) {
@@ -1010,17 +816,22 @@
 	}
 
 	public boolean startNodes(long maximumTimeToWaitInMillis)
-			throws TorProcessException {
+			throws PuppeTorException, RemoteException {
 
 		// log entering
 		this.logger.entering(this.getClass().getName(), "startNodes",
 				maximumTimeToWaitInMillis);
 
-		// check state
-		if (this.networkState != NetworkState.CONFIGURATIONS_WRITTEN) {
-			IllegalStateException e = new IllegalStateException();
-			this.logger.throwing(this.getClass().getName(), "startNodes", e);
-			throw e;
+		// check node states
+		for (ProxyNode node : this.nodes.values()) {
+			if (node.getNodeState() != NodeState.CONFIGURATION_WRITTEN) {
+				IllegalStateException e = new IllegalStateException(
+						"All configurations must be written before starting "
+								+ "nodes!");
+				this.logger
+						.throwing(this.getClass().getName(), "startNodes", e);
+				throw e;
+			}
 		}
 
 		// check parameter
@@ -1059,11 +870,16 @@
 				return false;
 			}
 
-			// if node start threw an exception, throw that exception now
-			if (nodeStarter.caughtException != null) {
+			// if any thread has caught an exception, throw that exception now
+			Exception caughtException = nodeStarter.caughtException;
+			if (caughtException != null) {
+				PuppeTorException ex = new PuppeTorException(
+						"Exception while starting node "
+								+ nodeStarter.node.getNodeName(),
+						caughtException);
 				this.logger.throwing(this.getClass().getName(), "startNodes",
-						nodeStarter.caughtException);
-				throw nodeStarter.caughtException;
+						ex);
+				throw ex;
 			}
 
 			// if node start did not succeed in the given time, fail
@@ -1085,9 +901,6 @@
 		this.logger.log(Level.FINE, "Starting nodes was successful and took "
 				+ ((after - before) / 1000) + " seconds.", this.networkName);
 
-		// change state
-		this.networkState = NetworkState.NODES_STARTED;
-
 		// log exiting and return true
 		this.logger.exiting(this.getClass().getName(), "startNodes", true);
 		return true;
@@ -1096,14 +909,15 @@
 	@Override
 	public String toString() {
 		return this.getClass().getSimpleName() + ": networkName=\""
-				+ this.networkName + "\", networkState="
-				+ this.networkState.toString() + ", nodes.size()="
-				+ this.nodes.size();
+				+ this.networkName;
 	}
 
-	public void writeConfigurations() throws TorProcessException,
-			RemoteException {
+	public String getNetworkName() {
+		return this.networkName;
+	}
 
+	public void writeConfigurations() throws PuppeTorException, RemoteException {
+
 		// log entering
 		this.logger.entering(this.getClass().getName(), "writeConfigurations");
 
@@ -1112,297 +926,77 @@
 			node.writeConfiguration();
 		}
 
-		// change state if necessary
-		if (this.networkState == NetworkState.CONFIGURING_NODES) {
-			this.networkState = NetworkState.CONFIGURATIONS_WRITTEN;
-		}
-
 		// log exiting
 		this.logger.exiting(this.getClass().getName(), "writeConfigurations");
 	}
 
-	public String getName() {
-		return this.networkName;
-	}
+	public void configureAsInterconnectedPrivateNetwork(Network remoteNetwork)
+			throws PuppeTorException, RemoteException {
 
-	public void addApprovedRouters(Set<String> approvedRoutersString)
-			throws RemoteException, TorProcessException {
-
 		// log entering
-		this.logger.entering(this.getClass().getName(), "addApprovedRouters",
-				approvedRoutersString);
+		logger.entering(this.getClass().getName(), "mergeNetworks",
+				remoteNetwork);
 
-		for (ProxyNode tempNode : nodes.values()) {
-			if (tempNode instanceof DirectoryNode) {
-				DirectoryNode tempDirNode = (DirectoryNode) tempNode;
-				tempDirNode.addApprovedRouters(approvedRoutersString);
-			}
+		// collect dir strings from this and the remote network
+		List<String> ourDirServerStrings = new ArrayList<String>();
+		for (DirectoryNode directory : this.getAllDirectoryNodes().values()) {
+			ourDirServerStrings.add(directory.getDirServerString());
 		}
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "addApprovedRouters");
-	}
-
-	public void addDirectoryStrings(List<String> dirServerStrings)
-			throws RemoteException, TorProcessException {
-
-		// log entering
-		this.logger.entering(this.getClass().getName(), "addDirectoryStrings",
-				dirServerStrings);
-
-		// add configuration strings to all nodes
-		for (ProxyNode node : this.nodes.values()) {
-			node.addConfigurations(dirServerStrings);
+		List<String> remoteDirServerStrings = new ArrayList<String>();
+		for (DirectoryNode directory : remoteNetwork.getAllDirectoryNodes()
+				.values()) {
+			remoteDirServerStrings.add(directory.getDirServerString());
 		}
 
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "addDirectoryStrings");
-	}
-
-	/* ***** TODO The following code is still unconfirmed!! ***** */
-
-	public void addEventListener(EventListener listener) throws RemoteException {
-
-		// log entering
-		logger.entering(this.getClass().getName(), "addEventListener");
-
-		// register the listener
-		this.eventManager.addEventListener(listener);
-
-		// log exiting
-		logger.exiting(this.getClass().getName(), "addEventListener");
-	}
-
-	public Network connectNetwork(String remoteHost, String bindingName)
-			throws MalformedURLException, RemoteException, NotBoundException {
-
-		// log entering
-		logger.entering(this.getClass().getName(), "connectNetwork");
-
-		// network lookup
-		Network network = (Network) Naming.lookup("rmi://" + remoteHost + "/"
-				+ bindingName);
-
-		// log exiting
-		logger.exiting(this.getClass().getName(), "connectNetwork");
-		return network;
-	}
-
-	public void mergeNetworks(Network remoteNetwork) throws RemoteException {
-
-		// log entering
-		logger.entering(this.getClass().getName(), "mergeNetworks");
-		try {
-			// lookup to recognize which network is without directories
-			if (this.getAllDirectoryNodes().size() > 0) {
-				if (remoteNetwork.getAllDirectoryNodes().size() > 0) {
-					configureAsWithDir(this, remoteNetwork);
-					// log exiting
-					logger.exiting(this.getClass().getName(), "mergeNetworks");
-				} else {
-					configureAsWithoutDir(this, remoteNetwork);
-					// log exiting
-					logger.exiting(this.getClass().getName(), "mergeNetworks");
-				}
-			} else {
-				configureAsWithoutDir(remoteNetwork, this);
-				// log exiting
-				logger.exiting(this.getClass().getName(), "mergeNetworks");
-			}
-		} catch (RemoteException e) {
-			logger.log(Level.SEVERE,
-					"Caught a remote exception while merging networks!");
+		// add dir strings of local directories to all nodes of the other
+		// network and vice versa
+		for (ProxyNode node : remoteNetwork.getAllNodes().values()) {
+			node.addConfigurations(ourDirServerStrings);
 		}
-	}
-
-	private void configureAsWithoutDir(Network first, Network second) {
-
-		// log entering
-		logger.entering(this.getClass().getName(), "configureAsWithoutDir");
-
-		try {
-			// adds the directory strings to the network without
-			exchangeDirectories(first, second);
-
-			// writes configuration to the network
-			second.writeConfigurations();
-
-			// starts the router in the without network
-			startRouters(second);
-
-			// hups the router in the without network
-			hupRouters(second);
-			// adds the new router fingerprints to the directories
-			exchangeFingerprints(second, first);
-
-			// hups the directories
-			hupDirectories(first);
-
-			// log exiting
-			logger.exiting(this.getClass().getName(), "configureAsWithoutDir");
-
-		} catch (RemoteException e) {
-			logger.log(Level.SEVERE,
-					"Caught a remote exception while configureAsWithoutDir!");
-		} catch (TorProcessException e) {
-			logger
-					.log(Level.SEVERE,
-							"Caught a tor process exception while configureAsWithoutDir!");
+		for (ProxyNode node : this.getAllNodes().values()) {
+			node.addConfigurations(remoteDirServerStrings);
 		}
-	}
 
-	private void configureAsWithDir(Network first, Network second) {
-
-		// log entering
-		logger.entering(this.getClass().getName(), "configureAsWithDir");
-
-		try {
-			// adds the directory strings to the other network
-			exchangeDirectories(first, second);
-			exchangeDirectories(second, first);
-
-			// adds the new router fingerprints to the directories
-			exchangeFingerprints(first, second);
-			exchangeFingerprints(second, first);
-
-			// writes configuration to files
-			first.writeConfigurations();
-			second.writeConfigurations();
-
-			// hups the directories
-			hupDirectories(first);
-			hupDirectories(second);
-
-			// hups the routers
-			hupRouters(first);
-			hupRouters(second);
-
-			// log exiting
-			logger.exiting(this.getClass().getName(), "configureAsWithDir");
-
-		} catch (RemoteException e) {
-			logger.log(Level.SEVERE,
-					"Caught a remote exception while configureAsWithDir!");
-		} catch (TorProcessException e) {
-			logger.log(Level.SEVERE,
-					"Caught a tor process exception while configureAsWithDir!");
+		// collect router fingerprints from all routers in this and the remote
+		// network
+		Set<String> ourApprovedRoutersStrings = new TreeSet<String>();
+		for (RouterNode router : this.getAllRouterNodes().values()) {
+			ourApprovedRoutersStrings.add(router.getFingerprint());
 		}
-	}
-
-	private void exchangeDirectories(Network first, Network second)
-			throws RemoteException, TorProcessException {
-
-		// log entering
-		logger.entering(this.getClass().getName(), "exchangeDirectories");
-
-		// collects all directories from network
-		HashMap<String, DirectoryNode> directories = (HashMap<String, DirectoryNode>) first
-				.getAllDirectoryNodes();
-		List<String> dirServerStrings = new ArrayList<String>();
-
-		// adds the directory strings to the collection
-		for (DirectoryNode directory : directories.values()) {
-			dirServerStrings.add(directory.determineDirServerString());
+		Set<String> remoteApprovedRoutersStrings = new TreeSet<String>();
+		for (RouterNode router : remoteNetwork.getAllRouterNodes().values()) {
+			remoteApprovedRoutersStrings.add(router.getFingerprint());
 		}
 
-		// adds the directory strings to the routers
-		second.addDirectoryStrings(dirServerStrings);
-
-		// log exiting
-		logger.exiting(this.getClass().getName(), "exchangeDirectories");
-	}
-
-	private void exchangeFingerprints(Network first, Network second)
-			throws RemoteException, TorProcessException {
-
-		// log entering
-		logger.entering(this.getClass().getName(), "exchangeFingerprints");
-
-		// collects all routers from network
-		HashMap<String, RouterNode> routers = (HashMap<String, RouterNode>) first
-				.getAllRouterNodes();
-		Set<String> approvedRoutersStrings = new TreeSet<String>();
-
-		// adds the fingerprint strings to the collection
-		for (ProxyNode router : routers.values()) {
-			if (router instanceof RouterNode) {
-				RouterNode localRouter = (RouterNode) router;
-				approvedRoutersStrings.add(localRouter.getNodeName() + " "
-						+ localRouter.getFingerprint());
-			}
+		// add the fingerprints of local routers and directories to the
+		// directories of the other network and vice versa
+		for (DirectoryNode node : remoteNetwork.getAllDirectoryNodes().values()) {
+			node.addApprovedRouters(ourApprovedRoutersStrings);
 		}
-
-		// adds the fingerprint strings to the directories
-		second.addApprovedRouters(approvedRoutersStrings);
-
-		// log exiting
-		logger.exiting(this.getClass().getName(), "exchangeFingerprints");
-	}
-
-	private void startRouters(Network network) throws RemoteException,
-			TorProcessException {
-
-		// log entering
-		logger.entering(this.getClass().getName(), "startRouters");
-
-		// collects all routers from network
-		HashMap<String, RouterNode> routers = (HashMap<String, RouterNode>) network
-				.getAllRouterNodes();
-
-		// starts all router
-		for (RouterNode router : routers.values()) {
-			router.startNode(5000);
+		for (DirectoryNode node : this.getAllDirectoryNodes().values()) {
+			node.addApprovedRouters(remoteApprovedRoutersStrings);
 		}
 
 		// log exiting
-		logger.exiting(this.getClass().getName(), "startRouters");
+		logger.exiting(this.getClass().getName(), "mergeNetworks");
 	}
 
-	private void hupDirectories(Network network) throws RemoteException,
-			TorProcessException {
+	public void addTemplateConfiguration(Class<? extends ProxyNode> nodeClass,
+			String templateConfigurationString) {
 
 		// log entering
-		logger.entering(this.getClass().getName(), "hupDirectories");
+		logger.entering(this.getClass().getName(), "addTemplateConfiguration",
+				new Object[] { nodeClass, templateConfigurationString });
 
-		// collects all directories from network
-		HashMap<String, DirectoryNode> directories = (HashMap<String, DirectoryNode>) network
-				.getAllDirectoryNodes();
-
-		// hups all directories
-		for (DirectoryNode directory : directories.values()) {
-			directory.hup();
-		}
-
-		// log exiting
-		logger.exiting(this.getClass().getName(), "hupDirectories");
-	}
-
-	private void hupRouters(Network network) throws RemoteException,
-			TorProcessException {
-
-		// log entering
-		logger.entering(this.getClass().getName(), "hupDirectories");
-
-		// collects all routers from network
-		HashMap<String, RouterNode> routers = (HashMap<String, RouterNode>) network
-				.getAllRouterNodes();
-
-		// hups all router
-		for (RouterNode router : routers.values()) {
-			router.hup();
-		}
-
-		// log exiting
-		logger.exiting(this.getClass().getName(), "hupRouters");
-	}
-
-	public void addTemplateConfiguration(Class<? extends ProxyNode> nodeClass,
-			String templateConfigurationString) {
+		// add template string to local map
 		if (!this.templateConfigurations.containsKey(nodeClass)) {
 			this.templateConfigurations.put(nodeClass, new ArrayList<String>());
 		}
 		this.templateConfigurations.get(nodeClass).add(
 				templateConfigurationString);
+
+		// log exiting
+		logger.exiting(this.getClass().getName(), "addTemplateConfiguration");
 	}
 
 	/**
@@ -1423,4 +1017,37 @@
 			}
 		}
 	}
+
+	public boolean bindAtRmiregistry() throws RemoteException {
+
+		// log entering
+		logger.entering(this.getClass().getName(), "bindAtRmiregistry");
+
+		// set the RMISecurityManager
+		System.setSecurityManager(new RMISecurityManager());
+
+		// bind the network to the rmiregistry
+		try {
+			Naming.rebind("//127.0.0.1/" + networkName, this);
+		} catch (MalformedURLException e) {
+			this.logger.log(Level.WARNING,
+					"URL to bind this network to is malformed!", e);
+			logger.exiting(this.getClass().getName(), "bindAtRmiregistry",
+					false);
+			return false;
+		}
+
+		// log exiting
+		logger.exiting(this.getClass().getName(), "bindAtRmiregistry", true);
+		return true;
+	}
+
+	/**
+	 * Returns the current port number and increments it afterwards.
+	 * 
+	 * @return The current port number.
+	 */
+	int getNextPortNumber() {
+		return this.portCounter++;
+	}
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -34,7 +34,6 @@
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
@@ -44,15 +43,15 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import net.freehaven.tor.control.TorControlConnection;
+import de.uniba.wiai.lspi.puppetor.HiddenService;
 import de.uniba.wiai.lspi.puppetor.NodeEventType;
 import de.uniba.wiai.lspi.puppetor.NodeState;
 import de.uniba.wiai.lspi.puppetor.ProxyNode;
-import de.uniba.wiai.lspi.puppetor.TorProcessException;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
 
 /**
  * Implementation of <code>ProxyNode</code>.
@@ -75,7 +74,7 @@
 	protected File configFile;
 
 	/**
-	 * Collects all configuration strings for this node during configuration
+	 * Collects all configuration strings for this node during the configuration
 	 * phase in the order they are added.
 	 */
 	protected List<String> configuration;
@@ -96,8 +95,7 @@
 	private EventManagerImpl eventManager;
 
 	/**
-	 * Logger for this <code>ProxyNodeImpl</code> instance which is called
-	 * "node." plus the name of this node.
+	 * Logger for this node which is called "node." plus the name of this node.
 	 */
 	protected Logger logger;
 
@@ -107,8 +105,8 @@
 	protected NetworkImpl network;
 
 	/**
-	 * Name of this node that is used as part of the working directory and as
-	 * logger name of this node.
+	 * Name of this node that is used as part of the working directory, as
+	 * logger name of this node, and as event source.
 	 */
 	protected String nodeName;
 
@@ -124,7 +122,7 @@
 	protected int socksPort;
 
 	/**
-	 * Handle on the running Tor process that belongs to this node.
+	 * The running Tor process that belongs to this node.
 	 */
 	protected Process torProcess;
 
@@ -134,6 +132,15 @@
 	protected File workingDir;
 
 	/**
+	 * Returns this node's working directory.
+	 * 
+	 * @return This node's working directory.
+	 */
+	File getWorkingDir() {
+		return this.workingDir;
+	}
+
+	/**
 	 * Creates a new <code>ProxyNodeImpl</code> and adds it to the given
 	 * <code>network</code>, but does not yet write its configuration to disk
 	 * or start the corresponding Tor process.
@@ -222,9 +229,10 @@
 
 		// allow two nodes on the same circuit to be in the same /16 net
 		// TODO this depends in private or public network setting!!!
-		// TODO even more important: this only works since Tor 0.1.2.x!!!
 		this.configuration.add("EnforceDistinctSubnets 0");
 
+		this.configuration.add("ClientDNSRejectInternalAddresses 0");
+
 		// don't rely on node verification, yet... TODO change?
 		this.configuration
 				.add("AllowInvalidNodes middle,rendezvous,exit,entry,introduction");
@@ -238,7 +246,6 @@
 
 		// log exiting
 		this.logger.exiting(this.getClass().getName(), "ProxyNodeImpl");
-
 	}
 
 	public void addConfiguration(String configurationString) {
@@ -292,14 +299,6 @@
 		this.logger.entering(this.getClass().getName(), "replaceConfiguration",
 				configurationString);
 
-		// check state
-		if (this.nodeState != NodeState.CONFIGURING) {
-			IllegalStateException e = new IllegalStateException();
-			this.logger.throwing(this.getClass().getName(),
-					"replaceConfiguration", e);
-			throw e;
-		}
-
 		// check parameter
 		if (configurationString == null || configurationString.length() < 1
 				|| !configurationString.contains(" ")) {
@@ -313,7 +312,7 @@
 		String configurationKey = configurationString.substring(0,
 				configurationString.indexOf(" "));
 
-		// iterate over existing configuration strings and replace first
+		// iterate over existing configuration strings and replace the first
 		// occurrence of configuration key with new configuration string
 		Iterator<String> it = this.configuration.listIterator();
 		boolean replaced = false;
@@ -337,180 +336,90 @@
 		this.logger.exiting(this.getClass().getName(), "replaceConfiguration");
 	}
 
-	public synchronized void addHiddenService(String serviceName,
-			int servicePort, int virtualPort) {
+	public void deleteConfiguration(String configurationKey)
+			throws RemoteException {
 
 		// log entering
-		this.logger.entering(this.getClass().getName(), "addHiddenService",
-				new Object[] { serviceName, servicePort, virtualPort });
+		this.logger.entering(this.getClass().getName(), "deleteConfiguration",
+				configurationKey);
 
-		// check state
-		if (this.nodeState != NodeState.CONFIGURING) {
-			this.logger.log(Level.SEVERE,
-					"Hidden service can only be added when node is in state "
-							+ "CONFIGURING!");
-			IllegalStateException e = new IllegalStateException(
-					"Hidden service can only be added when node is in state "
-							+ "CONFIGURING!");
-			this.logger.throwing(this.getClass().getName(), "addHiddenService",
-					e);
+		// check parameter
+		if (configurationKey == null || configurationKey.length() < 1) {
+			IllegalArgumentException e = new IllegalArgumentException();
+			this.logger.throwing(this.getClass().getName(),
+					"deleteConfiguration", e);
 			throw e;
 		}
 
-		// check parameters
-		if (serviceName == null || serviceName.length() == 0 || servicePort < 0
-				|| servicePort > 65535 || virtualPort < 0
-				|| virtualPort > 65535) {
-			this.logger.log(Level.SEVERE,
-					"Illegal argument when adding hidden service!");
-			IllegalArgumentException e = new IllegalArgumentException();
-			this.logger.throwing(this.getClass().getName(), "addHiddenService",
-					e);
-			throw e;
+		// iterate over existing configuration strings and remove all
+		// configuration strings that have the given configuration key
+		for (String currentConfigurationString : this.configuration) {
+			String currentConfigurationKey = currentConfigurationString
+					.substring(0, currentConfigurationString.indexOf(" "));
+			if (currentConfigurationKey.equals(configurationKey)) {
+				this.configuration.remove(currentConfigurationString);
+			}
 		}
 
-		// add hidden service using Tor controller
-		this.configuration.add("HiddenServiceDir "
-				+ workingDir.getAbsolutePath() + File.separator + serviceName
-				+ "\nHiddenServicePort " + virtualPort + " 127.0.0.1:"
-				+ servicePort);
-
 		// log exiting
-		this.logger.exiting(this.getClass().getName(), "addHiddenService");
+		this.logger.exiting(this.getClass().getName(), "deleteConfiguration");
 	}
 
-	/**
-	 * Adds a hidden service configuration to this node while the node is
-	 * running.
-	 * 
-	 * TODO not fully implemented yet, but this should be possible both, during
-	 * configuration and when running a network...
-	 * 
-	 * @param serviceName
-	 *            Name of the hidden service that will be used as name for the
-	 *            hidden service directory. May neither be <code>null</code>
-	 *            or a zero-length string.
-	 * @param servicePort
-	 *            The TCP port on which the service will be available for
-	 *            requests. This can, but need not be different from the virtual
-	 *            port that is announced to clients. May not be negative or
-	 *            greater than 65535.
-	 * @param virtualPort
-	 *            The virtual TCP port that this hidden service runs on as it is
-	 *            announced to clients. May not be negative or greater than
-	 *            65535.
-	 * @return The onion address string consisting of 16 base32 chars plus
-	 *         ".onion" for hidden service versions 0 and 1 or 16 base32 chars
-	 *         plus "." plus 24 base32 chars plus ".onion" for hidden service
-	 *         version 2.
-	 */
-	synchronized String addHiddenServiceUsingController(String serviceName,
+	public synchronized HiddenService addHiddenService(String serviceName,
 			int servicePort, int virtualPort) {
 
-		// TODO this method is not supported yet!
-		if (1 != 2) {
-			UnsupportedOperationException e = new UnsupportedOperationException();
-			this.logger.throwing(this.getClass().getName(),
-					"addHiddenServiceUsingController", e);
-			throw e;
-		}
-
 		// log entering
-		this.logger.entering(this.getClass().getName(),
-				"addHiddenServiceUsingController", new Object[] { serviceName,
-						servicePort, virtualPort });
+		this.logger.entering(this.getClass().getName(), "addHiddenService",
+				new Object[] { serviceName, servicePort, virtualPort });
 
-		// TODO check state
+		// create hidden service object; parameter checking is done in
+		// constructor
+		HiddenService result = new HiddenServiceImpl(this, serviceName,
+				servicePort, virtualPort);
 
-		// check parameters
-		if (serviceName == null || serviceName.length() == 0 || servicePort < 0
-				|| servicePort > 65535 || virtualPort < 0
-				|| virtualPort > 65535) {
-			IllegalArgumentException e = new IllegalArgumentException();
-			this.logger.throwing(this.getClass().getName(),
-					"addHiddenServiceUsingController", e);
-			throw e;
-		}
+		// add hidden service using Tor controller
+		this.configuration.add("HiddenServiceDir "
+				+ workingDir.getAbsolutePath() + File.separator + serviceName
+				+ "\nHiddenServicePort " + virtualPort + " 127.0.0.1:"
+				+ servicePort);
 
-		// String serverName = server.getServerName();
-		// int serverPort = server.getServerPort();
+		// log exiting and return hidden service object
+		this.logger.exiting(this.getClass().getName(), "addHiddenService",
+				result);
+		return result;
+	}
 
-		// add hidden service using Tor controller
-		this.logger.log(Level.FINE,
-				"Adding hidden service using Tor controller.");
+	public synchronized HiddenService addHiddenService(String serviceName,
+			int servicePort) {
 
-		List<String> configs = new ArrayList<String>();
-		configs.add("HiddenServiceDir " + workingDir.getAbsolutePath() + "/"
-				+ serviceName);
-		configs.add("HiddenServicePort " + virtualPort + " 127.0.0.1:"
-				+ servicePort);
-		try {
-			conn.setConf(configs);
-		} catch (IOException e) {
-			RuntimeException ex = new RuntimeException(
-					"IOException when trying to register a new "
-							+ "hidden service", e);
-			this.logger.throwing(this.getClass().getName(),
-					"addHiddenServiceUsingController", ex);
-			throw ex;
-		}
-		this.logger.log(Level.FINE,
-				"Hidden service successfully registered at Tor proxy.");
+		// log entering
+		this.logger.entering(this.getClass().getName(), "addHiddenService",
+				new Object[] { serviceName, servicePort });
 
-		// 
-		File hiddenServiceFile = new File(workingDir.getAbsolutePath() + "\\"
-				+ serviceName + "\\hostname");
-		if (!hiddenServiceFile.exists()) {
-			RuntimeException ex = new RuntimeException();
-			this.logger.throwing(this.getClass().getName(),
-					"addHiddenServiceUsingController", ex);
-			throw ex;
-		}
-		// read hostname from file
-		String address = null;
-		try {
-			BufferedReader br = new BufferedReader(new FileReader(
-					hiddenServiceFile));
-			address = br.readLine();
-			br.close();
-		} catch (IOException e) {
-			RuntimeException ex = new RuntimeException(e);
-			this.logger.throwing(this.getClass().getName(),
-					"addHiddenServiceUsingController", ex);
-			throw ex;
-		}
+		// invoke overloaded method
+		HiddenService result = this.addHiddenService(serviceName, servicePort,
+				80);
 
-		return address;
+		// log exiting and return hidden service
+		this.logger.exiting(this.getClass().getName(), "addHiddenService",
+				result);
+		return result;
 	}
 
-	public synchronized void configureDirServers(
-			Set<String> authorizedDirServerStrings) {
+	public synchronized HiddenService addHiddenService(String serviceName) {
 
 		// log entering
-		this.logger.entering(this.getClass().getName(), "configureDirServers",
-				authorizedDirServerStrings);
+		this.logger.entering(this.getClass().getName(), "addHiddenService",
+				serviceName);
 
-		// check parameter
-		if (authorizedDirServerStrings == null) {
-			IllegalArgumentException e = new IllegalArgumentException(
-					"Parameter may not be null!");
-			this.logger.throwing(this.getClass().getName(),
-					"configureDirServers", e);
-			throw e;
-		}
+		// invoke overloaded method
+		HiddenService result = this.addHiddenService(serviceName, this.network
+				.getNextPortNumber(), 80);
 
-		// add to configuration
-		int entriesBefore = this.configuration.size();
-		this.configuration.addAll(authorizedDirServerStrings);
-		this.logger.log(Level.FINE, "Added "
-				+ authorizedDirServerStrings.size()
-				+ " DirServer entries to configuration, having "
-				+ entriesBefore + " entries before and "
-				+ this.configuration.size()
-				+ " entries afterwards (duplicates possible)!");
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "configureDirServers");
+		// log exiting and return hidden service
+		this.logger.exiting(this.getClass().getName(), "addHiddenService",
+				result);
+		return result;
 	}
 
 	public String getNodeName() {
@@ -521,62 +430,9 @@
 		return this.nodeState;
 	}
 
-	public synchronized String getOnionAddress(String serviceName, int version)
-			throws TorProcessException {
+	public synchronized void hup() throws PuppeTorException {
 
 		// log entering
-		this.logger.entering(this.getClass().getName(), "getOnionAddress",
-				new Object[] { serviceName, version });
-
-		// check parameter
-		if (serviceName == null || serviceName.length() == 0) {
-			IllegalArgumentException e = new IllegalArgumentException();
-			this.logger.throwing(this.getClass().getName(), "getOnionAddress",
-					e);
-			throw e;
-		}
-
-		// check if hidden service directory exists
-		File hiddenServiceFile = new File(workingDir.getAbsolutePath()
-				+ File.separator + serviceName + File.separator + "hostname"
-				+ ((version <= 1) ? "" : "2"));
-		if (!hiddenServiceFile.exists()) {
-			this.logger.log(Level.SEVERE,
-					"Hidden service directory or hostname file does not exist: "
-							+ hiddenServiceFile.getAbsolutePath());
-
-			TorProcessException e = new TorProcessException(
-					"Hidden service directory or hostname file does not exist: "
-							+ hiddenServiceFile.getAbsolutePath());
-			this.logger.throwing(this.getClass().getName(), "getOnionAddress",
-					e);
-			throw e;
-		}
-
-		// read hostname from file
-		String address = null;
-		try {
-			BufferedReader br = new BufferedReader(new FileReader(
-					hiddenServiceFile));
-			address = br.readLine();
-			br.close();
-		} catch (IOException e) {
-			TorProcessException ex = new TorProcessException(
-					"Could not read hostname file!", e);
-			this.logger.throwing(this.getClass().getName(), "getOnionAddress",
-					ex);
-			throw ex;
-		}
-
-		// log exiting and return address
-		this.logger.exiting(this.getClass().getName(), "getOnionAddress",
-				address);
-		return address;
-	}
-
-	public synchronized void hup() throws TorProcessException {
-
-		// log entering
 		this.logger.entering(this.getClass().getName(), "hup");
 
 		// check state
@@ -592,7 +448,7 @@
 		try {
 			this.conn.signal("HUP");
 		} catch (IOException e) {
-			TorProcessException ex = new TorProcessException(
+			PuppeTorException ex = new PuppeTorException(
 					"Could not send the command HUP to the Tor process!", e);
 			this.logger.throwing(this.getClass().getName(), "hup", ex);
 			throw ex;
@@ -610,7 +466,7 @@
 		this.logger.exiting(this.getClass().getName(), "hup");
 	}
 
-	public synchronized void shutdown() throws TorProcessException {
+	public synchronized void shutdown() throws PuppeTorException {
 
 		// log entering
 		this.logger.entering(this.getClass().getName(), "shutdown");
@@ -625,12 +481,11 @@
 		// we cannot simply kill the Tor process, because we have established a
 		// controller connection to it which would interpret a closed socket as
 		// failure and throw a RuntimeException
-		// TODO who cares?!
 		try {
 			this.conn.shutdownTor("SHUTDOWN");
 			this.conn.shutdownTor("SHUTDOWN");
 		} catch (IOException e) {
-			TorProcessException ex = new TorProcessException(
+			PuppeTorException ex = new PuppeTorException(
 					"Could not send shutdown command to Tor process!", e);
 			this.logger.throwing(this.getClass().getName(), "shutdown", ex);
 			throw ex;
@@ -711,7 +566,7 @@
 	}
 
 	public synchronized boolean startNode(long maximumTimeToWaitInMillis)
-			throws TorProcessException {
+			throws PuppeTorException {
 
 		// log entering
 		this.logger.entering(this.getClass().getName(), "startNode",
@@ -736,7 +591,7 @@
 			this.logger.log(Level.FINE, "Started Tor process successfully!");
 		} catch (IOException e) {
 			String reason = "Could not start Tor process!";
-			TorProcessException ex = new TorProcessException(reason, e);
+			PuppeTorException ex = new PuppeTorException(reason, e);
 			this.logger.throwing(this.getClass().getName(), "startNode", ex);
 			throw ex;
 		}
@@ -828,7 +683,7 @@
 			// Tor did not open its control port
 			this.logger.log(Level.WARNING, "Tor node " + this.nodeName
 					+ " did not manage to open its control port within "
-					+ maximumTimeToWaitInMillis + " millis!");
+					+ maximumTimeToWaitInMillis + " milliseconds!");
 
 			// log exiting
 			this.logger.exiting(this.getClass().getName(), "startNode", false);
@@ -847,7 +702,7 @@
 		} catch (IOException e) {
 			String reason = "Could not connect to control port " + controlPort
 					+ "!";
-			TorProcessException ex = new TorProcessException(reason, e);
+			PuppeTorException ex = new PuppeTorException(reason, e);
 			this.logger.throwing(this.getClass().getName(), "startNode", ex);
 			throw ex;
 		}
@@ -872,60 +727,34 @@
 				+ ", socksPort=" + this.socksPort;
 	}
 
-	public synchronized void writeConfiguration() throws TorProcessException {
+	public synchronized void writeConfiguration() throws PuppeTorException {
 
 		// log entering
 		this.logger.entering(this.getClass().getName(), "writeConfiguration");
 
 		// write config file
-		this.writeConfigurationFile(this.configFile, this.configuration);
-
-		// change state, if necessary
-		if (this.nodeState == NodeState.CONFIGURING) {
-			this.nodeState = NodeState.CONFIGURATION_WRITTEN;
-		}
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "writeConfiguration");
-	}
-
-	/**
-	 * Writes the given configuration strings to the given configuration file.
-	 * 
-	 * @param configurationFile
-	 *            File to write the configuration to.
-	 * @param configurationStrings
-	 *            Configuration strings to be written.
-	 * @throws TorProcessException
-	 *             Thrown if the configuration file cannot be written to disk.
-	 */
-	protected void writeConfigurationFile(File configurationFile,
-			List<String> configurationStrings) throws TorProcessException {
-
-		// log entering
-		this.logger.entering(this.getClass().getName(),
-				"writeConfigurationFile", new Object[] { configurationFile,
-						configurationStrings });
-
-		// write config file
 		try {
 			BufferedWriter bw = new BufferedWriter(new FileWriter(
-					configurationFile));
-			for (String c : configurationStrings) {
+					this.configFile));
+			for (String c : this.configuration) {
 				bw.write(c + "\n");
 			}
 			bw.close();
 		} catch (IOException e) {
-			TorProcessException ex = new TorProcessException(
+			PuppeTorException ex = new PuppeTorException(
 					"Could not write configuration file!", e);
 			this.logger.throwing(this.getClass().getName(),
 					"writeConfigurationFile", ex);
 			throw ex;
 		}
 
+		// change state, if necessary
+		if (this.nodeState == NodeState.CONFIGURING) {
+			this.nodeState = NodeState.CONFIGURATION_WRITTEN;
+		}
+
 		// log exiting
-		this.logger
-				.exiting(this.getClass().getName(), "writeConfigurationFile");
+		this.logger.exiting(this.getClass().getName(), "writeConfiguration");
 	}
 
 	public int getSocksPort() {
@@ -939,17 +768,4 @@
 	public List<String> getConfiguration() {
 		return new ArrayList<String>(this.configuration);
 	}
-
-	public void replaceDirectoryServers(Set<String> dirServerString)
-			throws TorProcessException {
-		List<String> tempConfigurationList = new ArrayList<String>();
-		for (String line : this.configuration) {
-			if (!line.startsWith("DirServer")) {
-				tempConfigurationList.add(line);
-			}
-		}
-		tempConfigurationList.addAll(dirServerString);
-		this.configuration = tempConfigurationList;
-		this.writeConfigurationFile(configFile, this.configuration);
-	}
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -32,30 +32,185 @@
 package de.uniba.wiai.lspi.puppetor.impl;
 
 import java.io.BufferedReader;
+import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileReader;
+import java.io.FileWriter;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.rmi.RemoteException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.logging.Level;
 import java.util.regex.Pattern;
 
-import de.uniba.wiai.lspi.puppetor.NodeState;
+import de.uniba.wiai.lspi.puppetor.PuppeTorException;
 import de.uniba.wiai.lspi.puppetor.RouterNode;
-import de.uniba.wiai.lspi.puppetor.TorProcessException;
 
 /**
  * Implementation of <code>RouterNode</code>.
  * 
  * @author kloesing
- * 
  */
 @SuppressWarnings("serial")
 public class RouterNodeImpl extends ProxyNodeImpl implements RouterNode {
 
 	/**
+	 * Internal thread class that is used to determine fingerprints in parallel,
+	 * which can take a few seconds.
+	 */
+	private class FingerprintThread extends Thread {
+
+		@Override
+		public void run() {
+
+			// log entering
+			logger.entering(this.getClass().getName(), "run");
+
+			// create file reference for temporary config file
+			File tempConfigFile = new File(RouterNodeImpl.this.workingDir
+					.getAbsolutePath()
+					+ File.separator + "torrc.tmp");
+
+			// compose a modified config file, including a DirServer option with
+			// false fingerprint; this is necessary, because otherwise Tor
+			// would not accept that this router node might have a private IP
+			// address, but connects to the public directory servers
+			List<String> copyOfConfig = new ArrayList<String>(
+					RouterNodeImpl.this.configuration);
+			String fakeDirServerString = "DirServer "
+					+ RouterNodeImpl.this.nodeName + " 127.0.0.1:"
+					+ RouterNodeImpl.this.dirPort
+					+ " 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000";
+			copyOfConfig.add(fakeDirServerString);
+
+			// write config file
+			try {
+				BufferedWriter bw = new BufferedWriter(new FileWriter(
+						tempConfigFile));
+				for (String c : copyOfConfig) {
+					bw.write(c + "\n");
+				}
+				bw.close();
+			} catch (IOException e) {
+				PuppeTorException ex = new PuppeTorException(
+						"Could not write configuration file!", e);
+				logger.log(Level.WARNING, "Could not start Tor process!", ex);
+				RouterNodeImpl.this.setCaughtException(ex);
+				return;
+			}
+
+			// start process with option --list-fingerprint
+			ProcessBuilder processBuilder = new ProcessBuilder(torExecutable
+					.getPath(), "--list-fingerprint", "-f", "torrc.tmp");
+			processBuilder.directory(RouterNodeImpl.this.workingDir);
+			processBuilder.redirectErrorStream(true);
+			Process tmpProcess = null;
+			try {
+				tmpProcess = processBuilder.start();
+			} catch (IOException e) {
+				PuppeTorException ex = new PuppeTorException(
+						"Could not start Tor process temporarily with "
+								+ "--list-fingerprint option!", e);
+				logger.log(Level.WARNING, "Could not start Tor process!", ex);
+				RouterNodeImpl.this.setCaughtException(ex);
+				return;
+			}
+
+			// wait for process to terminate
+			int exitValue = 0;
+			try {
+				exitValue = tmpProcess.waitFor();
+			} catch (InterruptedException e) {
+				PuppeTorException ex = new PuppeTorException(
+						"Interrupted while waiting for Tor process to exit!", e);
+				logger.log(Level.WARNING,
+						"Temporary Tor process was interrupted!", ex);
+				RouterNodeImpl.this.setCaughtException(ex);
+				return;
+			}
+
+			if (exitValue != 0) {
+				PuppeTorException ex = new PuppeTorException(
+						"Could not start Tor process temporarily with "
+								+ "--list-fingerprint option! Tor exited with "
+								+ "exit value " + exitValue
+								+ "! Please go check the config options in "
+								+ tempConfigFile + " manually!");
+				logger.log(Level.WARNING, "Could not start Tor process!", ex);
+				RouterNodeImpl.this.setCaughtException(ex);
+				return;
+			}
+
+			// read fingerprint from file
+			File fingerprintFile = new File(RouterNodeImpl.this.workingDir
+					.getAbsolutePath()
+					+ File.separator + "fingerprint");
+			try {
+				BufferedReader br2 = new BufferedReader(new FileReader(
+						fingerprintFile));
+				RouterNodeImpl.this.setFingerprint(br2.readLine());
+				br2.close();
+			} catch (IOException e) {
+				PuppeTorException ex = new PuppeTorException(
+						"Could not read fingerprint from file!", e);
+				logger.log(Level.WARNING, "Could not read fingerprint file!",
+						ex);
+				RouterNodeImpl.this.setCaughtException(ex);
+				return;
+			}
+
+			// delete temporary config file
+			tempConfigFile.delete();
+
+			// log exiting
+			logger.exiting(this.getClass().getName(), "run");
+		}
+	}
+
+	/**
+	 * Invoked by the fingerprint thread: sets the determined fingerprint string
+	 * 
+	 * @param fingerprint
+	 *            The determined fingerprint string.
+	 */
+	private synchronized void setFingerprint(String fingerprint) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "setFingerprint",
+				fingerprint);
+
+		// remember fingerprint and notify all waiting threads
+		this.fingerprint = fingerprint;
+		this.notifyAll();
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "setFingerprint");
+	}
+
+	/**
+	 * Invoked by the fingerprint thread: sets the exception that occurred when
+	 * trying to determine the fingerprint.
+	 * 
+	 * @param caughtException
+	 *            The exception that occurred when trying to determine the
+	 *            fingerprint.
+	 */
+	private synchronized void setCaughtException(
+			PuppeTorException caughtException) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "setCaughtException",
+				caughtException);
+
+		// remember caught exception and notify all waiting threads
+		this.caughtException = caughtException;
+		this.notifyAll();
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "setCaughtException");
+	}
+
+	/**
 	 * Port on which the Tor node will be listening for directory requests from
 	 * other Tor nodes.
 	 */
@@ -68,21 +223,17 @@
 	protected String serverIpAddress;
 
 	/**
-	 * Temporary config file that is used to determine the fingerprint of this
-	 * node.
-	 */
-	protected File tempConfigFile;
-
-	/**
 	 * The fingerprint of this node that is determined as hash value of its
-	 * onion key.
+	 * onion key. It is initialized with <code>null</code> and set by the
+	 * fingerprint thread as soon as it is determined.
 	 */
-	protected String fingerprint;
+	private String fingerprint;
 
 	/**
-	 * The base32-encoded ID of this node.
+	 * The exception that was caught when determining the fingerprint of this
+	 * node, if any.
 	 */
-	protected String fingerprintBase32;
+	protected PuppeTorException caughtException;
 
 	/**
 	 * The pattern for valid IP v4 addresses in dotted decimal notation.
@@ -172,7 +323,8 @@
 		// all routers mirror the directory
 		this.configuration.add("DirPort " + dirPort);
 
-		// the address of this node should be localhost and not guessed by Tor
+		// the address of this node should be manually specified and not guessed
+		// by Tor
 		this.configuration.add("Address " + serverIpAddress);
 
 		// the OR port may only be contacted locally
@@ -190,263 +342,47 @@
 		// bypass testing if we are reachable
 		this.configuration.add("AssumeReachable 1");
 
-		// create file reference for temporary config file
-		this.tempConfigFile = new File(this.workingDir.getAbsolutePath()
-				+ File.separator + "torrc.tmp");
+		// start a thread to determine the node's fingerprint in the background
+		this.determineFingerprint();
 
 		// log exiting
 		this.logger.exiting(this.getClass().getName(), "RouterNodeImpl");
 	}
 
-	public synchronized String getFingerprintBase32() {
+	public synchronized String getFingerprint() throws PuppeTorException,
+			RemoteException {
 
 		// log entering
-		this.logger.entering(this.getClass().getName(), "getFingerprintBase32");
-
-		// check state
-		if (this.nodeState != NodeState.RUNNING
-				&& this.nodeState != NodeState.SHUT_DOWN) {
-			String reason = "Node is neither in state NodeState.RUNNING "
-					+ "nor in NodeState.SHUT_DOWN, but in state "
-					+ this.nodeState + "!";
-			IllegalStateException e = new IllegalStateException(reason);
-			this.logger.throwing(this.getClass().getName(),
-					"getFingerprintBase32", e);
-			throw e;
-		}
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(), "getFingerprintBase32",
-				this.fingerprintBase32);
-		return this.fingerprintBase32;
-	}
-
-	public String getFingerprint() throws RemoteException {
-
-		// log entering
 		this.logger.entering(this.getClass().getName(), "getFingerprint");
 
-		// check state
-		if (this.nodeState != NodeState.RUNNING
-				&& this.nodeState != NodeState.SHUT_DOWN) {
-			String reason = "Node is neither in state NodeState.RUNNING "
-					+ "nor in NodeState.SHUT_DOWN, but in state "
-					+ this.nodeState + "!";
-			IllegalStateException e = new IllegalStateException(reason);
-			this.logger
-					.throwing(this.getClass().getName(), "getFingerprint", e);
-			throw e;
-		}
-		String result = this.fingerprint.substring(this.fingerprint
-				.indexOf(" ") + 1);
-
-		// log exiting
-		this.logger
-				.exiting(this.getClass().getName(), "getFingerprint", result);
-		return result;
-	}
-
-	public synchronized String determineFingerprint()
-			throws TorProcessException {
-
-		// log entering
-		this.logger.entering(this.getClass().getName(), "determineFingerprint");
-
-		// check if we can answer the request immediately
-		if (this.fingerprint != null) {
-
-			// log exiting and return fingerprint
-			this.logger.exiting(this.getClass().getName(),
-					"determineFingerprint", this.fingerprint);
-			return this.fingerprint;
-		}
-
-		// write modified config file, including a DirServer option with
-		// false fingerprint; this is necessary, because otherwise Tor
-		// would not accept that this router node has a private IP
-		// address, but connects to the public directory servers; just a
-		// workaround...
-		List<String> copyOfConfig = new ArrayList<String>(this.configuration);
-		// TODO when working dir exists, but Tor did not manage to write
-		// a fingerprint file, we get a NullPointerException...
-		String fakeDirServerString = "DirServer " + this.nodeName
-				+ " 127.0.0.1:" + this.dirPort
-				+ " 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000";
-		copyOfConfig.add(fakeDirServerString);
-		writeConfigurationFile(this.tempConfigFile, copyOfConfig);
-
-		// start process with option --list-fingerprint
-		// TODO make this more configurable
-		ProcessBuilder processBuilder = new ProcessBuilder(torExecutable
-				.getPath(), "--list-fingerprint", "-f", "torrc.tmp");
-		processBuilder.directory(this.workingDir);
-		processBuilder.redirectErrorStream(true);
-		Process tmpProcess = null;
-		try {
-			tmpProcess = processBuilder.start();
-		} catch (IOException e) {
-			TorProcessException ex = new TorProcessException(
-					"Could not start Tor process temporarily with "
-							+ "--list-fingerprint option!", e);
-			this.logger.throwing(this.getClass().getName(),
-					"determineFingerprint", ex);
-			throw ex;
-		}
-
-		// start thread to parse output
-		final BufferedReader br = new BufferedReader(new InputStreamReader(
-				tmpProcess.getInputStream()));
-		Thread outputThread = new Thread(new Runnable() {
-			public void run() {
-				try {
-					// read output from tor
-					String line = null;
-					while ((line = br.readLine()) != null) {
-						// TODO discard output?
-						logger.log(Level.FINER, line);
-					}
-				} catch (IOException e) {
-					// TODO how to handle?
-					logger.log(Level.SEVERE,
-							"I/O exception while parsing output of temporary "
-									+ "Tor process!");
-				}
+		// wait until either the fingerprint has been determined or an exception
+		// was caught
+		while (this.fingerprint == null && this.caughtException == null) {
+			try {
+				wait();
+			} catch (InterruptedException e) {
+				// do nothing
 			}
-		});
-		outputThread.setDaemon(true);
-		outputThread.start();
-
-		// wait for process to terminate (should be quite fast)
-		int exitValue = 0;
-		try {
-			exitValue = tmpProcess.waitFor();
-		} catch (InterruptedException e) {
-			// TODO how to handle?
-			this.logger.log(Level.WARNING,
-					"Temporary Tor process was interrupted!", e);
 		}
 
-		if (exitValue != 0) {
-			TorProcessException ex = new TorProcessException(
-					"Could not start Tor process temporarily with "
-							+ "--list-fingerprint option! Tor exited with "
-							+ "exit value " + exitValue
-							+ "! Please go check the config options in "
-							+ this.tempConfigFile + " manually!");
-			this.logger.throwing(this.getClass().getName(),
-					"determineFingerprint", ex);
-			throw ex;
+		if (this.caughtException != null) {
+			this.logger.throwing(this.getClass().getName(), "getFingerprint",
+					this.caughtException);
+			throw this.caughtException;
 		}
 
-		// read fingerprint from file
-		this.readFingerprintFromFile();
-
-		// log exiting and return fingerprint
-		this.logger.exiting(this.getClass().getName(), "determineFingerprint",
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "getFingerprint",
 				this.fingerprint);
 		return this.fingerprint;
 	}
 
-	/**
-	 * Reads the fingerprint from disk. This requires that the fingerprint file
-	 * has been written before by Tor. Otherwise, a TorProcessException is
-	 * thrown.
-	 * 
-	 * @throws TorProcessException
-	 *             Thrown if no fingerprint file has been written by Tor before.
-	 */
-	private void readFingerprintFromFile() throws TorProcessException {
-
-		// log entering
-		this.logger.entering(this.getClass().getName(),
-				"readFingerprintFromFile");
-
-		// check if the fingerprint has been read before
-		if (this.fingerprint != null) {
-
-			// log exiting and return fingerprint
-			this.logger.exiting(this.getClass().getName(),
-					"readFingerprintFromFile");
-			return;
-		}
-
-		// read fingerprint from file
-		File fingerprintFile = new File(this.workingDir.getAbsolutePath()
-				+ File.separator + "fingerprint");
-		try {
-			BufferedReader br2 = new BufferedReader(new FileReader(
-					fingerprintFile));
-			this.fingerprint = br2.readLine();
-			br2.close();
-		} catch (IOException e) {
-			TorProcessException ex = new TorProcessException(
-					"Could not read fingerprint from file!", e);
-			this.logger.throwing(this.getClass().getName(),
-					"determineFingerprint", ex);
-			throw ex;
-		}
-
-		// parse fingerprint string to base32 encoded ID
-
-		// convert fingerprint string to base32 encoding
-		// TODO move to separate util class, document it
-		String fp = this.fingerprint;
-		fp = fp.substring(fp.indexOf(' ') + 1);
-		byte[] bytes = new byte[20];
-		int j = 0;
-		for (String part : fp.split(" ")) {
-			bytes[j++] = (byte) Integer.parseInt(part.substring(0, 2), 16);
-			bytes[j++] = (byte) Integer.parseInt(part.substring(2), 16);
-		}
-		int i = 0, index = 0, digit = 0;
-		int currByte, nextByte;
-		StringBuffer base32 = new StringBuffer((bytes.length + 7) * 8 / 5);
-		String base32Chars = "abcdefghijklmnopqrstuvwxyz234567";
-		while (i < bytes.length) {
-			currByte = (bytes[i] >= 0) ? bytes[i] : (bytes[i] + 256);
-			if (index > 3) {
-				if ((i + 1) < bytes.length) {
-					nextByte = (bytes[i + 1] >= 0) ? bytes[i + 1]
-							: (bytes[i + 1] + 256);
-				} else {
-					nextByte = 0;
-				}
-				digit = currByte & (0xFF >> index);
-				index = (index + 5) % 8;
-				digit <<= index;
-				digit |= nextByte >> (8 - index);
-				i++;
-			} else {
-				digit = (currByte >> (8 - (index + 5))) & 0x1F;
-				index = (index + 5) % 8;
-				if (index == 0) {
-					i++;
-				}
-			}
-			base32.append(base32Chars.charAt(digit));
-		}
-		this.fingerprintBase32 = base32.toString();
-
-		// log exiting
-		this.logger.exiting(this.getClass().getName(),
-				"readFingerprintFromFile");
-	}
-
 	@Override
 	public String toString() {
 		return super.toString() + ", orPort=" + this.orPort + ", dirPort="
 				+ this.dirPort;
 	}
 
-	public String getName() {
-		String fp = "";
-		if (this.fingerprint != null) {
-			fp = this.fingerprint.substring(this.fingerprint.indexOf(" ") + 1);
-			fp = fp.substring(0, 4);
-		}
-		return this.nodeName + " (" + fp + ")";
-	}
-
 	public int getDirPort() {
 		return this.dirPort;
 	}
@@ -455,20 +391,21 @@
 		return this.orPort;
 	}
 
-	public boolean startNode(long maximumTimeToWaitInMillis)
-			throws TorProcessException {
+	/**
+	 * Determines the fingerprint of this node by starting a background thread
+	 * that performs this operation.
+	 */
+	protected void determineFingerprint() {
 
 		// log entering
-		this.logger.entering(this.getClass().getName(), "startNode");
+		this.logger.entering(this.getClass().getName(), "determineFingerprint");
 
-		// before starting the node, determine its fingerprint
-		this.determineFingerprint();
+		// start a thread to determine this node's fingerprint
+		FingerprintThread fingerprintThread = new FingerprintThread();
+		fingerprintThread.setName(nodeName + " Fingerprint Resolver");
+		fingerprintThread.start();
 
-		// perform the same as a proxy node does
-		boolean result = super.startNode(maximumTimeToWaitInMillis);
-
-		// log exiting and return result
-		this.logger.exiting(this.getClass().getName(), "startNode", result);
-		return result;
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "determineFingerprint");
 	}
 }

Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java	2007-10-01 16:25:45 UTC (rev 11735)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java	2007-10-01 21:59:35 UTC (rev 11736)
@@ -231,13 +231,13 @@
 	}
 
 	/**
-	 * Event manager to which all events concerning this server application are
-	 * notified.
+	 * Event manager that handles all events concerning this server application.
 	 */
 	private EventManagerImpl eventManager;
 
 	/**
-	 * Logger for this server.
+	 * Logger for this server which is called "server." plus the name of this
+	 * server application.
 	 */
 	private Logger logger;
 
@@ -282,7 +282,7 @@
 		}
 
 		// create logger
-		this.logger = Logger.getLogger("application." + serverApplicationName);
+		this.logger = Logger.getLogger("server." + serverApplicationName);
 
 		// log entering
 		this.logger.entering(this.getClass().getName(),
@@ -308,7 +308,7 @@
 		this.logger.exiting(this.getClass().getName(), "ServerApplicationImpl");
 	}
 
-	public synchronized void listen() {
+	public synchronized void startListening() {
 
 		// log entering
 		this.logger.entering(this.getClass().getName(), "listen");
@@ -336,10 +336,10 @@
 		// log entering
 		this.logger.entering(this.getClass().getName(), "stopListening");
 
-		// check if a request is running
+		// check if we are listening
 		if (this.serverThread == null) {
 			IllegalStateException e = new IllegalStateException(
-					"We did not start listening before!");
+					"We are not listening!");
 			this.logger.throwing(this.getClass().getName(), "stopListening", e);
 			throw e;
 		}
@@ -350,10 +350,17 @@
 		// interrupt thread
 		this.serverThread.interrupt();
 
+		// unset listening thread
+		this.serverThread = null;
+
 		// log exiting
 		this.logger.exiting(this.getClass().getName(), "stopListening");
 	}
 
+	public synchronized boolean isListening() {
+		return (this.serverThread != null);
+	}
+
 	@Override
 	public String toString() {
 		return this.getClass().getSimpleName() + ": serverApplicationName=\""
@@ -364,4 +371,8 @@
 	public String getServerApplicationName() {
 		return this.serverApplicationName;
 	}
+
+	public int getServerPort() {
+		return this.serverPort;
+	}
 }



More information about the tor-commits mailing list