[RJ-Server] Add easier option to log the communication

  - Improve .toString() implementations

Change-Id: Id4934529590a3bd1d7c37ce6f1ddef34799bcf70
diff --git a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/GDCmdItem.java b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/GDCmdItem.java
index 9685efb..783c395 100644
--- a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/GDCmdItem.java
+++ b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/GDCmdItem.java
@@ -41,7 +41,7 @@
 	public static final byte DRAW_TEXT=                    0x17;
 	public static final byte DRAW_RASTER=                  0x18;
 	
-	public static final byte CAPTURE=                      0x1c;
+	public static final byte CAPTURE=                      0x1C;
 	
 	public static final byte C_NEW_PAGE=                   0x21;
 	public static final byte C_CLOSE_DEVICE=               0x22;
@@ -54,6 +54,69 @@
 	
 	public static final byte U_LOCATOR=                    0x31;
 	
+	private static final byte ANSWER=                      (byte)0xF0;
+	
+	private static final String getCommandLabel(final byte commandId) {
+		switch (commandId) {
+		case SET_CLIP:
+			return "SET_CLIP";
+		case SET_COLOR:
+			return "SET_COLOR";
+		case SET_FILL:
+			return "SET_FILL";
+		case SET_LINE:
+			return "SET_LINE";
+		case SET_FONT:
+			return "SET_FONT";
+		
+		case DRAW_LINE:
+			return "DRAW_LINE";
+		case DRAW_RECTANGLE:
+			return "DRAW_RECTANGLE";
+		case DRAW_POLYLINE:
+			return "DRAW_POLYLINE";
+		case DRAW_POLYGON:
+			return "DRAW_POLYGON";
+		case DRAW_PATH:
+			return "DRAW_PATH";
+		case DRAW_CIRCLE:
+			return "DRAW_CIRCLE";
+		case DRAW_TEXT:
+			return "DRAW_TEXT";
+		case DRAW_RASTER:
+			return "DRAW_RASTER";
+		
+		case CAPTURE:
+			return "CAPTURE";
+		
+		case C_NEW_PAGE:
+			return "C_NEW_PAGE";
+		case C_CLOSE_DEVICE:
+			return "C_CLOSE_DEVICE";
+		case C_GET_SIZE:
+			return "C_GET_SIZE";
+		case C_SET_ACTIVE_OFF:
+			return "C_SET_ACTIVE_OFF";
+		case C_SET_ACTIVE_ON:
+			return "C_SET_ACTIVE_ON";
+		case C_SET_MODE:
+			return "C_SET_MODE";
+		case C_GET_FONTMETRIC:
+			return "C_GET_FONTMETRIC";
+		case C_GET_STRINGWIDTH:
+			return "C_GET_STRINGWIDTH";
+		
+		case U_LOCATOR:
+			return "U_LOCATOR";
+		
+		case ANSWER:
+			return "ANSWER";
+			
+		default:
+			return String.format("%1$02X", commandId);
+		}
+	}
+	
 	
 	private static final double[] NO_DATA= new double[0];
 	
@@ -146,19 +209,13 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer(100);
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=ANSWER");
-			sb.append(")");
-			sb.append("\n<GD-DATA>\n");
+			final StringBuilder sb= toString(ANSWER);
+			sb.append("<GD-DATA>\n");
 			switch (this.options & OM_WITHDATA) {
 			case OV_WITHDOUBLE:
-				sb.append(Arrays.toString((double[]) this.data));
+				sb.append(Arrays.toString((double[])this.data)).append('\n');
 			}
-			sb.append("\n</GD-DATA>");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -199,16 +256,10 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer(100);
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=ANSWER");
-			sb.append(")");
-			sb.append("\n<GD-DATA>\n");
-			sb.append(Arrays.toString(this.data));
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(ANSWER);
+			sb.append("<GD-DATA>\n");
+			sb.append(Arrays.toString(this.data)).append('\n');
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -248,15 +299,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(C_NEW_PAGE);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(C_NEW_PAGE);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -281,14 +326,8 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(C_CLOSE_DEVICE);
-			sb.append(")\n<GD-DATA />");
+			final StringBuilder sb= toString(C_CLOSE_DEVICE);
+			sb.append("<GD-DATA/>");
 			return sb.toString();
 		}
 		
@@ -320,15 +359,8 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(C_GET_SIZE);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(C_GET_SIZE);
+			sb.append("<GD-DATA/>");
 			return sb.toString();
 		}
 		
@@ -353,14 +385,8 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(C_SET_ACTIVE_OFF);
-			sb.append(")\n<GD-DATA />");
+			final StringBuilder sb= toString(C_SET_ACTIVE_OFF);
+			sb.append("<GD-DATA/>");
 			return sb.toString();
 		}
 		
@@ -385,14 +411,8 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(C_SET_ACTIVE_ON);
-			sb.append(")\n<GD-DATA />");
+			final StringBuilder sb= toString(C_SET_ACTIVE_ON);
+			sb.append("<GD-DATA/>");
 			return sb.toString();
 		}
 		
@@ -422,16 +442,10 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(C_SET_MODE);
-			sb.append(")<GD-DATA>\nmode= ");
-			sb.append(this.mode);
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(C_SET_MODE);
+			sb.append("<GD-DATA>\n");
+			sb.append("mode= ").append(this.mode).append('\n');
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -468,15 +482,10 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(C_GET_FONTMETRIC);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(C_GET_FONTMETRIC);
+			sb.append("<GD-DATA>\n");
+			sb.append("c= ").append(this.c).append('\n');
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -513,15 +522,10 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(C_GET_STRINGWIDTH);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(C_GET_STRINGWIDTH);
+			sb.append("<GD-DATA>\n");
+			sb.append("text= ").append(this.text).append('\n');
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -561,15 +565,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(SET_CLIP);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(SET_CLIP);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -599,15 +597,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(SET_COLOR);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(SET_COLOR);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -637,15 +629,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(SET_FILL);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(SET_FILL);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -689,14 +675,8 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(SET_LINE);
-			sb.append(")\n<GD-DATA>\n");
+			final StringBuilder sb= toString(SET_LINE);
+			sb.append("<GD-DATA>\n");
 			sb.append("\n</GD-DATA>");
 			return sb.toString();
 		}
@@ -738,15 +718,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(SET_FONT);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(SET_FONT);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -786,15 +760,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(DRAW_LINE);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(DRAW_LINE);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -834,15 +802,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(DRAW_RECTANGLE);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(DRAW_RECTANGLE);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -878,15 +840,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(DRAW_POLYLINE);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(DRAW_POLYLINE);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -922,15 +878,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(DRAW_POLYGON);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(DRAW_POLYGON);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -972,15 +922,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(DRAW_PATH);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(DRAW_PATH);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -1017,15 +961,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(DRAW_CIRCLE);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(DRAW_CIRCLE);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -1069,15 +1007,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(DRAW_TEXT);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(DRAW_TEXT);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -1137,15 +1069,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(DRAW_RASTER);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(DRAW_RASTER);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -1187,15 +1113,9 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(CAPTURE);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(CAPTURE);
+			sb.append("<GD-DATA>\n");
+			sb.append("</GD-DATA>");
 			return sb.toString();
 		}
 		
@@ -1222,15 +1142,8 @@
 		
 		@Override
 		public String toString() {
-			final StringBuffer sb= new StringBuffer();
-			sb.append("GDCmdItem (options=0x");
-			sb.append(Integer.toHexString(this.options));
-			sb.append(", device=");
-			sb.append(this.devId);
-			sb.append(", commandId=");
-			sb.append(U_LOCATOR);
-			sb.append(")\n<GD-DATA>\n");
-			sb.append("\n</GD-DATA>");
+			final StringBuilder sb= toString(U_LOCATOR);
+			sb.append("<GD-DATA/>\n");
 			return sb.toString();
 		}
 		
@@ -1307,4 +1220,17 @@
 		return true;
 	}
 	
+	
+	protected StringBuilder toString(final byte commandId) {
+		final StringBuilder sb= new StringBuilder();
+		sb.append("GDCmdItem (options=0x");
+		sb.append(Integer.toHexString(this.options));
+		sb.append(", device=");
+		sb.append(this.devId);
+		sb.append(", commandId=");
+		sb.append(getCommandLabel(commandId));
+		sb.append(")\n");
+		return sb;
+	}
+	
 }
diff --git a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/MainCmdC2SList.java b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/MainCmdC2SList.java
index 3b94497..e38f767 100644
--- a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/MainCmdC2SList.java
+++ b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/MainCmdC2SList.java
@@ -174,7 +174,7 @@
 		sb.append("MainCmdC2SList (");
 		sb.append("):");
 		if (this.first == null) {
-			sb.append("\n<ITEM />");
+			sb.append("\n<ITEM/>");
 		}
 		else {
 			MainCmdItem item= this.first;
diff --git a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/MainCmdS2CList.java b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/MainCmdS2CList.java
index fe72243..da85348 100644
--- a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/MainCmdS2CList.java
+++ b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/MainCmdS2CList.java
@@ -211,7 +211,7 @@
 		sb.append(this.isBusy);
 		sb.append("):");
 		if (this.first == null) {
-			sb.append("\n<ITEM />");
+			sb.append("\n<ITEM/>");
 		}
 		else {
 			MainCmdItem item= this.first;
diff --git a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/RjsStatus.java b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/RjsStatus.java
index bca70ce..3e0883a 100644
--- a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/RjsStatus.java
+++ b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/RjsStatus.java
@@ -208,7 +208,7 @@
 			sb.append("\n</TEXT>");
 		}
 		else {
-			sb.append("\n<TEXT />");
+			sb.append("\n<TEXT/>");
 		}
 		return sb.toString();
 	}
diff --git a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/ServerControl.java b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/ServerControl.java
index 131ced1..942233d 100644
--- a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/ServerControl.java
+++ b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/ServerControl.java
@@ -95,7 +95,7 @@
 	}
 	
 	
-	protected static final Logger LOGGER= Logger.getLogger("org.eclipse.statet.rj.server");
+	protected static final Logger LOGGER= Logger.getLogger("org.eclipse.statet.rj.server.srv");
 	
 	
 	
diff --git a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/engine/ComLoggingSrvEngine.java b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/engine/ComLoggingSrvEngine.java
new file mode 100644
index 0000000..4c00240
--- /dev/null
+++ b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/engine/ComLoggingSrvEngine.java
@@ -0,0 +1,258 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.rj.server.srv.engine;
+
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+
+import java.rmi.RemoteException;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+import org.eclipse.statet.jcommons.lang.ObjectUtils.ToStringBuilder;
+
+import org.eclipse.statet.rj.server.RjsComObject;
+import org.eclipse.statet.rj.server.Server;
+import org.eclipse.statet.rj.server.srv.RMIServerControl;
+import org.eclipse.statet.rj.server.srvext.Client;
+
+
+@NonNullByDefault
+public class ComLoggingSrvEngine implements SrvEngine {
+	
+	
+	private static class ComLogRecord extends LogRecord {
+		
+		private static final long serialVersionUID= 1L;
+		
+		private final transient Client client;
+		private final long callId;
+		
+		public ComLogRecord(final Level level, final String methodName, final Client client) {
+			super(level, null);
+			this.client= client;
+			this.callId= getSequenceNumber();
+			
+			setSourceClassName("SrvEngine");
+			setSourceMethodName(methodName);
+		}
+		
+		public ComLogRecord(final Level level, final ComLogRecord ref) {
+			super(level, null);
+			this.client= ref.client;
+			this.callId= ref.callId;
+			
+			setSourceClassName("SrvEngine");
+			setSourceMethodName(ref.getSourceMethodName());
+		}
+		
+		
+		public ToStringBuilder entering(final @Nullable String detail) {
+			final ToStringBuilder sb= new ToStringBuilder("ENTRY #");
+			sb.append(this.callId);
+			if (detail != null) {
+				sb.append(' ', detail);
+			}
+			sb.addProp("client", this.client);
+			return sb;
+		}
+		
+		public ToStringBuilder exiting(final @Nullable String detail) {
+			final ToStringBuilder sb= new ToStringBuilder("EXIT #");
+			sb.append(this.callId);
+			if (detail != null) {
+				sb.append(' ', detail);
+			}
+			sb.addProp("client", this.client);
+			return sb;
+		}
+		
+	}
+	
+	
+	private final SrvEngine engine;
+	
+	private final Logger logger;
+	
+	
+	public ComLoggingSrvEngine(final SrvEngine engine, final Logger logger) {
+		this.engine= nonNullAssert(engine);
+		this.logger= nonNullAssert(logger);
+	}
+	
+	
+	@Override
+	public void init(final RMIServerControl control, final Server publicServer,
+			final Map<String, ? extends @NonNull Object> more) throws Exception {
+		this.engine.init(control, publicServer, more);
+	}
+	
+	@Override
+	public int[] getVersion() {
+		return this.engine.getVersion();
+	}
+	
+	@Override
+	public int getState() {
+		return this.engine.getState();
+	}
+	
+	@Override
+	public Client getCurrentClient() {
+		return this.engine.getCurrentClient();
+	}
+	
+	@Override
+	public Map<String, Object> getPlatformData() {
+		return this.engine.getPlatformData();
+	}
+	
+	
+	@Override
+	public Object start(final Client client, final Map<String, ? extends @NonNull Object> properties)
+			throws RemoteException {
+		final ComLogRecord ref= entering("start", client, properties);
+		try {
+			final var result= this.engine.start(client, properties);
+			exiting(ref, result);
+			return result;
+		}
+		catch (final RemoteException | RuntimeException e) {
+			exiting(ref, e);
+			throw e;
+		}
+	}
+	
+	@Override
+	public Object connect(final Client client, final Map<String, ? extends @NonNull Object> properties)
+			throws RemoteException {
+		final ComLogRecord ref= entering("connect", client, properties);
+		try {
+			final var result= this.engine.connect(client, properties);
+			exiting(ref, result);
+			return result;
+		}
+		catch (final RemoteException | RuntimeException e) {
+			exiting(ref, e);
+			throw e;
+		}
+	}
+	
+	@Override
+	public void disconnect(final Client client)
+			throws RemoteException {
+		final ComLogRecord ref= entering("disconnect", client);
+		try {
+			this.engine.disconnect(client);
+			exiting(ref);
+		}
+		catch (final RemoteException | RuntimeException e) {
+			exiting(ref, e);
+			throw e;
+		}
+	}
+	
+	@Override
+	public void setProperties(final Client client, final Map<String, ? extends @NonNull Object> properties)
+			throws RemoteException {
+		final ComLogRecord ref= entering("setProperties", client, properties);
+		try {
+			this.engine.setProperties(client, properties);
+			exiting(ref);
+		}
+		catch (final RemoteException | RuntimeException e) {
+			exiting(ref, e);
+			throw e;
+		}
+	}
+	
+	
+	@Override
+	public RjsComObject runMainLoop(final Client client, final RjsComObject com)
+			throws RemoteException {
+		final ComLogRecord ref= entering("runMainLoop", client, com);
+		try {
+			final var result= this.engine.runMainLoop(client, com);
+			exiting(ref, result);
+			return result;
+		}
+		catch (final RemoteException | RuntimeException e) {
+			exiting(ref, e);
+			throw e;
+		}
+	}
+	
+	@Override
+	public RjsComObject runAsync(final Client client, final RjsComObject com)
+			throws RemoteException {
+		final ComLogRecord ref= entering("runAsync", client, com);
+		try {
+			final var result= this.engine.runAsync(client, com);
+			exiting(ref, result);
+			return result;
+		}
+		catch (final RemoteException | RuntimeException e) {
+			exiting(ref, e);
+			throw e;
+		}
+	}
+	
+	
+	private ComLogRecord entering(final String methodName, final Client client,
+			final Object param0) {
+		final var record= new ComLogRecord(Level.FINER, methodName, client);
+		final ToStringBuilder sb= record.entering(null);
+		sb.addProp(">>", param0);
+		record.setMessage(sb.toString());
+		this.logger.log(record);
+		return record;
+	}
+	
+	private ComLogRecord entering(final String methodName, final Client client) {
+		final var record= new ComLogRecord(Level.FINER, methodName, client);
+		final ToStringBuilder sb= record.entering(null);
+		record.setMessage(sb.toString());
+		this.logger.log(record);
+		return record;
+	}
+	
+	private void exiting(final ComLogRecord ref, final Object result) {
+		final var record= new ComLogRecord(Level.FINER, ref);
+		final ToStringBuilder sb= record.exiting(null);
+		sb.addProp("<<", result);
+		record.setMessage(sb.toString());
+		this.logger.log(record);
+	}
+	
+	private void exiting(final ComLogRecord ref) {
+		final var record= new ComLogRecord(Level.FINER, ref);
+		final ToStringBuilder sb= record.exiting(null);
+		record.setMessage(sb.toString());
+		this.logger.log(record);
+	}
+	
+	private void exiting(final ComLogRecord ref, final Throwable e) {
+		final var record= new ComLogRecord(Level.FINER, ref);
+		final ToStringBuilder sb= record.exiting("with exception");
+		record.setMessage(sb.toString());
+		record.setThrown(e);
+		this.logger.log(record);
+	}
+	
+}
diff --git a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/engine/REngineImpl.java b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/engine/REngineImpl.java
index 935a397..7bdedf2 100644
--- a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/engine/REngineImpl.java
+++ b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/engine/REngineImpl.java
@@ -14,8 +14,14 @@
 
 package org.eclipse.statet.rj.server.srv.engine;
 
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+
 import java.rmi.RemoteException;
 import java.util.Map;
+import java.util.logging.Logger;
+
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 
 import org.eclipse.statet.rj.server.REngine;
 import org.eclipse.statet.rj.server.RjsComObject;
@@ -23,6 +29,7 @@
 import org.eclipse.statet.rj.server.srvext.Client;
 
 
+@NonNullByDefault
 public final class REngineImpl implements REngine {
 	
 	
@@ -32,8 +39,14 @@
 	
 	
 	public REngineImpl(final Server publicServer, final SrvEngine srvEngine, final Client client) {
-		this.publicServer= publicServer;
-		this.srvEngine= srvEngine;
+		this.publicServer= nonNullAssert(publicServer);
+		if (Boolean.getBoolean("org.eclipse.statet.rj.server.srv.engine.ComLogger.enable")) {
+			final Logger logger= Logger.getLogger("org.eclipse.statet.rj.server.srv");
+			this.srvEngine= new ComLoggingSrvEngine(srvEngine, logger);
+		}
+		else {
+			this.srvEngine= nonNullAssert(srvEngine);
+		}
 		this.client= client;
 	}
 	
@@ -49,27 +62,32 @@
 	}
 	
 	@Override
-	public void setProperties(final Map<String, ? extends Object> properties) throws RemoteException {
+	public void setProperties(final Map<String, ? extends @NonNull Object> properties)
+			throws RemoteException {
 		this.srvEngine.setProperties(this.client, properties);
 	}
 	
 	@Override
-	public void disconnect() throws RemoteException {
+	public void disconnect()
+			throws RemoteException {
 		this.srvEngine.disconnect(this.client);
 	}
 	
 	@Override
-	public RjsComObject runAsync(final RjsComObject com) throws RemoteException {
+	public RjsComObject runAsync(final RjsComObject com)
+			throws RemoteException {
 		return this.srvEngine.runAsync(this.client, com);
 	}
 	
 	@Override
-	public RjsComObject runMainLoop(final RjsComObject com) throws RemoteException {
+	public RjsComObject runMainLoop(final RjsComObject com)
+			throws RemoteException {
 		return this.srvEngine.runMainLoop(this.client, com);
 	}
 	
 	@Override
-	public boolean isClosed() throws RemoteException {
+	public boolean isClosed()
+			throws RemoteException {
 		return (this.srvEngine.getCurrentClient() != this.client);
 	}
 	
diff --git a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/engine/SrvEngine.java b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/engine/SrvEngine.java
index 4497074..068d107 100644
--- a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/engine/SrvEngine.java
+++ b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srv/engine/SrvEngine.java
@@ -30,7 +30,9 @@
 public interface SrvEngine {
 	
 	
-	void init(RMIServerControl control, Server publicServer, Map<String, ? extends @NonNull Object> more) throws Exception;
+	void init(RMIServerControl control, Server publicServer,
+			Map<String, ? extends @NonNull Object> more)
+			throws Exception;
 	
 	int[] getVersion();
 	
@@ -38,12 +40,18 @@
 	Client getCurrentClient();
 	Map<String, Object> getPlatformData();
 	
-	Object start(Client client, Map<String, ? extends @NonNull Object> properties) throws RemoteException;
-	Object connect(Client client, Map<String, ? extends @NonNull Object> properties) throws RemoteException;
-	void disconnect(Client client) throws RemoteException;
-	void setProperties(Client client, Map<String, ? extends @NonNull Object> properties) throws RemoteException;
+	Object start(Client client, Map<String, ? extends @NonNull Object> properties)
+			throws RemoteException;
+	Object connect(Client client, Map<String, ? extends @NonNull Object> properties)
+			throws RemoteException;
+	void disconnect(Client client)
+			throws RemoteException;
+	void setProperties(Client client, Map<String, ? extends @NonNull Object> properties)
+			throws RemoteException;
 	
-	RjsComObject runMainLoop(Client client, RjsComObject com) throws RemoteException;
-	RjsComObject runAsync(Client client, RjsComObject com) throws RemoteException;
+	RjsComObject runMainLoop(Client client, RjsComObject com)
+			throws RemoteException;
+	RjsComObject runAsync(Client client, RjsComObject com)
+			throws RemoteException;
 	
 }
diff --git a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srvext/Client.java b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srvext/Client.java
index cffa287..2f9d0e2 100644
--- a/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srvext/Client.java
+++ b/core/org.eclipse.statet.rj.server/src/org/eclipse/statet/rj/server/srvext/Client.java
@@ -37,4 +37,11 @@
 		return this.username;
 	}
 	
+	
+	@Override
+	public String toString() {
+		return String.format("Client %1$08X (slot= %4$s) '%2$s'",
+				hashCode(), this.username, this.clientId, this.slot );
+	}
+	
 }