catch up with branch daily

Signed-off-by: Ralf Mollik <ramollik@compex-commerce.com>
diff --git a/org.eclipse.osbp.abstractstatemachine/META-INF/MANIFEST.MF b/org.eclipse.osbp.abstractstatemachine/META-INF/MANIFEST.MF
index 18b8a6b..8c9254b 100644
--- a/org.eclipse.osbp.abstractstatemachine/META-INF/MANIFEST.MF
+++ b/org.eclipse.osbp.abstractstatemachine/META-INF/MANIFEST.MF
@@ -21,7 +21,14 @@
  org.eclipse.osbp.vaadin.addons.keyevents;bundle-version="0.9.0",
  org.jsoup;bundle-version="1.8.3",
  org.apache.commons.lang3,
- javax.persistence;bundle-version="2.1.0"
+ javax.persistence;bundle-version="2.1.0",
+ org.apache.cxf.cxf-core;bundle-version="3.1.7",
+ org.apache.httpcomponents.httpclient;bundle-version="4.3.6",
+ org.apache.httpcomponents.httpcore;bundle-version="4.3.3",
+ org.apache.commons.codec,
+ com.fasterxml.jackson.core.jackson-annotations;bundle-version="2.6.0",
+ com.fasterxml.jackson.core.jackson-core;bundle-version="2.6.2",
+ com.fasterxml.jackson.core.jackson-databind;bundle-version="2.6.2"
 Import-Package: com.vaadin.ui,
  javax.servlet;version="2.6.0",
  javax.servlet.http;version="2.6.0",
diff --git a/org.eclipse.osbp.abstractstatemachine/src/org/eclipse/osbp/abstractstatemachine/AbstractPeripheralService.java b/org.eclipse.osbp.abstractstatemachine/src/org/eclipse/osbp/abstractstatemachine/AbstractPeripheralService.java
index 29a5831..8f007df 100644
--- a/org.eclipse.osbp.abstractstatemachine/src/org/eclipse/osbp/abstractstatemachine/AbstractPeripheralService.java
+++ b/org.eclipse.osbp.abstractstatemachine/src/org/eclipse/osbp/abstractstatemachine/AbstractPeripheralService.java
@@ -15,16 +15,39 @@
  */
 package org.eclipse.osbp.abstractstatemachine;
 
+import java.awt.Graphics2D;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.net.MalformedURLException;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import org.eclipse.core.runtime.FileLocator;
+import javax.imageio.ImageIO;
+import javax.swing.Timer;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.ParseException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.util.EntityUtils;
 import org.eclipse.osbp.preferences.ProductConfiguration;
 import org.eclipse.osbp.ui.api.message.MessageEvent;
 import org.eclipse.osbp.ui.api.message.MessageEvent.EventType;
@@ -36,9 +59,10 @@
 import org.eclipse.osbp.ui.api.statemachine.IPeripheral;
 import org.eclipse.osbp.ui.api.statemachine.IStateMachine;
 import org.eclipse.osbp.ui.api.themes.IThemeResourceService.ThemeResourceType;
-import org.osgi.framework.FrameworkUtil;
 
-import com.vaadin.server.ExternalResource;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.fasterxml.jackson.databind.type.SimpleType;
 import com.vaadin.ui.Audio;
 import com.vaadin.ui.Video;
 
@@ -54,7 +78,6 @@
 import jpos.POSPrinterConst;
 import jpos.POSPrinterControl114;
 import jpos.config.JposEntryRegistry;
-import jpos.epson.EpsonLineDisplayConst;
 import jpos.epson.EpsonPOSPrinterConst;
 import jpos.events.ErrorListener;
 import jpos.events.OutputCompleteEvent;
@@ -70,12 +93,11 @@
  * PeripheralService is a bridge between javaPOS and the state machine
  * participant.
  */
-public abstract class AbstractPeripheralService extends AbstractStateMachineParticipant	implements IPeripheral, IPeripheral.Command, StatusUpdateListener, ErrorListener, OutputCompleteListener, ISignatureListener {
+public abstract class AbstractPeripheralService extends AbstractStateMachineParticipant	implements IPeripheral, IPeripheral.Command, 
+								StatusUpdateListener, ErrorListener, OutputCompleteListener, ISignatureListener, ActionListener {
+	private static final String CASH_DRAWER_CONST = "CashDrawerConst";
 
-	/**
-	 * 
-	 */
-	private static final long serialVersionUID = 5490886401274716894L;
+	private static final String POS_PRINTER_CONST = "POSPrinterConst";
 
 	/** The devices. */
 	protected Map<String, BaseControl> devices = new HashMap<>();
@@ -97,8 +119,8 @@
 
 	/** is init done correctly? */
 	protected boolean initDone = false;
-	
 	/** The pen data supplied by signature pad. */
+	
 	private List<PenData> penData = new ArrayList<>();
 	
 	/** The beeper. */
@@ -109,7 +131,28 @@
 	
 	/** The video. */
 	protected Video video;
+
+	/** if javapos is installed remotely. */
+	protected boolean isRemote = false;
+
+	/** The remote host. */
+	protected String remoteHost;
 	
+	/** The remote port. */
+	protected int remotePort = 9090;
+	
+	/** The devices status poll. */
+	private Timer statusPoll;
+
+	/** The signature status poll. */
+	private Timer signatureStatusPoll;
+	
+	/** The fields of CashDrawerConst. */
+	Field[] cashDrawerFields = CashDrawerConst.class.getDeclaredFields();
+	
+	/** The pos printer fields. */
+	Field[] posPrinterFields = POSPrinterConst.class.getDeclaredFields();
+
 	@Override
 	public boolean equals(Object obj) {
 		return super.equals(obj);
@@ -143,30 +186,145 @@
 	 */
 	@Override 
 	public void init() {
-		LOGGER.debug("Library path=" + System.getProperty("java.library.path"));
-		// load all names of configured devices, instantiate and cache it
-		String configFile = ProductConfiguration.getJavaPosConfiguration();
-		if (configFile == null || configFile.length() == 0) {
-			LOGGER.debug("POS setupfile is not registered in product preferences");
-			return;
+		isRemote = statemachine.isJavaPosRemote();
+		if(!isRemote) {
+			LOGGER.debug("Library path=" + System.getProperty("java.library.path"));
+			// load all names of configured devices, instantiate and cache it
+			String configFile = ProductConfiguration.getJavaPosConfiguration();
+			if (configFile == null || configFile.length() == 0) {
+				LOGGER.debug("POS setupfile is not registered in product preferences");
+				return;
+			}
+			try {
+				new URL(configFile);
+				System.setProperty(JposPropertiesConst.JPOS_POPULATOR_FILE_URL_PROP_NAME, configFile);
+			} catch (MalformedURLException e) {
+				System.setProperty(JposPropertiesConst.JPOS_POPULATOR_FILE_PROP_NAME, configFile);
+			}
+			System.setProperty(JposPropertiesConst.JPOS_REG_POPULATOR_CLASS_PROP_NAME,
+					"jpos.config.simple.xml.SimpleXmlRegPopulator");
+			System.setProperty(JposPropertiesConst.JPOS_SERVICE_MANAGER_CLASS_PROP_NAME,
+					"jpos.loader.simple.SimpleServiceManager");
+			JposProperties jposProperties = new DefaultProperties();
+			jposProperties.loadJposProperties();
+			SimpleServiceManager serviceManager = new SimpleServiceManager(jposProperties);
+			serviceManager.getEntryRegistry().load();
+			props = serviceManager.getEntryRegistry();
+		} else {
+			LOGGER.debug("Remote POS server active");
+			initDone = true;
+			remoteHost = statemachine.getHostName();
+			remotePort = statemachine.getRemotePort();
+			URIBuilder builder = new URIBuilder();
+			builder.setScheme("http").setHost(remoteHost).setPort(remotePort);
+			if(statusPoll == null) {
+				statusPoll = new Timer(1000, this);
+				statusPoll.start();
+			} else if(!statusPoll.isRunning()){
+				statusPoll.restart();
+			}
 		}
-		try {
-			new URL(configFile);
-			System.setProperty(JposPropertiesConst.JPOS_POPULATOR_FILE_URL_PROP_NAME, configFile);
-		} catch (MalformedURLException e) {
-			System.setProperty(JposPropertiesConst.JPOS_POPULATOR_FILE_PROP_NAME, configFile);
-		}
-		System.setProperty(JposPropertiesConst.JPOS_REG_POPULATOR_CLASS_PROP_NAME,
-				"jpos.config.simple.xml.SimpleXmlRegPopulator");
-		System.setProperty(JposPropertiesConst.JPOS_SERVICE_MANAGER_CLASS_PROP_NAME,
-				"jpos.loader.simple.SimpleServiceManager");
-		JposProperties jposProperties = new DefaultProperties();
-		jposProperties.loadJposProperties();
-		SimpleServiceManager serviceManager = new SimpleServiceManager(jposProperties);
-		serviceManager.getEntryRegistry().load();
-		props = serviceManager.getEntryRegistry();
 	}
 
+	private String doHttpGet(String path) {
+		return doHttpGet(path, null, null, null, null);
+	}
+	
+	private String doHttpGet(String path, String paraName1, String para1, String paraName2, String para2) {
+		String responseString = null;
+		URIBuilder builder = new URIBuilder();
+		builder.setScheme("http").setHost(remoteHost).setPort(remotePort);
+		builder.setPath(path);
+		builder.clearParameters();
+		if(paraName1 != null && para1 != null) {
+			builder.addParameter(paraName1, para1);
+		}
+		if(paraName2 != null && para2 != null) {
+			builder.addParameter(paraName2, para2);
+		}
+		try {
+			HttpGet get = new HttpGet(builder.build());
+			get.addHeader("Accept", "application/json");
+			CloseableHttpClient httpClient = HttpClientBuilder.create().build();
+			CloseableHttpResponse response = httpClient.execute(get);
+			responseString = EntityUtils.toString(response.getEntity());
+			get.releaseConnection();
+		} catch (URISyntaxException | ParseException | IOException e) {
+			LOGGER.error("{}", e);
+		}
+		return responseString;
+	}
+
+	private void doHttpPut(String path, String paraName1, String para1) {
+		doHttpPut(path, paraName1, para1, null, null);
+	}
+	
+	private void doHttpPut(String path, String paraName1, String para1, String paraName2, String para2) {
+		URIBuilder builder = new URIBuilder(); 
+		builder.setScheme("http").setHost(remoteHost).setPort(remotePort);
+		builder.setPath(path);
+		builder.clearParameters();
+		if(paraName1 != null && para1 != null) {
+			builder.addParameter(paraName1, para1);
+		}
+		if(paraName2 != null && para2 != null) {
+			builder.addParameter(paraName2, para2);
+		}
+		try {
+			HttpPut put = new HttpPut(builder.build());
+			put.addHeader("Accept", "application/json");
+			CloseableHttpClient httpClient = HttpClientBuilder.create().build();
+			CloseableHttpResponse response = httpClient.execute(put);
+			LOGGER.debug(EntityUtils.toString(response.getEntity()));
+			put.releaseConnection();
+		} catch (URISyntaxException | ParseException | IOException e) {
+			LOGGER.error("{}", e);
+		}
+	}
+
+	private void doHttpPost(String path) {
+		doHttpPost(path, null, null);
+	}
+	
+	private void doHttpPost(String path, String paraName, String para) {
+		doHttpPost(path, paraName, para, null);
+	}
+	
+	private void doHttpPost(String path, String paraName, String para, byte[] image) {
+		URIBuilder builder = new URIBuilder();
+		builder.setScheme("http").setHost(remoteHost).setPort(remotePort);
+		builder.setPath(path);
+		try {
+			HttpPost post = new HttpPost(builder.build());
+			post.addHeader("Accept", "application/json");
+			if(paraName != null && para != null || image != null) {
+				MultipartEntityBuilder mpeBuilder = MultipartEntityBuilder.create();
+				mpeBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
+				if(paraName != null && para != null) {
+					mpeBuilder.addTextBody(paraName, para);
+				}
+				if(image != null) {
+					mpeBuilder.addBinaryBody("image", image, ContentType.MULTIPART_FORM_DATA, "");
+				}
+				post.setEntity(mpeBuilder.build());
+			}
+			CloseableHttpClient httpClient = HttpClientBuilder.create().build();
+			CloseableHttpResponse response = httpClient.execute(post);
+			LOGGER.debug(EntityUtils.toString(response.getEntity()));
+			post.releaseConnection();
+		} catch (URISyntaxException | ParseException | IOException e) {
+			LOGGER.error("{}", e);
+		}
+	}
+	
+	public void releaseDevices() {
+		if(statusPoll != null && statusPoll.isRunning()) {
+			statusPoll.stop();
+		}
+		if(signatureStatusPoll != null && signatureStatusPoll.isRunning()) {
+			signatureStatusPoll.stop();
+		}
+	}
 	/*
 	 * (non-Javadoc)
 	 * 
@@ -187,7 +345,11 @@
 	 *            the new display line
 	 */
 	public void setDisplayLine(String deviceName, String displayLine) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/displayLine", "deviceName", deviceName, "displayLine", displayLine);
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		String[] parts = displayLine.split("\\|");
@@ -205,113 +367,56 @@
 	}
 
 	/**
-	 * Registers a bitmap.
+	 * Registers a bitmap to all printers.
 	 *
-	 * @param deviceName
-	 *            the device name
 	 * @param registerBitmap
 	 *            encodes a pipe separated tuple bitmapNumber, bitmapFilename
 	 */
-	public void setPrinterBitmap(String deviceName, String registerBitmap) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+	public void setPrinterBitmap(String registerId, String registerBitmapId) {
+		if(isRemote) {
+			byte[] img = statemachine.getBlobService().getByteArrayImage(registerBitmapId, 0);
+			doHttpPost("/devices/printerBitmap", "registerId", registerId, img);
+			return;
+			
+		} else if (!initDone) {
 			return;
 		}
-		String[] parts = registerBitmap.split("\\|");
-		String path = null;
-		File file = null;
+    	// load the image
+		BufferedImage img = statemachine.getBlobService().getBufferedImage(registerBitmapId, 0);
+		// convert it to black/white
+        BufferedImage blackWhite = new BufferedImage(400, 180, BufferedImage.TYPE_BYTE_BINARY);
+        Graphics2D g2d = blackWhite.createGraphics();
+        g2d.drawImage(img, 0, 0, 400, 180, 0, 0, img.getWidth(), img.getHeight(), null);
+        g2d.dispose();
+		
+        // write file in temp dir
+		File tempFile = null;
 		try {
-			// get absolute physical path of bundle
-			file = FileLocator.getBundleFile(FrameworkUtil.getBundle(this.getClass()));
-		} catch (IOException e) {
-			LOGGER.error("jpos error setRegisterBitmap:{} ex:{}", registerBitmap, e);
+			tempFile = File.createTempFile("printerBitmap", ".bmp");
+			ImageIO.write(blackWhite, "BMP", tempFile);
+		} catch (IOException ioex) {
+			LOGGER.error("{}", ioex);
 			return;
 		}
-		if (file != null) {
-			path = file.getAbsolutePath() + "/" + parts[1];
-		}
-		if (path != null) {
+		
+		for(BaseControl device:devices.values()) {
+			String deviceName = "";
 			try {
-				if (((POSPrinterControl114) devices.get(deviceName)).getCapRecBitmap()) {
-					int lineWidth = ((POSPrinterControl114) devices.get(deviceName)).getRecLineWidth();
-					((POSPrinterControl114) devices.get(deviceName)).setBitmap(new Integer(parts[0]),
-							POSPrinterConst.PTR_S_RECEIPT, path, lineWidth / 2, POSPrinterConst.PTR_BM_CENTER);
+				deviceName = device.getPhysicalDeviceName();
+				if(device instanceof POSPrinter && ((POSPrinterControl114) device).getCapRecBitmap()) {
+					int lineWidth = ((POSPrinterControl114) device).getRecLineWidth();
+					((POSPrinterControl114) device).setBitmap(new Integer(registerId),
+							POSPrinterConst.PTR_S_RECEIPT, tempFile.getPath(), lineWidth / 2, POSPrinterConst.PTR_BM_CENTER);
 				}
-			} catch (JposException e) {
-				LOGGER.error("jpos error setRegisterBitmap for device:{} and:{} ex:{}", deviceName, registerBitmap, e);
+			} catch (JposException jex) {
+				LOGGER.error("jpos error setRegisterBitmap for device:{} and:{} ex:{}", deviceName, registerBitmapId, jex);
 			} catch (Exception ex) {
-				LOGGER.error("general error registerBitmap for device:{} text:{} ex:{}", deviceName, registerBitmap,
-						ex);
+				LOGGER.error("general error registerBitmap for device:{} text:{} ex:{}", deviceName, registerBitmapId,ex);
 			}
 		}
 	}
 
 	/**
-	 * Registers a bitmap.
-	 *
-	 * @param deviceName
-	 *            the device name
-	 * @param registerBitmap
-	 *            encodes a pipe separated tuple bitmapNumber, bitmapFilename
-	 */
-	public void setDisplayBitmap(String deviceName, String registerBitmap) {
-		if (!initDone || !devices.containsKey(deviceName)) {
-			return;
-		}
-		String[] parts = registerBitmap.split("\\|");
-		String path = null;
-		File file = null;
-		try {
-			// get absolute physical path of bundle
-			file = FileLocator.getBundleFile(FrameworkUtil.getBundle(this.getClass()));
-		} catch (IOException e) {
-			LOGGER.error("jpos error setRegisterBitmap for device:{} and:{} ex:{}", deviceName, registerBitmap, e);
-			return;
-		}
-		if (file != null) {
-			path = file.getAbsolutePath() + "/" + parts[1];
-		}
-		if (path != null) {
-			try {
-				if (((LineDisplayControl114) devices.get(deviceName)).getCapBitmap()) {
-					((LineDisplayControl114) devices.get(deviceName)).setBitmap(new Integer(parts[0]), parts[1],
-							LineDisplayConst.DISP_BM_ASIS, LineDisplayConst.DISP_BM_LEFT, LineDisplayConst.DISP_BM_TOP);
-				}
-			} catch (JposException e) {
-				LOGGER.error("jpos error setDisplayBitmap for device:{} and:{} ex:{}", deviceName, registerBitmap, e);
-			} catch (Exception ex) {
-				LOGGER.error("jpos error setDisplayBitmap for device:{} and:{} genericex:{}", deviceName,
-						registerBitmap, ex);
-			}
-		}
-	}
-
-	/**
-	 * Sets the display bitmap.
-	 *
-	 * @param deviceName
-	 *            the device name
-	 * @param displayBitmap
-	 *            the new display bitmap
-	 */
-	public void setDisplayBitmap(String deviceName, Integer displayBitmap) {
-		if (!initDone || !devices.containsKey(deviceName)) {
-			return;
-		}
-		String text = String.format("\\u%03db|%dB", displayBitmap, displayBitmap);
-		try {
-			int[] pram2 = new int[1];
-			Object pram3 = new Object();
-
-			((LineDisplayControl114) devices.get(deviceName)).directIO(EpsonLineDisplayConst.DISP_DI_GRAPHIC, pram2,
-					pram3);
-			((LineDisplayControl114) devices.get(deviceName)).createWindow(0, 0, 64, 256, 64, 256);
-			((LineDisplayControl114) devices.get(deviceName)).displayText(text, LineDisplayConst.DISP_DT_NORMAL);
-		} catch (JposException e) {
-			LOGGER.error("jpos error setDisplayBitmap for device:{} and:{} ex:{}", deviceName, displayBitmap, e);
-		}
-	}
-
-	/**
 	 * Sets the device brightness.
 	 *
 	 * @param deviceName
@@ -320,7 +425,11 @@
 	 *            the new device brightness
 	 */
 	public void setDeviceBrightness(String deviceName, Integer deviceBrightness) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/deviceBrightness", "deviceName", deviceName, "deviceBrightness", deviceBrightness.toString());
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -339,7 +448,11 @@
 	 *            the new blink rate
 	 */
 	public void setBlinkRate(String deviceName, Integer blinkRate) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/blinkRate", "deviceName", deviceName, "blinkRate", blinkRate.toString());
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -358,7 +471,11 @@
 	 *            see: LineDisplayConst
 	 */
 	public void setCursorType(String deviceName, Integer cursorType) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/cursorType", "deviceName", deviceName, "cursorType", cursorType.toString());
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -377,7 +494,11 @@
 	 *            the new marquee format
 	 */
 	public void setMarqueeFormat(String deviceName, Integer marqueeFormat) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/marqueeFormat", "deviceName", deviceName, "marqueeFormat", marqueeFormat.toString());
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -396,7 +517,11 @@
 	 *            the new marquee repeat wait
 	 */
 	public void setMarqueeRepeatWait(String deviceName, Integer marqueeRepeatWait) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/marqueeRepeatWait", "deviceName", deviceName, "marqueeRepeatWait", marqueeRepeatWait.toString());
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -415,7 +540,11 @@
 	 *            the new marquee type
 	 */
 	public void setMarqueeType(String deviceName, Integer marqueeType) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/marqueeType", "deviceName", deviceName, "marqueeType", marqueeType.toString());
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -434,7 +563,11 @@
 	 *            the new marquee unit wait
 	 */
 	public void setMarqueeUnitWait(String deviceName, Integer marqueeUnitWait) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/marqueeUnitWait", "deviceName", deviceName, "marqueeUnitWait", marqueeUnitWait.toString());
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -454,7 +587,11 @@
 	 *            the new creates the window
 	 */
 	public void setCreateWindow(String deviceName, String createWindow) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/createWindow", "deviceName", deviceName, "createWindow", createWindow);
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		String[] parts = createWindow.split("\\|");
@@ -479,7 +616,11 @@
 	 *            the new destroy window
 	 */
 	public void setDestroyWindow(String deviceName, String destroyWindow) { // NOSONAR
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/destroyWindow", "deviceName", deviceName, "destroyWindow", destroyWindow);
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -501,7 +642,11 @@
 	 *            encodes a pipe separated tuple: direction, units
 	 */
 	public void setScroll(String deviceName, String scroll) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/scroll", "deviceName", deviceName, "scroll", scroll);
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		String[] parts = scroll.split("\\|");
@@ -524,7 +669,11 @@
 	 *            the new inter character wait
 	 */
 	public void setInterCharacterWait(String deviceName, Integer interCharacterWait) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/interCharacterWait", "deviceName", deviceName, "interCharacterWait", interCharacterWait.toString());
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -544,7 +693,11 @@
 	 *            attribute
 	 */
 	public void setDisplayTextAt(String deviceName, String displayTextAt) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/displayTextAt", "deviceName", deviceName, "displayTextAt", displayTextAt);
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		String[] parts = displayTextAt.split("\\|");
@@ -572,7 +725,11 @@
 	 *            is nt used
 	 */
 	public void setClearDisplay(String deviceName, String clearDisplay) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/clearDisplay", "deviceName", deviceName, "clearDisplay", clearDisplay);
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -595,7 +752,11 @@
 	 *            is ignored
 	 */
 	public void setOpenDrawer(String deviceName, Integer openDrawer) { // NOSONAR
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/openDrawer", "deviceName", deviceName, "openDrawer", openDrawer.toString());
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -614,7 +775,11 @@
 	 *            text to print
 	 */
 	public void setPrintNormal(String deviceName, String printNormal) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/printNormal", "deviceName", deviceName, "printNormal", printNormal);
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -633,7 +798,11 @@
 	 *            the text to print as footer
 	 */
 	public void setPrintCut(String deviceName, String footer) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/printCut", "deviceName", deviceName, "printCut", footer);
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		try {
@@ -660,7 +829,11 @@
 	 *            the bitmap-id to print
 	 */
 	public void setPrintBitmap(String deviceName, Integer printBitmap) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/printBitmap", "deviceName", deviceName, "printBitmap", printBitmap.toString());
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		String text = String.format("\u001b|%dB", printBitmap);
@@ -681,20 +854,6 @@
 	}
 
 	/**
-	 * show a pre-loaded bitmap.
-	 *
-	 * @param deviceName
-	 *            the device name
-	 * @param displayBitmap
-	 *            the display bitmap
-	 */
-	public void setShowBitmap(String deviceName, Integer displayBitmap) { // NOSONAR
-		if (!initDone || !devices.containsKey(deviceName)) {
-			return;
-		}
-	}
-
-	/**
 	 * prints a barcode.
 	 *
 	 * @param deviceName
@@ -704,7 +863,11 @@
 	 *            jpos.POSPrinterConst
 	 */
 	public void setPrintBarcode(String deviceName, String data) {
-		if (!initDone || !devices.containsKey(deviceName)) {
+		if(isRemote) {
+			doHttpPut("/devices/printBarcode", "deviceName", deviceName, "printBarcode", data);
+			return;
+			
+		} else if (!initDone || !devices.containsKey(deviceName)) {
 			return;
 		}
 		String[] parts = data.split("\\|");
@@ -733,7 +896,12 @@
 	 *            the new slip notifications enabled
 	 */
 	public void setSlipNotificationsEnabled(boolean slipNotificationsEnabled) {
-		this.slipNotificationsEnabled = slipNotificationsEnabled;
+		if(isRemote) {
+			doHttpPut("/devices/slipNotificationsEnabled", "slipNotificationsEnabled", Boolean.toString(slipNotificationsEnabled));
+			return;
+		} else {
+			this.slipNotificationsEnabled = slipNotificationsEnabled;
+		}
 	}
 
 	/**
@@ -742,9 +910,14 @@
 	 * @return true, if is slip notifications enabled
 	 */
 	public boolean isSlipNotificationsEnabled() {
-		return slipNotificationsEnabled;
+		if(isRemote) {
+			return Boolean.getBoolean(doHttpGet("/devices/isSlipNotificationsEnabled"));
+		} else {
+			return slipNotificationsEnabled;
+		}
 	}
 
+	
 	/*
 	 * (non-Javadoc)
 	 * 
@@ -755,171 +928,179 @@
 	public void statusUpdateOccurred(StatusUpdateEvent e) { // NOSONAR
 		Object source = e.getSource();
 		if (source instanceof CashDrawer) {
-			switch (e.getStatus()) {
-			case CashDrawerConst.CASH_SUE_DRAWERCLOSED: // Drawer is closed.
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGECLOSE, "drawerOpen"));
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.TRIGGER, "onDrawerClosed"));
-				}
-				break;
-			case CashDrawerConst.CASH_SUE_DRAWEROPEN: // Drawer is opened.
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGEOPEN, "drawerOpen"));
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.TRIGGER, "onDrawerOpened"));
-				}
-				break;
-			default:
-				break;
-			}
+			decodeCashDrawerStatus(e.getStatus());
 		}
 		if (source instanceof POSPrinter) {
-			switch (e.getStatus()) {
-			case POSPrinterConst.PTR_SUE_COVER_OPEN:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGEOPEN, "coverOpen"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_COVER_OK:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGECLOSE, "coverOpen"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_REC_EMPTY:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGEOPEN, "receiptEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_REC_PAPEROK:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGECLOSE, "receiptEmpty"));
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSCLOSE, "receiptNearEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_REC_NEAREMPTY:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.STATUSOPEN, "receiptNearEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_SLP_EMPTY:
-				if (statemachine != null && slipNotificationsEnabled) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGEOPEN, "slipEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_SLP_PAPEROK:
-				if (statemachine != null && slipNotificationsEnabled) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGECLOSE, "slipEmpty"));
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.STATUSCLOSE, "slipNearEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_SLP_NEAREMPTY:
-				if (statemachine != null && slipNotificationsEnabled) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.STATUSOPEN, "slipNearEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_JRN_EMPTY:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGEOPEN, "journalEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_JRN_PAPEROK:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGECLOSE, "journalEmpty"));
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSCLOSE, "journalNearEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_JRN_NEAREMPTY:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.STATUSOPEN, "journalNearEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_REC_CARTRIDGE_EMPTY:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.MESSAGEOPEN, "receiptCartridgeEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_REC_CARTRIDGE_NEAREMPTY:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSOPEN, "receiptCartridgeNearEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_REC_HEAD_CLEANING:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSOPEN, "receiptHeadCleaning"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_REC_CARTDRIGE_OK:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.MESSAGECLOSE, "receiptCartridgeEmpty"));
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSCLOSE, "receiptHeadCleaning"));
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSCLOSE, "receiptCartridgeNearEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_SLP_CARTRIDGE_EMPTY:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.MESSAGEOPEN, "slipCartridgeEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_SLP_CARTRIDGE_NEAREMPTY:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSOPEN, "slipCartridgeNearEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_SLP_HEAD_CLEANING:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine, new MessageEvent(EventType.STATUSOPEN, "slipHeadCleaning"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_SLP_CARTDRIGE_OK:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.MESSAGECLOSE, "slipCartridgeEmpty"));
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSCLOSE, "slipHeadCleaning"));
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSCLOSE, "slipCartridgeNearEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_JRN_CARTRIDGE_EMPTY:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.MESSAGEOPEN, "journalCartridgeEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_JRN_CARTRIDGE_NEAREMPTY:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSOPEN, "journalCartridgeNearEmpty"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_JRN_HEAD_CLEANING:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSOPEN, "journalHeadCleaning"));
-				}
-				break;
-			case POSPrinterConst.PTR_SUE_JRN_CARTDRIGE_OK:
-				if (statemachine != null) {
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.MESSAGECLOSE, "journalCartridgeEmpty"));
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSCLOSE, "journalHeadCleaning"));
-					statemachine.processEvent(statemachine,
-							new MessageEvent(EventType.STATUSCLOSE, "journalCartridgeNearEmpty"));
-				}
-				break;
-			default:
-				break;
+			decodePOSPrinterStatus(e.getStatus());
+		}
+	}
+
+	private void decodePOSPrinterStatus(int status) {
+		switch (status) {
+		case POSPrinterConst.PTR_SUE_COVER_OPEN:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGEOPEN, "coverOpen"));
 			}
+			break;
+		case POSPrinterConst.PTR_SUE_COVER_OK:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGECLOSE, "coverOpen"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_REC_EMPTY:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGEOPEN, "receiptEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_REC_PAPEROK:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGECLOSE, "receiptEmpty"));
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSCLOSE, "receiptNearEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_REC_NEAREMPTY:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.STATUSOPEN, "receiptNearEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_SLP_EMPTY:
+			if (statemachine != null && slipNotificationsEnabled) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGEOPEN, "slipEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_SLP_PAPEROK:
+			if (statemachine != null && slipNotificationsEnabled) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGECLOSE, "slipEmpty"));
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.STATUSCLOSE, "slipNearEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_SLP_NEAREMPTY:
+			if (statemachine != null && slipNotificationsEnabled) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.STATUSOPEN, "slipNearEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_JRN_EMPTY:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGEOPEN, "journalEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_JRN_PAPEROK:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGECLOSE, "journalEmpty"));
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSCLOSE, "journalNearEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_JRN_NEAREMPTY:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.STATUSOPEN, "journalNearEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_REC_CARTRIDGE_EMPTY:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.MESSAGEOPEN, "receiptCartridgeEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_REC_CARTRIDGE_NEAREMPTY:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSOPEN, "receiptCartridgeNearEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_REC_HEAD_CLEANING:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSOPEN, "receiptHeadCleaning"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_REC_CARTDRIGE_OK:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.MESSAGECLOSE, "receiptCartridgeEmpty"));
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSCLOSE, "receiptHeadCleaning"));
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSCLOSE, "receiptCartridgeNearEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_SLP_CARTRIDGE_EMPTY:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.MESSAGEOPEN, "slipCartridgeEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_SLP_CARTRIDGE_NEAREMPTY:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSOPEN, "slipCartridgeNearEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_SLP_HEAD_CLEANING:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.STATUSOPEN, "slipHeadCleaning"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_SLP_CARTDRIGE_OK:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.MESSAGECLOSE, "slipCartridgeEmpty"));
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSCLOSE, "slipHeadCleaning"));
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSCLOSE, "slipCartridgeNearEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_JRN_CARTRIDGE_EMPTY:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.MESSAGEOPEN, "journalCartridgeEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_JRN_CARTRIDGE_NEAREMPTY:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSOPEN, "journalCartridgeNearEmpty"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_JRN_HEAD_CLEANING:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSOPEN, "journalHeadCleaning"));
+			}
+			break;
+		case POSPrinterConst.PTR_SUE_JRN_CARTDRIGE_OK:
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.MESSAGECLOSE, "journalCartridgeEmpty"));
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSCLOSE, "journalHeadCleaning"));
+				statemachine.processEvent(statemachine,
+						new MessageEvent(EventType.STATUSCLOSE, "journalCartridgeNearEmpty"));
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	private void decodeCashDrawerStatus(int status) {
+		switch (status) {
+		case CashDrawerConst.CASH_SUE_DRAWERCLOSED: // Drawer is closed.
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGECLOSE, "drawerOpen"));
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.TRIGGER, "onDrawerClosed"));
+			}
+			break;
+		case CashDrawerConst.CASH_SUE_DRAWEROPEN: // Drawer is opened.
+			if (statemachine != null) {
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.MESSAGEOPEN, "drawerOpen"));
+				statemachine.processEvent(statemachine, new MessageEvent(EventType.TRIGGER, "onDrawerOpened"));
+			}
+			break;
+		default:
+			break;
 		}
 	}
 
@@ -930,7 +1111,11 @@
 	 */
 	@Override
 	public void errorOccurred(jpos.events.ErrorEvent e) { // NOSONAR
-		switch (e.getErrorCodeExtended()) {
+		decodePOSPrinterError(e.getErrorCodeExtended());
+	}
+
+	private void decodePOSPrinterError(int error) {
+		switch (error) {
 		case POSPrinterConst.JPOS_EPTR_COVER_OPEN:
 			if (statemachine != null) {
 				statemachine.processEvent(statemachine, new MessageEvent(EventType.ERROR, "errorCoverOpen"));
@@ -1194,7 +1379,7 @@
 	 * @param reportParameters
 	 *            the new prints the report
 	 */
-	public void setPrintReport(String reportParameters) {
+	public void setPrintReport(String reportParameters, String printService) {
 		if (statemachine.getReportProvider() != null) {
 			String[] parts = reportParameters.split("\\|");
 			String reportName = parts[0];
@@ -1220,12 +1405,20 @@
 					}
 				}
 			}
-			statemachine.getReportProvider().printReportAsPdf(reportName, statemachine.getEclipseContext(), filterMap);
+			statemachine.getReportProvider().printReportAsPdf(reportName, statemachine.getEclipseContext(), filterMap, printService);
 		}
 	}
 	
 	public void setSignatureOpen(String dummy) {
-		if(POSServiceBinder.getSignatureService() != null) {
+		if(isRemote) {
+			doHttpPost("/signature/openTablet");
+			if(signatureStatusPoll == null) {
+				signatureStatusPoll = new Timer(1000, this);
+				signatureStatusPoll.start();
+			} else if(!statusPoll.isRunning()){
+				signatureStatusPoll.restart();
+			}
+		} else if(POSServiceBinder.getSignatureService() != null) {
 			if(POSServiceBinder.getSignatureService().openTablet(statemachine.getBlobService())) {
 				statemachine.schedule(statemachine, 500, new MessageEvent(EventType.TRIGGER, "onSignaturePadOpen"));
 			}
@@ -1233,46 +1426,65 @@
 	}
 
 	public void setSignatureCapture(String dummy) {
-		if(POSServiceBinder.getSignatureService() != null) {
+		if(isRemote) {
+			doHttpPost("/signature/captureTablet");
+		} else if(POSServiceBinder.getSignatureService() != null) {
 			POSServiceBinder.getSignatureService().captureTablet();
 			POSServiceBinder.getSignatureService().addListener(this);
 		}
 	}
 	
 	public void setSignatureIdle(String dummy) {
-		if(POSServiceBinder.getSignatureService() != null) {
+		if(isRemote) {
+			doHttpPost("/signature/idleTablet");
+		} else if(POSServiceBinder.getSignatureService() != null) {			
 			POSServiceBinder.getSignatureService().removeListener(this);
 			POSServiceBinder.getSignatureService().idleTablet();
 		}
 	}
 	
 	public void setSignatureCaptureImage(String imageId) {
-		if(POSServiceBinder.getSignatureService() != null) {
+		if(isRemote) {
+			byte[] img = statemachine.getBlobService().getByteArrayImage(imageId, 0);
+			doHttpPost("/signature/captureImage", null, null, img);
+		} else if(POSServiceBinder.getSignatureService() != null) {			
 			POSServiceBinder.getSignatureService().setCaptureImage(imageId); 
 		}
 	}
 	
 	public void setSignatureAddSlide(String slideId) {
-		if(POSServiceBinder.getSignatureService() != null) {
+		if(isRemote) {
+			byte[] img = statemachine.getBlobService().getByteArrayImage(slideId, 0);
+			doHttpPost("/signature/addSlide", null, null, img);
+		} else if(POSServiceBinder.getSignatureService() != null) {			
 			POSServiceBinder.getSignatureService().addSlideTablet(slideId); 
 		}
 	}
 	
-	public void setSignatureSlideDelay(int delay) {
-		if(POSServiceBinder.getSignatureService() != null) {
+	public void setSignatureSlideDelay(Integer delay) {
+		if(isRemote) {
+			doHttpPut("/signature/slideDelay", "delay", delay.toString());
+		} else if(POSServiceBinder.getSignatureService() != null) {			
 			POSServiceBinder.getSignatureService().setSlideDelay(delay); 
 		}
 	}
 	
 	public void setSignatureClose(String dummy) {
-		if(POSServiceBinder.getSignatureService() != null) {
+		if(isRemote) {
+			doHttpPost("closeTablet");
+			if(signatureStatusPoll != null && signatureStatusPoll.isRunning()) {
+				signatureStatusPoll.stop();
+			}
+		} else if(POSServiceBinder.getSignatureService() != null) {
 			POSServiceBinder.getSignatureService().removeListener(this);
 			POSServiceBinder.getSignatureService().closeTablet();
 		}
 	}
 
 	public void setSignatureLabel(String labels) {
-		if(POSServiceBinder.getSignatureService() != null) {
+		if(isRemote) {
+			doHttpPut("/signature/addLabels", "labels", labels);
+		} else if(POSServiceBinder.getSignatureService() != null) {			
 			POSServiceBinder.getSignatureService().setTabletLabel(labels);
 		}
 	}
@@ -1301,11 +1513,29 @@
 	}
 	
 	public List<PenData> getPenData() {
+		if(isRemote) {
+			String jsonString =  doHttpGet("/signature/penData");
+			ObjectMapper mapper = new ObjectMapper();
+			try {
+				penData = mapper.readValue(jsonString, CollectionType.construct(List.class, SimpleType.construct(PenData.class)));
+			} catch (IOException e) {
+				LOGGER.error("{}", e);
+			}
+		}
 		return penData;
 	}
 
 	public String getSignatureBlob() {
-		return POSServiceBinder.getSignatureService().getSignatureBlob(statemachine.getSceenWidth(), statemachine.getScreenHeight());
+		if(isRemote) {
+			String base64String =  doHttpGet("/signature/signature", "width", Integer.toString(statemachine.getSceenWidth()), "height", Integer.toString(statemachine.getScreenHeight()));
+			if(base64String != null) {
+				byte[] bytes = Base64.decodeBase64(base64String.getBytes());
+				return statemachine.getBlobService().createBlobMapping(new ByteArrayInputStream(bytes), "", "image/jpeg");
+			}
+		} else if(POSServiceBinder.getSignatureService() != null) {
+			return POSServiceBinder.getSignatureService().getSignatureBlob(statemachine.getSceenWidth(), statemachine.getScreenHeight());
+		}
+		return null;
 	}
 
     public void setBeeper(String parameters) {
@@ -1327,4 +1557,75 @@
 //    	audio.setSources(new ExternalResource("http://www.podtrac.com/pts/redirect.mp3?http://piledriver.hipcast.com/deluge/WrestleManiaX7OSWReview55.mp3"));
     	audio.play();
     }
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		if(e.getSource().equals(statusPoll)) {
+			decodeRemoteState(doHttpGet("/devices/states"));
+			decodeRemoteError(doHttpGet("/devices/errors"));
+		}
+		if(e.getSource().equals(signatureStatusPoll)) {
+			decodeRemoteSignatureState(doHttpGet("/signature/states"));
+		}
+	}
+
+	private void decodeRemoteError(String responseString) {
+		List<String> errors = Arrays.asList(responseString.replace("[","").replace("]", "").replace("\"", "").split(","));
+		for(String error:errors) {
+			if(error.startsWith(POS_PRINTER_CONST)) {
+				for ( Field field : posPrinterFields ) {
+					if(field.getName().equals(error.substring(POS_PRINTER_CONST.length()+1))) {
+						field.setAccessible(true);
+						try {
+							decodePOSPrinterError((int)field.get(error));
+						} catch (IllegalArgumentException | IllegalAccessException e1) {
+							LOGGER.error("{}", e1);
+						}
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	private void decodeRemoteState(String responseString) {
+		List<String> states = Arrays.asList(responseString.replace("[","").replace("]", "").replace("\"", "").split(","));
+		for(String state:states) {
+			if(state.startsWith(CASH_DRAWER_CONST)) {
+				for ( Field field : cashDrawerFields ) {
+				   field.setAccessible(true);
+					if(field.getName().equals(state.substring(CASH_DRAWER_CONST.length()+1))) {
+						field.setAccessible(true);
+						try {
+							decodeCashDrawerStatus((int)field.get(state));
+						} catch (IllegalArgumentException | IllegalAccessException e1) {
+							LOGGER.error("{}", e1);
+						}
+						break;
+					}
+				}
+			} else if(state.startsWith(POS_PRINTER_CONST)) {
+				for ( Field field : posPrinterFields ) {
+					if(field.getName().equals(state.substring(POS_PRINTER_CONST.length()+1))) {
+						field.setAccessible(true);
+						try {
+							decodePOSPrinterStatus((int)field.get(state));
+						} catch (IllegalArgumentException | IllegalAccessException e1) {
+							LOGGER.error("{}", e1);
+						}
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	private void decodeRemoteSignatureState(String responseString) {
+		List<String> states = Arrays.asList(responseString.replace("[","").replace("]", "").replace("\"", "").split(","));
+		for(String state:states) {
+			if(!state.isEmpty()) {
+				statemachine.schedule(statemachine, 500, new MessageEvent(EventType.TRIGGER, state));
+			}
+		}
+	}
 }
diff --git a/org.eclipse.osbp.abstractstatemachine/src/org/eclipse/osbp/abstractstatemachine/AbstractStateMachine.java b/org.eclipse.osbp.abstractstatemachine/src/org/eclipse/osbp/abstractstatemachine/AbstractStateMachine.java
index 3b6568b..7ef81f4 100644
--- a/org.eclipse.osbp.abstractstatemachine/src/org/eclipse/osbp/abstractstatemachine/AbstractStateMachine.java
+++ b/org.eclipse.osbp.abstractstatemachine/src/org/eclipse/osbp/abstractstatemachine/AbstractStateMachine.java
@@ -27,7 +27,6 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -35,11 +34,16 @@
 import javax.annotation.PreDestroy;
 import javax.swing.Timer;
 
+import org.apache.http.ParseException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.util.EntityUtils;
 import org.eclipse.e4.core.contexts.IEclipseContext;
 import org.eclipse.osbp.dsl.common.datatypes.IDto;
 import org.eclipse.osbp.runtime.common.filter.IQuery;
 import org.eclipse.osbp.runtime.common.session.ISession;
-import org.eclipse.osbp.runtime.common.session.ISessionManager.FutureResult;
 import org.eclipse.osbp.ui.api.customfields.IBlobService;
 import org.eclipse.osbp.ui.api.message.IMessageRequester;
 import org.eclipse.osbp.ui.api.message.MessageEvent;
@@ -87,7 +91,7 @@
 	protected SelectableChannel channel;
 	protected String lastTrigger;
 	protected IEclipseContext eclipseContext;
-	protected LinkedBlockingQueue<MessageEvent> queue = new LinkedBlockingQueue<MessageEvent>();
+	protected LinkedBlockingQueue<MessageEvent> queue = new LinkedBlockingQueue<>();
 
 	protected Map<Integer, String> keyMapper = new HashMap<Integer, String>() {
 		private static final long serialVersionUID = 8801421284339992078L;
@@ -122,7 +126,8 @@
 	};
 	private KeyEventExtension extension = null;
 	private AtomicBoolean active = new AtomicBoolean(false);
-	private Thread thread;
+	private AtomicBoolean blocking = new AtomicBoolean(false);
+	private int remotePort = 9090;
 
 	@Override
 	public void installKeyEventConsumer(VerticalLayout layout) {
@@ -155,18 +160,22 @@
 	}
 
 	protected void keyPressed(org.eclipse.osbp.vaadin.addons.keyevents.client.Event event) {
-		if (!Character.isISOControl(event.charCode)) {
+		if (Character.isAlphabetic(event.charCode) || Character.isDigit(event.charCode)) {
+			String evtCode = Character.toString((char) event.charCode);
+			logger.debug("alpha or digit {}", evtCode);
 			processEvent(this, new MessageEvent(EventType.KEYBOARD, Character.toString((char) event.charCode)));
+		} else if (keyMapper.containsKey(event.charCode)) {
+			logger.debug("keymapper code {}", keyMapper.get(event.charCode));
+			processEvent(this, new MessageEvent(EventType.FUNCTIONALKEYBOARD, keyMapper.get(event.charCode)));
 		} else {
-			if (keyMapper.containsKey(event.charCode)) {
-				processEvent(this, new MessageEvent(EventType.FUNCTIONALKEYBOARD, keyMapper.get(event.charCode)));
-			}
+			logger.debug("unprocessed code {}", event.charCode);
 		}
 	}
 
 	public void processEvent(final IStateMachine statemachine, final MessageEvent event) {
 		synchronized (queue) {
-			queue.offer(event);
+			logger.debug("enqueue event {}", event);
+			queue.add(event);
 		}
 	}
 
@@ -514,12 +523,23 @@
 		String hostName = null;
 		try {
 			InetAddress netAddress = InetAddress.getByName(webBrowser.getAddress());
-			hostName = netAddress.getHostName();
+			hostName = netAddress.getHostName().toLowerCase();
+			logger.error("**** browser returned this host-name: {} ****", hostName);
 		} catch (UnknownHostException e) {
 			logger.error("{}", e);
 		}
 		return hostName;
 	}
+	
+	@Override
+	public int getRemotePort() {
+		return remotePort;
+	}
+	
+	@Override
+	public void setRemotePort(int port) {
+		this.remotePort = remotePort;
+	}
 
 	@Override
 	public boolean isItMe(String hostName) {
@@ -527,11 +547,16 @@
 		try {
 			InetAddress netAddress = InetAddress.getByName(hostName);
 			hostAddress = netAddress.getHostAddress();
+			logger.error("**** browser returned this host-address: {} ****", hostAddress);
 		} catch (UnknownHostException e) {
 			logger.error("{}", e);
 			return false;
 		}
-		return getIPAddress().equals(hostAddress);
+		boolean ret = getIPAddress().equals(hostAddress);
+		if(!ret) {
+			logger.error("**** browser host-address and requested host are not equal! Requested: {} ****", hostName);
+		}
+		return ret;
 	}
 
 	@Override
@@ -574,36 +599,39 @@
 	}
 
 	public void start() {
-		// TODO add stop method
 		active.set(true);
 		final UI ui = getEclipseContext().get(UI.class);
 		Runnable runnable = () -> {
 			while (active.get()) {
-				try {
+				if(!blocking.get()) {
 					MessageEvent event;
 					try {
 						event = queue.take();
 						ui.access(() -> {
+							logger.debug("doProcess event {}", event);
 							doProcessEvent(AbstractStateMachine.this, event);
 						});
 					} catch (InterruptedException e) {
+						logger.debug("dequeuing was interrupted {}", e);
+						Thread.currentThread().interrupt();
 					}
-				} catch (Exception e) {
-					logger.error("{}", e);
+				}
+				try {
+					Thread.sleep(10);
+				} catch (InterruptedException e) {
+					// TODO sleep interrupted
 				}
 			}
 		};
 
-		thread = new Thread(runnable, "Statemachine processor " + getClass().getName());
+		Thread thread = new Thread(runnable, "Statemachine processor " + getClass().getName());
 		thread.setDaemon(true);
 		thread.start();
 	}
 
 	@Override
 	public void schedule(IStateMachine statemachine, int milliseconds, MessageEvent event) {
-		Timer timer = new Timer(milliseconds, action -> {
-			statemachine.processEvent(statemachine, event);
-		});
+		Timer timer = new Timer(milliseconds, action -> statemachine.processEvent(statemachine, event));
 		timer.setRepeats(false);
 		timer.start();
 	}
@@ -782,7 +810,7 @@
 	public void sendSlaveData(Map<String, Object> data) {
 		String currentInstanceId = (String) getEclipseContext().get("e4ApplicationInstanceId");
 		List<ISession> sessions = POSServiceBinder.getSessionManager()
-				.getSessions((s) -> s.get("e4ApplicationInstanceId").equals(currentInstanceId));
+				.getSessions(s -> s.get("e4ApplicationInstanceId").equals(currentInstanceId));
 		if (!sessions.isEmpty()) {
 			List<ISession> slaves = sessions.get(0).getSlaves();
 			for (ISession slave : slaves) {
@@ -793,4 +821,28 @@
 			}
 		}
 	}
+	
+	@Override
+	public void blockQueueTaking(boolean block) {
+		logger.debug("switch blocking {}", block);
+		blocking.set(block);
+	}
+	
+	@Override
+	public boolean isJavaPosRemote() {
+		String cmd = String.format("http://%s:%d/devices/posParameters", getHostName(), getRemotePort());
+		HttpGet get = new HttpGet(cmd);
+		get.addHeader("Accept", "application/json");
+		
+		try(CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
+			CloseableHttpResponse response = httpClient.execute(get);
+			logger.info(EntityUtils.toString(response.getEntity()));
+			return true;
+		} catch (ParseException | IOException e) {
+			// it's ok - we have no remote javapos
+		} finally {
+			get.releaseConnection();
+		}
+		return false;
+	}
 }