You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by bf...@apache.org on 2013/02/13 23:04:21 UTC
[23/50] [abbrv] Moved console-proxy into services
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4869f0ca/services/console-proxy/server/scripts/ssvm-check.sh
----------------------------------------------------------------------
diff --git a/services/console-proxy/server/scripts/ssvm-check.sh b/services/console-proxy/server/scripts/ssvm-check.sh
new file mode 100644
index 0000000..a401164
--- /dev/null
+++ b/services/console-proxy/server/scripts/ssvm-check.sh
@@ -0,0 +1,136 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+# Health check script for the Secondary Storage VM
+
+# DNS server is specified.
+
+
+CMDLINE=/var/cache/cloud/cmdline
+for i in `cat $CMDLINE`
+do
+ key=`echo $i | cut -d= -f1`
+ value=`echo $i | cut -d= -f2`
+ case $key in
+ host)
+ MGMTSERVER=$value
+ ;;
+ esac
+done
+
+
+# ping dns server
+echo ================================================
+DNSSERVER=`egrep '^nameserver' /etc/resolv.conf | awk '{print $2}'| head -1`
+echo "First DNS server is " $DNSSERVER
+ping -c 2 $DNSSERVER
+if [ $? -eq 0 ]
+then
+ echo "Good: Can ping DNS server"
+else
+ echo "WARNING: cannot ping DNS server"
+ echo "route follows"
+ route -n
+fi
+
+
+# check dns resolve
+echo ================================================
+nslookup download.cloud.com 1> /tmp/dns 2>&1
+grep 'no servers could' /tmp/dns 1> /dev/null 2>&1
+if [ $? -eq 0 ]
+then
+ echo "ERROR: DNS not resolving download.cloud.com"
+ echo resolv.conf follows
+ cat /etc/resolv.conf
+ exit 2
+else
+ echo "Good: DNS resolves download.cloud.com"
+fi
+
+
+# check to see if we have the NFS volume mounted
+echo ================================================
+mount|grep -v sunrpc|grep nfs 1> /dev/null 2>&1
+if [ $? -eq 0 ]
+then
+ echo "NFS is currently mounted"
+ # check for write access
+ for MOUNTPT in `mount|grep -v sunrpc|grep nfs| awk '{print $3}'`
+ do
+ if [ $MOUNTPT != "/proc/xen" ] # mounted by xen
+ then
+ echo Mount point is $MOUNTPT
+ touch $MOUNTPT/foo
+ if [ $? -eq 0 ]
+ then
+ echo "Good: Can write to mount point"
+ rm $MOUNTPT/foo
+ else
+ echo "ERROR: Cannot write to mount point"
+ echo "You need to export with norootsquash"
+ fi
+ fi
+ done
+else
+ echo "ERROR: NFS is not currently mounted"
+ echo "Try manually mounting from inside the VM"
+ NFSSERVER=`awk '{print $17}' $CMDLINE|awk -F= '{print $2}'|awk -F: '{print $1}'`
+ echo "NFS server is " $NFSSERVER
+ ping -c 2 $NFSSERVER
+ if [ $? -eq 0 ]
+ then
+ echo "Good: Can ping NFS server"
+ else
+ echo "WARNING: cannot ping NFS server"
+ echo routing table follows
+ route -n
+ fi
+fi
+
+
+# check for connectivity to the management server
+echo ================================================
+echo Management server is $MGMTSERVER. Checking connectivity.
+socatout=$(echo | socat - TCP:$MGMTSERVER:8250,connect-timeout=3 2>&1)
+if [ $? -eq 0 ]
+then
+ echo "Good: Can connect to management server port 8250"
+else
+ echo "ERROR: Cannot connect to $MGMTSERVER port 8250"
+ echo $socatout
+ exit 4
+fi
+
+
+# check for the java process running
+echo ================================================
+ps -eaf|grep -v grep|grep java 1> /dev/null 2>&1
+if [ $? -eq 0 ]
+then
+ echo "Good: Java process is running"
+else
+ echo "ERROR: Java process not running. Try restarting the SSVM."
+ exit 3
+fi
+
+echo ================================================
+echo Tests Complete. Look for ERROR or WARNING above.
+
+exit 0
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4869f0ca/services/console-proxy/server/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java
----------------------------------------------------------------------
diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java b/services/console-proxy/server/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java
new file mode 100644
index 0000000..cff00b3
--- /dev/null
+++ b/services/console-proxy/server/src/com/cloud/consoleproxy/AjaxFIFOImageCache.java
@@ -0,0 +1,83 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.consoleproxy;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.cloud.consoleproxy.util.Logger;
+
+public class AjaxFIFOImageCache {
+ private static final Logger s_logger = Logger.getLogger(AjaxFIFOImageCache.class);
+
+ private List<Integer> fifoQueue;
+ private Map<Integer, byte[]> cache;
+ private int cacheSize;
+ private int nextKey = 0;
+
+ public AjaxFIFOImageCache(int cacheSize) {
+ this.cacheSize = cacheSize;
+ fifoQueue = new ArrayList<Integer>();
+ cache = new HashMap<Integer, byte[]>();
+ }
+
+ public synchronized void clear() {
+ fifoQueue.clear();
+ cache.clear();
+ }
+
+ public synchronized int putImage(byte[] image) {
+ while(cache.size() >= cacheSize) {
+ Integer keyToRemove = fifoQueue.remove(0);
+ cache.remove(keyToRemove);
+
+ if(s_logger.isTraceEnabled())
+ s_logger.trace("Remove image from cache, key: " + keyToRemove);
+ }
+
+ int key = getNextKey();
+
+ if(s_logger.isTraceEnabled())
+ s_logger.trace("Add image to cache, key: " + key);
+
+ cache.put(key, image);
+ fifoQueue.add(key);
+ return key;
+ }
+
+ public synchronized byte[] getImage(int key) {
+ if (key == 0) {
+ key = nextKey;
+ }
+ if (cache.containsKey(key)) {
+ if (s_logger.isTraceEnabled())
+ s_logger.trace("Retrieve image from cache, key: " + key);
+
+ return cache.get(key);
+ }
+
+ if (s_logger.isTraceEnabled())
+ s_logger.trace("Image is no long in cache, key: " + key);
+ return null;
+ }
+
+ public synchronized int getNextKey() {
+ return ++nextKey;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4869f0ca/services/console-proxy/server/src/com/cloud/consoleproxy/AuthenticationException.java
----------------------------------------------------------------------
diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/AuthenticationException.java b/services/console-proxy/server/src/com/cloud/consoleproxy/AuthenticationException.java
new file mode 100644
index 0000000..3fa2667
--- /dev/null
+++ b/services/console-proxy/server/src/com/cloud/consoleproxy/AuthenticationException.java
@@ -0,0 +1,33 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.consoleproxy;
+
+public class AuthenticationException extends Exception {
+ private static final long serialVersionUID = -393139302884898842L;
+ public AuthenticationException() {
+ super();
+ }
+ public AuthenticationException(String s) {
+ super(s);
+ }
+ public AuthenticationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ public AuthenticationException(Throwable cause) {
+ super(cause);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4869f0ca/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxy.java
----------------------------------------------------------------------
diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxy.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxy.java
new file mode 100644
index 0000000..a722d83
--- /dev/null
+++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxy.java
@@ -0,0 +1,499 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.consoleproxy;
+
+import java.io.File;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetSocketAddress;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executor;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.log4j.xml.DOMConfigurator;
+
+import com.cloud.consoleproxy.util.Logger;
+import com.google.gson.Gson;
+import com.sun.net.httpserver.HttpServer;
+
+/**
+ *
+ * ConsoleProxy, singleton class that manages overall activities in console proxy process. To make legacy code work, we still
+ */
+public class ConsoleProxy {
+ private static final Logger s_logger = Logger.getLogger(ConsoleProxy.class);
+
+ public static final int KEYBOARD_RAW = 0;
+ public static final int KEYBOARD_COOKED = 1;
+
+ public static int VIEWER_LINGER_SECONDS = 180;
+
+ public static Object context;
+
+ // this has become more ugly, to store keystore info passed from management server (we now use management server managed keystore to support
+ // dynamically changing to customer supplied certificate)
+ public static byte[] ksBits;
+ public static String ksPassword;
+
+ public static Method authMethod;
+ public static Method reportMethod;
+ public static Method ensureRouteMethod;
+
+ static Hashtable<String, ConsoleProxyClient> connectionMap = new Hashtable<String, ConsoleProxyClient>();
+ static int httpListenPort = 80;
+ static int httpCmdListenPort = 8001;
+ static int reconnectMaxRetry = 5;
+ static int readTimeoutSeconds = 90;
+ static int keyboardType = KEYBOARD_RAW;
+ static String factoryClzName;
+ static boolean standaloneStart = false;
+
+ static String encryptorPassword = genDefaultEncryptorPassword();
+
+ private static String genDefaultEncryptorPassword() {
+ try {
+ SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
+
+ byte[] randomBytes = new byte[16];
+ random.nextBytes(randomBytes);
+ return Base64.encodeBase64String(randomBytes);
+ } catch (NoSuchAlgorithmException e) {
+ s_logger.error("Unexpected exception ", e);
+ assert(false);
+ }
+
+ return "Dummy";
+ }
+
+ private static void configLog4j() {
+ URL configUrl = System.class.getResource("/conf/log4j-cloud.xml");
+ if(configUrl == null)
+ configUrl = ClassLoader.getSystemResource("log4j-cloud.xml");
+
+ if(configUrl == null)
+ configUrl = ClassLoader.getSystemResource("conf/log4j-cloud.xml");
+
+ if(configUrl != null) {
+ try {
+ System.out.println("Configure log4j using " + configUrl.toURI().toString());
+ } catch (URISyntaxException e1) {
+ e1.printStackTrace();
+ }
+
+ try {
+ File file = new File(configUrl.toURI());
+
+ System.out.println("Log4j configuration from : " + file.getAbsolutePath());
+ DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000);
+ } catch (URISyntaxException e) {
+ System.out.println("Unable to convert log4j configuration Url to URI");
+ }
+ // DOMConfigurator.configure(configUrl);
+ } else {
+ System.out.println("Configure log4j with default properties");
+ }
+ }
+
+ private static void configProxy(Properties conf) {
+ s_logger.info("Configure console proxy...");
+ for(Object key : conf.keySet()) {
+ s_logger.info("Property " + (String)key + ": " + conf.getProperty((String)key));
+ }
+
+ String s = conf.getProperty("consoleproxy.httpListenPort");
+ if (s!=null) {
+ httpListenPort = Integer.parseInt(s);
+ s_logger.info("Setting httpListenPort=" + s);
+ }
+
+ s = conf.getProperty("premium");
+ if(s != null && s.equalsIgnoreCase("true")) {
+ s_logger.info("Premium setting will override settings from consoleproxy.properties, listen at port 443");
+ httpListenPort = 443;
+ factoryClzName = "com.cloud.consoleproxy.ConsoleProxySecureServerFactoryImpl";
+ } else {
+ factoryClzName = ConsoleProxyBaseServerFactoryImpl.class.getName();
+ }
+
+ s = conf.getProperty("consoleproxy.httpCmdListenPort");
+ if (s!=null) {
+ httpCmdListenPort = Integer.parseInt(s);
+ s_logger.info("Setting httpCmdListenPort=" + s);
+ }
+
+ s = conf.getProperty("consoleproxy.reconnectMaxRetry");
+ if (s!=null) {
+ reconnectMaxRetry = Integer.parseInt(s);
+ s_logger.info("Setting reconnectMaxRetry=" + reconnectMaxRetry);
+ }
+
+ s = conf.getProperty("consoleproxy.readTimeoutSeconds");
+ if (s!=null) {
+ readTimeoutSeconds = Integer.parseInt(s);
+ s_logger.info("Setting readTimeoutSeconds=" + readTimeoutSeconds);
+ }
+ }
+
+ public static ConsoleProxyServerFactory getHttpServerFactory() {
+ try {
+ Class<?> clz = Class.forName(factoryClzName);
+ try {
+ ConsoleProxyServerFactory factory = (ConsoleProxyServerFactory)clz.newInstance();
+ factory.init(ConsoleProxy.ksBits, ConsoleProxy.ksPassword);
+ return factory;
+ } catch (InstantiationException e) {
+ s_logger.error(e.getMessage(), e);
+ return null;
+ } catch (IllegalAccessException e) {
+ s_logger.error(e.getMessage(), e);
+ return null;
+ }
+ } catch (ClassNotFoundException e) {
+ s_logger.warn("Unable to find http server factory class: " + factoryClzName);
+ return new ConsoleProxyBaseServerFactoryImpl();
+ }
+ }
+
+ public static ConsoleProxyAuthenticationResult authenticateConsoleAccess(ConsoleProxyClientParam param, boolean reauthentication) {
+
+ ConsoleProxyAuthenticationResult authResult = new ConsoleProxyAuthenticationResult();
+ authResult.setSuccess(true);
+ authResult.setReauthentication(reauthentication);
+ authResult.setHost(param.getClientHostAddress());
+ authResult.setPort(param.getClientHostPort());
+
+ if(standaloneStart) {
+ return authResult;
+ }
+
+ if(authMethod != null) {
+ Object result;
+ try {
+ result = authMethod.invoke(ConsoleProxy.context,
+ param.getClientHostAddress(),
+ String.valueOf(param.getClientHostPort()),
+ param.getClientTag(),
+ param.getClientHostPassword(),
+ param.getTicket(),
+ new Boolean(reauthentication));
+ } catch (IllegalAccessException e) {
+ s_logger.error("Unable to invoke authenticateConsoleAccess due to IllegalAccessException" + " for vm: " + param.getClientTag(), e);
+ authResult.setSuccess(false);
+ return authResult;
+ } catch (InvocationTargetException e) {
+ s_logger.error("Unable to invoke authenticateConsoleAccess due to InvocationTargetException " + " for vm: " + param.getClientTag(), e);
+ authResult.setSuccess(false);
+ return authResult;
+ }
+
+ if(result != null && result instanceof String) {
+ authResult = new Gson().fromJson((String)result, ConsoleProxyAuthenticationResult.class);
+ } else {
+ s_logger.error("Invalid authentication return object " + result + " for vm: " + param.getClientTag() + ", decline the access");
+ authResult.setSuccess(false);
+ }
+ } else {
+ s_logger.warn("Private channel towards management server is not setup. Switch to offline mode and allow access to vm: " + param.getClientTag());
+ }
+
+ return authResult;
+ }
+
+ public static void reportLoadInfo(String gsonLoadInfo) {
+ if(reportMethod != null) {
+ try {
+ reportMethod.invoke(ConsoleProxy.context, gsonLoadInfo);
+ } catch (IllegalAccessException e) {
+ s_logger.error("Unable to invoke reportLoadInfo due to " + e.getMessage());
+ } catch (InvocationTargetException e) {
+ s_logger.error("Unable to invoke reportLoadInfo due to " + e.getMessage());
+ }
+ } else {
+ s_logger.warn("Private channel towards management server is not setup. Switch to offline mode and ignore load report");
+ }
+ }
+
+ public static void ensureRoute(String address) {
+ if(ensureRouteMethod != null) {
+ try {
+ ensureRouteMethod.invoke(ConsoleProxy.context, address);
+ } catch (IllegalAccessException e) {
+ s_logger.error("Unable to invoke ensureRoute due to " + e.getMessage());
+ } catch (InvocationTargetException e) {
+ s_logger.error("Unable to invoke ensureRoute due to " + e.getMessage());
+ }
+ } else {
+ s_logger.warn("Unable to find ensureRoute method, console proxy agent is not up to date");
+ }
+ }
+
+ public static void startWithContext(Properties conf, Object context, byte[] ksBits, String ksPassword) {
+ s_logger.info("Start console proxy with context");
+ if(conf != null) {
+ for(Object key : conf.keySet()) {
+ s_logger.info("Context property " + (String)key + ": " + conf.getProperty((String)key));
+ }
+ }
+
+ configLog4j();
+ Logger.setFactory(new ConsoleProxyLoggerFactory());
+
+ // Using reflection to setup private/secure communication channel towards management server
+ ConsoleProxy.context = context;
+ ConsoleProxy.ksBits = ksBits;
+ ConsoleProxy.ksPassword = ksPassword;
+ try {
+ Class<?> contextClazz = Class.forName("com.cloud.agent.resource.consoleproxy.ConsoleProxyResource");
+ authMethod = contextClazz.getDeclaredMethod("authenticateConsoleAccess", String.class, String.class, String.class, String.class, String.class, Boolean.class);
+ reportMethod = contextClazz.getDeclaredMethod("reportLoadInfo", String.class);
+ ensureRouteMethod = contextClazz.getDeclaredMethod("ensureRoute", String.class);
+ } catch (SecurityException e) {
+ s_logger.error("Unable to setup private channel due to SecurityException", e);
+ } catch (NoSuchMethodException e) {
+ s_logger.error("Unable to setup private channel due to NoSuchMethodException", e);
+ } catch (IllegalArgumentException e) {
+ s_logger.error("Unable to setup private channel due to IllegalArgumentException", e);
+ } catch(ClassNotFoundException e) {
+ s_logger.error("Unable to setup private channel due to ClassNotFoundException", e);
+ }
+
+ // merge properties from conf file
+ InputStream confs = ConsoleProxy.class.getResourceAsStream("/conf/consoleproxy.properties");
+ Properties props = new Properties();
+ if (confs == null) {
+ s_logger.info("Can't load consoleproxy.properties from classpath, will use default configuration");
+ } else {
+ try {
+ props.load(confs);
+
+ for(Object key : props.keySet()) {
+ // give properties passed via context high priority, treat properties from consoleproxy.properties
+ // as default values
+ if(conf.get(key) == null)
+ conf.put(key, props.get(key));
+ }
+ } catch (Exception e) {
+ s_logger.error(e.toString(), e);
+ }
+ }
+
+ start(conf);
+ }
+
+ public static void start(Properties conf) {
+ System.setProperty("java.awt.headless", "true");
+
+ configProxy(conf);
+
+ ConsoleProxyServerFactory factory = getHttpServerFactory();
+ if(factory == null) {
+ s_logger.error("Unable to load console proxy server factory");
+ System.exit(1);
+ }
+
+ if(httpListenPort != 0) {
+ startupHttpMain();
+ } else {
+ s_logger.error("A valid HTTP server port is required to be specified, please check your consoleproxy.httpListenPort settings");
+ System.exit(1);
+ }
+
+ if(httpCmdListenPort > 0) {
+ startupHttpCmdPort();
+ } else {
+ s_logger.info("HTTP command port is disabled");
+ }
+
+ ConsoleProxyGCThread cthread = new ConsoleProxyGCThread(connectionMap);
+ cthread.setName("Console Proxy GC Thread");
+ cthread.start();
+ }
+
+ private static void startupHttpMain() {
+ try {
+ ConsoleProxyServerFactory factory = getHttpServerFactory();
+ if(factory == null) {
+ s_logger.error("Unable to load HTTP server factory");
+ System.exit(1);
+ }
+
+ HttpServer server = factory.createHttpServerInstance(httpListenPort);
+ server.createContext("/getscreen", new ConsoleProxyThumbnailHandler());
+ server.createContext("/resource/", new ConsoleProxyResourceHandler());
+ server.createContext("/ajax", new ConsoleProxyAjaxHandler());
+ server.createContext("/ajaximg", new ConsoleProxyAjaxImageHandler());
+ server.setExecutor(new ThreadExecutor()); // creates a default executor
+ server.start();
+ } catch(Exception e) {
+ s_logger.error(e.getMessage(), e);
+ System.exit(1);
+ }
+ }
+
+ private static void startupHttpCmdPort() {
+ try {
+ s_logger.info("Listening for HTTP CMDs on port " + httpCmdListenPort);
+ HttpServer cmdServer = HttpServer.create(new InetSocketAddress(httpCmdListenPort), 2);
+ cmdServer.createContext("/cmd", new ConsoleProxyCmdHandler());
+ cmdServer.setExecutor(new ThreadExecutor()); // creates a default executor
+ cmdServer.start();
+ } catch(Exception e) {
+ s_logger.error(e.getMessage(), e);
+ System.exit(1);
+ }
+ }
+
+ public static void main(String[] argv) {
+ standaloneStart = true;
+ configLog4j();
+ Logger.setFactory(new ConsoleProxyLoggerFactory());
+
+ InputStream confs = ConsoleProxy.class.getResourceAsStream("/conf/consoleproxy.properties");
+ Properties conf = new Properties();
+ if (confs == null) {
+ s_logger.info("Can't load consoleproxy.properties from classpath, will use default configuration");
+ } else {
+ try {
+ conf.load(confs);
+ } catch (Exception e) {
+ s_logger.error(e.toString(), e);
+ }
+ }
+ start(conf);
+ }
+
+ public static ConsoleProxyClient getVncViewer(ConsoleProxyClientParam param) throws Exception {
+ ConsoleProxyClient viewer = null;
+
+ boolean reportLoadChange = false;
+ String clientKey = param.getClientMapKey();
+ synchronized (connectionMap) {
+ viewer = connectionMap.get(clientKey);
+ if (viewer == null) {
+ viewer = new ConsoleProxyVncClient();
+ viewer.initClient(param);
+ connectionMap.put(clientKey, viewer);
+ s_logger.info("Added viewer object " + viewer);
+
+ reportLoadChange = true;
+ } else if (!viewer.isFrontEndAlive()) {
+ s_logger.info("The rfb thread died, reinitializing the viewer " + viewer);
+ viewer.initClient(param);
+ } else if (!param.getClientHostPassword().equals(viewer.getClientHostPassword())) {
+ s_logger.warn("Bad sid detected(VNC port may be reused). sid in session: " + viewer.getClientHostPassword()
+ + ", sid in request: " + param.getClientHostPassword());
+ viewer.initClient(param);
+ }
+ }
+
+ if(reportLoadChange) {
+ ConsoleProxyClientStatsCollector statsCollector = getStatsCollector();
+ String loadInfo = statsCollector.getStatsReport();
+ reportLoadInfo(loadInfo);
+ if(s_logger.isDebugEnabled())
+ s_logger.debug("Report load change : " + loadInfo);
+ }
+
+ return viewer;
+ }
+
+ public static ConsoleProxyClient getAjaxVncViewer(ConsoleProxyClientParam param, String ajaxSession) throws Exception {
+
+ boolean reportLoadChange = false;
+ String clientKey = param.getClientMapKey();
+ synchronized (connectionMap) {
+ ConsoleProxyClient viewer = connectionMap.get(clientKey);
+ if (viewer == null) {
+ viewer = new ConsoleProxyVncClient();
+ viewer.initClient(param);
+
+ connectionMap.put(clientKey, viewer);
+ s_logger.info("Added viewer object " + viewer);
+ reportLoadChange = true;
+ } else if (!viewer.isFrontEndAlive()) {
+ s_logger.info("The rfb thread died, reinitializing the viewer " + viewer);
+ viewer.initClient(param);
+ } else if (!param.getClientHostPassword().equals(viewer.getClientHostPassword())) {
+ s_logger.warn("Bad sid detected(VNC port may be reused). sid in session: "
+ + viewer.getClientHostPassword() + ", sid in request: " + param.getClientHostPassword());
+ viewer.initClient(param);
+ } else {
+ if(ajaxSession == null || ajaxSession.isEmpty())
+ authenticationExternally(param);
+ }
+
+ if(reportLoadChange) {
+ ConsoleProxyClientStatsCollector statsCollector = getStatsCollector();
+ String loadInfo = statsCollector.getStatsReport();
+ reportLoadInfo(loadInfo);
+ if(s_logger.isDebugEnabled())
+ s_logger.debug("Report load change : " + loadInfo);
+ }
+ return viewer;
+ }
+ }
+
+ public static void removeViewer(ConsoleProxyClient viewer) {
+ synchronized (connectionMap) {
+ for(Map.Entry<String, ConsoleProxyClient> entry : connectionMap.entrySet()) {
+ if(entry.getValue() == viewer) {
+ connectionMap.remove(entry.getKey());
+ return;
+ }
+ }
+ }
+ }
+
+ public static ConsoleProxyClientStatsCollector getStatsCollector() {
+ return new ConsoleProxyClientStatsCollector(connectionMap);
+ }
+
+ public static void authenticationExternally(ConsoleProxyClientParam param) throws AuthenticationException {
+ ConsoleProxyAuthenticationResult authResult = authenticateConsoleAccess(param, false);
+
+ if(authResult == null || !authResult.isSuccess()) {
+ s_logger.warn("External authenticator failed authencation request for vm " + param.getClientTag() + " with sid " + param.getClientHostPassword());
+
+ throw new AuthenticationException("External authenticator failed request for vm " + param.getClientTag() + " with sid " + param.getClientHostPassword());
+ }
+ }
+
+ public static ConsoleProxyAuthenticationResult reAuthenticationExternally(ConsoleProxyClientParam param) {
+ return authenticateConsoleAccess(param, true);
+ }
+
+ public static String getEncryptorPassword() {
+ return encryptorPassword;
+ }
+
+ public static void setEncryptorPassword(String password) {
+ encryptorPassword = password;
+ }
+
+ static class ThreadExecutor implements Executor {
+ public void execute(Runnable r) {
+ new Thread(r).start();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4869f0ca/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java
----------------------------------------------------------------------
diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java
new file mode 100644
index 0000000..6cadeca
--- /dev/null
+++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java
@@ -0,0 +1,406 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.consoleproxy;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.cloud.consoleproxy.util.Logger;
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+public class ConsoleProxyAjaxHandler implements HttpHandler {
+ private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxHandler.class);
+
+ public ConsoleProxyAjaxHandler() {
+ }
+
+ public void handle(HttpExchange t) throws IOException {
+ try {
+ if(s_logger.isTraceEnabled())
+ s_logger.trace("AjaxHandler " + t.getRequestURI());
+
+ long startTick = System.currentTimeMillis();
+
+ doHandle(t);
+
+ if(s_logger.isTraceEnabled())
+ s_logger.trace(t.getRequestURI() + " process time " + (System.currentTimeMillis() - startTick) + " ms");
+ } catch (IOException e) {
+ throw e;
+ } catch (IllegalArgumentException e) {
+ s_logger.warn("Exception, ", e);
+ t.sendResponseHeaders(400, -1); // bad request
+ } catch(Throwable e) {
+ s_logger.error("Unexpected exception, ", e);
+ t.sendResponseHeaders(500, -1); // server error
+ } finally {
+ t.close();
+ }
+ }
+
+ private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException {
+ String queries = t.getRequestURI().getQuery();
+ if(s_logger.isTraceEnabled())
+ s_logger.trace("Handle AJAX request: " + queries);
+
+ Map<String, String> queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries);
+
+ String host = queryMap.get("host");
+ String portStr = queryMap.get("port");
+ String sid = queryMap.get("sid");
+ String tag = queryMap.get("tag");
+ String ticket = queryMap.get("ticket");
+ String ajaxSessionIdStr = queryMap.get("sess");
+ String eventStr = queryMap.get("event");
+ String console_url = queryMap.get("consoleurl");
+ String console_host_session = queryMap.get("sessionref");
+
+ if(tag == null)
+ tag = "";
+
+ long ajaxSessionId = 0;
+ int event = 0;
+
+ int port;
+
+ if(host == null || portStr == null || sid == null)
+ throw new IllegalArgumentException();
+
+ try {
+ port = Integer.parseInt(portStr);
+ } catch (NumberFormatException e) {
+ s_logger.warn("Invalid number parameter in query string: " + portStr);
+ throw new IllegalArgumentException(e);
+ }
+
+ if(ajaxSessionIdStr != null) {
+ try {
+ ajaxSessionId = Long.parseLong(ajaxSessionIdStr);
+ } catch (NumberFormatException e) {
+ s_logger.warn("Invalid number parameter in query string: " + ajaxSessionIdStr);
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ if(eventStr != null) {
+ try {
+ event = Integer.parseInt(eventStr);
+ } catch (NumberFormatException e) {
+ s_logger.warn("Invalid number parameter in query string: " + eventStr);
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ ConsoleProxyClient viewer = null;
+ try {
+ ConsoleProxyClientParam param = new ConsoleProxyClientParam();
+ param.setClientHostAddress(host);
+ param.setClientHostPort(port);
+ param.setClientHostPassword(sid);
+ param.setClientTag(tag);
+ param.setTicket(ticket);
+ param.setClientTunnelUrl(console_url);
+ param.setClientTunnelSession(console_host_session);
+
+ viewer = ConsoleProxy.getAjaxVncViewer(param, ajaxSessionIdStr);
+ } catch(Exception e) {
+
+ s_logger.warn("Failed to create viewer due to " + e.getMessage(), e);
+
+ String[] content = new String[] {
+ "<html><head></head><body>",
+ "<div id=\"main_panel\" tabindex=\"1\">",
+ "<p>Access is denied for the console session. Please close the window and retry again</p>",
+ "</div></body></html>"
+ };
+
+ StringBuffer sb = new StringBuffer();
+ for(int i = 0; i < content.length; i++)
+ sb.append(content[i]);
+
+ sendResponse(t, "text/html", sb.toString());
+ return;
+ }
+
+ if(event != 0) {
+ if(ajaxSessionId != 0 && ajaxSessionId == viewer.getAjaxSessionId()) {
+ if(event == 7) {
+ // client send over an event bag
+ InputStream is = t.getRequestBody();
+ handleClientEventBag(viewer, convertStreamToString(is, true));
+ } else {
+ handleClientEvent(viewer, event, queryMap);
+ }
+ sendResponse(t, "text/html", "OK");
+ } else {
+ if(s_logger.isDebugEnabled())
+ s_logger.debug("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId());
+
+ sendResponse(t, "text/html", "Invalid ajax client session id");
+ }
+ } else {
+ if(ajaxSessionId != 0 && ajaxSessionId != viewer.getAjaxSessionId()) {
+ s_logger.info("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId());
+ handleClientKickoff(t, viewer);
+ } else if(ajaxSessionId == 0) {
+ if(s_logger.isDebugEnabled())
+ s_logger.debug("Ajax request indicates a fresh client start");
+
+ String title = queryMap.get("t");
+ String guest = queryMap.get("guest");
+ handleClientStart(t, viewer, title != null ? title : "", guest);
+ } else {
+
+ if(s_logger.isTraceEnabled())
+ s_logger.trace("Ajax request indicates client update");
+
+ handleClientUpdate(t, viewer);
+ }
+ }
+ }
+
+ private static String convertStreamToString(InputStream is, boolean closeStreamAfterRead) {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ StringBuilder sb = new StringBuilder();
+ String line = null;
+ try {
+ while ((line = reader.readLine()) != null) {
+ sb.append(line + "\n");
+ }
+ } catch (IOException e) {
+ s_logger.warn("Exception while reading request body: ", e);
+ } finally {
+ if(closeStreamAfterRead) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ private void sendResponse(HttpExchange t, String contentType, String response) throws IOException {
+ Headers hds = t.getResponseHeaders();
+ hds.set("Content-Type", contentType);
+
+ t.sendResponseHeaders(200, response.length());
+ OutputStream os = t.getResponseBody();
+ try {
+ os.write(response.getBytes());
+ } finally {
+ os.close();
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void handleClientEventBag(ConsoleProxyClient viewer, String requestData) {
+ if(s_logger.isTraceEnabled())
+ s_logger.trace("Handle event bag, event bag: " + requestData);
+
+ int start = requestData.indexOf("=");
+ if(start < 0)
+ start = 0;
+ else if(start > 0)
+ start++;
+ String data = URLDecoder.decode(requestData.substring(start));
+ String[] tokens = data.split("\\|");
+ if(tokens != null && tokens.length > 0) {
+ int count = 0;
+ try {
+ count = Integer.parseInt(tokens[0]);
+ int parsePos = 1;
+ int type, event, x, y, code, modifiers;
+ for(int i = 0; i < count; i++) {
+ type = Integer.parseInt(tokens[parsePos++]);
+ if(type == 1) {
+ // mouse event
+ event = Integer.parseInt(tokens[parsePos++]);
+ x = Integer.parseInt(tokens[parsePos++]);
+ y = Integer.parseInt(tokens[parsePos++]);
+ code = Integer.parseInt(tokens[parsePos++]);
+ modifiers = Integer.parseInt(tokens[parsePos++]);
+
+ Map<String, String> queryMap = new HashMap<String, String>();
+ queryMap.put("event", String.valueOf(event));
+ queryMap.put("x", String.valueOf(x));
+ queryMap.put("y", String.valueOf(y));
+ queryMap.put("code", String.valueOf(code));
+ queryMap.put("modifier", String.valueOf(modifiers));
+ handleClientEvent(viewer, event, queryMap);
+ } else {
+ // keyboard event
+ event = Integer.parseInt(tokens[parsePos++]);
+ code = Integer.parseInt(tokens[parsePos++]);
+ modifiers = Integer.parseInt(tokens[parsePos++]);
+
+ Map<String, String> queryMap = new HashMap<String, String>();
+ queryMap.put("event", String.valueOf(event));
+ queryMap.put("code", String.valueOf(code));
+ queryMap.put("modifier", String.valueOf(modifiers));
+ handleClientEvent(viewer, event, queryMap);
+ }
+ }
+ } catch(NumberFormatException e) {
+ s_logger.warn("Exception in handle client event bag: " + data + ", ", e);
+ } catch(Exception e) {
+ s_logger.warn("Exception in handle client event bag: " + data + ", ", e);
+ } catch(OutOfMemoryError e) {
+ s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched");
+ System.exit(1);
+ }
+ }
+ }
+
+ private void handleClientEvent(ConsoleProxyClient viewer, int event, Map<String, String> queryMap) {
+ int code = 0;
+ int x = 0, y = 0;
+ int modifiers = 0;
+
+ String str;
+ switch(event) {
+ case 1: // mouse move
+ case 2: // mouse down
+ case 3: // mouse up
+ case 8: // mouse double click
+ str = queryMap.get("x");
+ if(str != null) {
+ try {
+ x = Integer.parseInt(str);
+ } catch (NumberFormatException e) {
+ s_logger.warn("Invalid number parameter in query string: " + str);
+ throw new IllegalArgumentException(e);
+ }
+ }
+ str = queryMap.get("y");
+ if(str != null) {
+ try {
+ y = Integer.parseInt(str);
+ } catch (NumberFormatException e) {
+ s_logger.warn("Invalid number parameter in query string: " + str);
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ if(event != 1) {
+ str = queryMap.get("code");
+ try {
+ code = Integer.parseInt(str);
+ } catch (NumberFormatException e) {
+ s_logger.warn("Invalid number parameter in query string: " + str);
+ throw new IllegalArgumentException(e);
+ }
+
+ str = queryMap.get("modifier");
+ try {
+ modifiers = Integer.parseInt(str);
+ } catch (NumberFormatException e) {
+ s_logger.warn("Invalid number parameter in query string: " + str);
+ throw new IllegalArgumentException(e);
+ }
+
+ if(s_logger.isTraceEnabled())
+ s_logger.trace("Handle client mouse event. event: " + event + ", x: " + x + ", y: " + y + ", button: " + code + ", modifier: " + modifiers);
+ } else {
+ if(s_logger.isTraceEnabled())
+ s_logger.trace("Handle client mouse move event. x: " + x + ", y: " + y);
+ }
+ viewer.sendClientMouseEvent(InputEventType.fromEventCode(event), x, y, code, modifiers);
+ break;
+
+ case 4: // key press
+ case 5: // key down
+ case 6: // key up
+ str = queryMap.get("code");
+ try {
+ code = Integer.parseInt(str);
+ } catch (NumberFormatException e) {
+ s_logger.warn("Invalid number parameter in query string: " + str);
+ throw new IllegalArgumentException(e);
+ }
+
+ str = queryMap.get("modifier");
+ try {
+ modifiers = Integer.parseInt(str);
+ } catch (NumberFormatException e) {
+ s_logger.warn("Invalid number parameter in query string: " + str);
+ throw new IllegalArgumentException(e);
+ }
+
+ if(s_logger.isDebugEnabled())
+ s_logger.debug("Handle client keyboard event. event: " + event + ", code: " + code + ", modifier: " + modifiers);
+ viewer.sendClientRawKeyboardEvent(InputEventType.fromEventCode(event), code, modifiers);
+ break;
+
+ default :
+ break;
+ }
+ }
+
+ private void handleClientKickoff(HttpExchange t, ConsoleProxyClient viewer) throws IOException {
+ String response = viewer.onAjaxClientKickoff();
+ t.sendResponseHeaders(200, response.length());
+ OutputStream os = t.getResponseBody();
+ try {
+ os.write(response.getBytes());
+ } finally {
+ os.close();
+ }
+ }
+
+ private void handleClientStart(HttpExchange t, ConsoleProxyClient viewer, String title, String guest) throws IOException {
+ List<String> languages = t.getRequestHeaders().get("Accept-Language");
+ String response = viewer.onAjaxClientStart(title, languages, guest);
+
+ Headers hds = t.getResponseHeaders();
+ hds.set("Content-Type", "text/html");
+ hds.set("Cache-Control", "no-cache");
+ hds.set("Cache-Control", "no-store");
+ t.sendResponseHeaders(200, response.length());
+
+ OutputStream os = t.getResponseBody();
+ try {
+ os.write(response.getBytes());
+ } finally {
+ os.close();
+ }
+ }
+
+ private void handleClientUpdate(HttpExchange t, ConsoleProxyClient viewer) throws IOException {
+ String response = viewer.onAjaxClientUpdate();
+
+ Headers hds = t.getResponseHeaders();
+ hds.set("Content-Type", "text/javascript");
+ t.sendResponseHeaders(200, response.length());
+
+ OutputStream os = t.getResponseBody();
+ try {
+ os.write(response.getBytes());
+ } finally {
+ os.close();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4869f0ca/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java
----------------------------------------------------------------------
diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java
new file mode 100644
index 0000000..5e10149
--- /dev/null
+++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java
@@ -0,0 +1,159 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.consoleproxy;
+
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+
+import com.cloud.consoleproxy.util.Logger;
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+public class ConsoleProxyAjaxImageHandler implements HttpHandler {
+ private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxImageHandler.class);
+
+ public void handle(HttpExchange t) throws IOException {
+ try {
+ if(s_logger.isDebugEnabled())
+ s_logger.debug("AjaxImageHandler " + t.getRequestURI());
+
+ long startTick = System.currentTimeMillis();
+
+ doHandle(t);
+
+ if(s_logger.isDebugEnabled())
+ s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms");
+ } catch (IOException e) {
+ throw e;
+ } catch (IllegalArgumentException e) {
+ s_logger.warn("Exception, ", e);
+ t.sendResponseHeaders(400, -1); // bad request
+ } catch(OutOfMemoryError e) {
+ s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched");
+ System.exit(1);
+ } catch(Throwable e) {
+ s_logger.error("Unexpected exception, ", e);
+ t.sendResponseHeaders(500, -1); // server error
+ } finally {
+ t.close();
+ }
+ }
+
+ private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException {
+ String queries = t.getRequestURI().getQuery();
+ Map<String, String> queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries);
+
+ String host = queryMap.get("host");
+ String portStr = queryMap.get("port");
+ String sid = queryMap.get("sid");
+ String tag = queryMap.get("tag");
+ String ticket = queryMap.get("ticket");
+ String keyStr = queryMap.get("key");
+ String console_url = queryMap.get("consoleurl");
+ String console_host_session = queryMap.get("sessionref");
+ String w = queryMap.get("w");
+ String h = queryMap.get("h");
+
+ int key = 0;
+ int width = 144;
+ int height = 110;
+
+ if(tag == null)
+ tag = "";
+
+ int port;
+ if(host == null || portStr == null || sid == null)
+ throw new IllegalArgumentException();
+
+ try {
+ port = Integer.parseInt(portStr);
+ } catch (NumberFormatException e) {
+ s_logger.warn("Invalid numeric parameter in query string: " + portStr);
+ throw new IllegalArgumentException(e);
+ }
+
+ try {
+ if (keyStr != null)
+ key = Integer.parseInt(keyStr);
+ if(null != w)
+ width = Integer.parseInt(w);
+
+ if(null != h)
+ height = Integer.parseInt(h);
+
+ } catch (NumberFormatException e) {
+ s_logger.warn("Invalid numeric parameter in query string: " + keyStr);
+ throw new IllegalArgumentException(e);
+ }
+
+ ConsoleProxyClientParam param = new ConsoleProxyClientParam();
+ param.setClientHostAddress(host);
+ param.setClientHostPort(port);
+ param.setClientHostPassword(sid);
+ param.setClientTag(tag);
+ param.setTicket(ticket);
+ param.setClientTunnelUrl(console_url);
+ param.setClientTunnelSession(console_host_session);
+
+ ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param);
+
+ if (key == 0) {
+ Image scaledImage = viewer.getClientScaledImage(width, height);
+ BufferedImage bufferedImage = new BufferedImage(width, height,
+ BufferedImage.TYPE_3BYTE_BGR);
+ Graphics2D bufImageGraphics = bufferedImage.createGraphics();
+ bufImageGraphics.drawImage(scaledImage, 0, 0, null);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(8196);
+ javax.imageio.ImageIO.write(bufferedImage, "jpg", bos);
+ byte[] bs = bos.toByteArray();
+ Headers hds = t.getResponseHeaders();
+ hds.set("Content-Type", "image/jpeg");
+ hds.set("Cache-Control", "no-cache");
+ hds.set("Cache-Control", "no-store");
+ t.sendResponseHeaders(200, bs.length);
+ OutputStream os = t.getResponseBody();
+ os.write(bs);
+ os.close();
+ } else {
+ AjaxFIFOImageCache imageCache = viewer.getAjaxImageCache();
+ byte[] img = imageCache.getImage(key);
+
+ if(img != null) {
+ Headers hds = t.getResponseHeaders();
+ hds.set("Content-Type", "image/jpeg");
+ t.sendResponseHeaders(200, img.length);
+
+ OutputStream os = t.getResponseBody();
+ try {
+ os.write(img, 0, img.length);
+ } finally {
+ os.close();
+ }
+ } else {
+ if(s_logger.isInfoEnabled())
+ s_logger.info("Image has already been swept out, key: " + key);
+ t.sendResponseHeaders(404, -1);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4869f0ca/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java
----------------------------------------------------------------------
diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java
new file mode 100644
index 0000000..26ee9b3
--- /dev/null
+++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAuthenticationResult.java
@@ -0,0 +1,81 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.consoleproxy;
+
+// duplicated class
+public class ConsoleProxyAuthenticationResult {
+ private boolean success;
+ private boolean isReauthentication;
+ private String host;
+ private int port;
+ private String tunnelUrl;
+ private String tunnelSession;
+
+ public ConsoleProxyAuthenticationResult() {
+ success = false;
+ isReauthentication = false;
+ port = 0;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public void setSuccess(boolean success) {
+ this.success = success;
+ }
+
+ public boolean isReauthentication() {
+ return isReauthentication;
+ }
+
+ public void setReauthentication(boolean isReauthentication) {
+ this.isReauthentication = isReauthentication;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public String getTunnelUrl() {
+ return tunnelUrl;
+ }
+
+ public void setTunnelUrl(String tunnelUrl) {
+ this.tunnelUrl = tunnelUrl;
+ }
+
+ public String getTunnelSession() {
+ return tunnelSession;
+ }
+
+ public void setTunnelSession(String tunnelSession) {
+ this.tunnelSession = tunnelSession;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4869f0ca/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java
----------------------------------------------------------------------
diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java
new file mode 100644
index 0000000..c9ad8ab
--- /dev/null
+++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java
@@ -0,0 +1,48 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.consoleproxy;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import javax.net.ssl.SSLServerSocket;
+
+import com.cloud.consoleproxy.util.Logger;
+import com.sun.net.httpserver.HttpServer;
+
+public class ConsoleProxyBaseServerFactoryImpl implements ConsoleProxyServerFactory {
+ private static final Logger s_logger = Logger.getLogger(ConsoleProxyBaseServerFactoryImpl.class);
+
+ @Override
+ public void init(byte[] ksBits, String ksPassword) {
+ }
+
+ @Override
+ public HttpServer createHttpServerInstance(int port) throws IOException {
+ if(s_logger.isInfoEnabled())
+ s_logger.info("create HTTP server instance at port: " + port);
+ return HttpServer.create(new InetSocketAddress(port), 5);
+ }
+
+ @Override
+ public SSLServerSocket createSSLServerSocket(int port) throws IOException {
+ if(s_logger.isInfoEnabled())
+ s_logger.info("SSL server socket is not supported in ConsoleProxyBaseServerFactoryImpl");
+
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4869f0ca/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClient.java
----------------------------------------------------------------------
diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClient.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClient.java
new file mode 100644
index 0000000..8a0be05
--- /dev/null
+++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClient.java
@@ -0,0 +1,69 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.consoleproxy;
+
+import java.awt.Image;
+import java.util.List;
+
+/**
+ * ConsoleProxyClient defines an standard interface that a console client should implement,
+ *
+ * ConsoleProxyClient maintains a session towards the target host, it glues the session
+ * to a AJAX front-end viewer
+ */
+public interface ConsoleProxyClient {
+ int getClientId();
+
+ //
+ // Quick status
+ //
+ boolean isHostConnected();
+ boolean isFrontEndAlive();
+
+ //
+ // AJAX viewer
+ //
+ long getAjaxSessionId();
+ AjaxFIFOImageCache getAjaxImageCache();
+ Image getClientScaledImage(int width, int height); // client thumbnail support
+
+ String onAjaxClientStart(String title, List<String> languages, String guest);
+ String onAjaxClientUpdate();
+ String onAjaxClientKickoff();
+
+ //
+ // Input handling
+ //
+ void sendClientRawKeyboardEvent(InputEventType event, int code, int modifiers);
+ void sendClientMouseEvent(InputEventType event, int x, int y, int code, int modifiers);
+
+ //
+ // Info/Stats
+ //
+ long getClientCreateTime();
+ long getClientLastFrontEndActivityTime();
+ String getClientHostAddress();
+ int getClientHostPort();
+ String getClientHostPassword();
+ String getClientTag();
+
+ //
+ // Setup/house-keeping
+ //
+ void initClient(ConsoleProxyClientParam param);
+ void closeClient();
+}
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4869f0ca/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java
----------------------------------------------------------------------
diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java
new file mode 100644
index 0000000..289bdab
--- /dev/null
+++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java
@@ -0,0 +1,457 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.consoleproxy;
+
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.consoleproxy.util.TileInfo;
+import com.cloud.consoleproxy.util.TileTracker;
+import com.cloud.consoleproxy.vnc.FrameBufferCanvas;
+
+/**
+ *
+ * an instance of specialized console protocol implementation, such as VNC or RDP
+ *
+ * It mainly implements the features needed by front-end AJAX viewer
+ *
+ */
+public abstract class ConsoleProxyClientBase implements ConsoleProxyClient, ConsoleProxyClientListener {
+ private static final Logger s_logger = Logger.getLogger(ConsoleProxyClientBase.class);
+
+ private static int s_nextClientId = 0;
+ protected int clientId = getNextClientId();
+
+ protected long ajaxSessionId = 0;
+
+ protected boolean dirtyFlag = false;
+ protected Object tileDirtyEvent = new Object();
+ protected TileTracker tracker;
+ protected AjaxFIFOImageCache ajaxImageCache = new AjaxFIFOImageCache(2);
+
+ protected ConsoleProxyClientParam clientParam;
+ protected String clientToken;
+
+ protected long createTime = System.currentTimeMillis();
+ protected long lastFrontEndActivityTime = System.currentTimeMillis();
+
+ protected boolean framebufferResized = false;
+ protected int resizedFramebufferWidth;
+ protected int resizedFramebufferHeight;
+
+ public ConsoleProxyClientBase() {
+ tracker = new TileTracker();
+ tracker.initTracking(64, 64, 800, 600);
+ }
+
+ //
+ // interface ConsoleProxyClient
+ //
+ @Override
+ public int getClientId() {
+ return clientId;
+ }
+
+ public abstract boolean isHostConnected();
+ public abstract boolean isFrontEndAlive();
+
+ @Override
+ public long getAjaxSessionId() {
+ return this.ajaxSessionId;
+ }
+
+ @Override
+ public AjaxFIFOImageCache getAjaxImageCache() {
+ return ajaxImageCache;
+ }
+
+ public Image getClientScaledImage(int width, int height) {
+ FrameBufferCanvas canvas = getFrameBufferCavas();
+ if(canvas != null)
+ return canvas.getFrameBufferScaledImage(width, height);
+
+ return null;
+ }
+
+ public abstract void sendClientRawKeyboardEvent(InputEventType event, int code, int modifiers);
+ public abstract void sendClientMouseEvent(InputEventType event, int x, int y, int code, int modifiers);
+
+ @Override
+ public long getClientCreateTime() {
+ return createTime;
+ }
+
+ @Override
+ public long getClientLastFrontEndActivityTime() {
+ return lastFrontEndActivityTime;
+ }
+
+ @Override
+ public String getClientHostAddress() {
+ return clientParam.getClientHostAddress();
+ }
+
+ @Override
+ public int getClientHostPort() {
+ return clientParam.getClientHostPort();
+ }
+
+ @Override
+ public String getClientHostPassword() {
+ return clientParam.getClientHostPassword();
+ }
+
+ @Override
+ public String getClientTag() {
+ if(clientParam.getClientTag() != null)
+ return clientParam.getClientTag();
+ return "";
+ }
+
+ @Override
+ public abstract void initClient(ConsoleProxyClientParam param);
+
+ @Override
+ public abstract void closeClient();
+
+ //
+ // interface FrameBufferEventListener
+ //
+ @Override
+ public void onFramebufferSizeChange(int w, int h) {
+ tracker.resize(w, h);
+
+ synchronized(this) {
+ framebufferResized = true;
+ resizedFramebufferWidth = w;
+ resizedFramebufferHeight = h;
+ }
+
+ signalTileDirtyEvent();
+ }
+
+ @Override
+ public void onFramebufferUpdate(int x, int y, int w, int h) {
+ if(s_logger.isTraceEnabled())
+ s_logger.trace("Frame buffer update {" + x + "," + y + "," + w + "," + h + "}");
+ tracker.invalidate(new Rectangle(x, y, w, h));
+
+ signalTileDirtyEvent();
+ }
+
+ //
+ // AJAX Image manipulation
+ //
+ public byte[] getFrameBufferJpeg() {
+ FrameBufferCanvas canvas = getFrameBufferCavas();
+ if(canvas != null)
+ return canvas.getFrameBufferJpeg();
+
+ return null;
+ }
+
+ public byte[] getTilesMergedJpeg(List<TileInfo> tileList, int tileWidth, int tileHeight) {
+ FrameBufferCanvas canvas = getFrameBufferCavas();
+ if(canvas != null)
+ return canvas.getTilesMergedJpeg(tileList, tileWidth, tileHeight);
+ return null;
+ }
+
+ private String prepareAjaxImage(List<TileInfo> tiles, boolean init) {
+ byte[] imgBits;
+ if(init)
+ imgBits = getFrameBufferJpeg();
+ else
+ imgBits = getTilesMergedJpeg(tiles, tracker.getTileWidth(), tracker.getTileHeight());
+
+ if(imgBits == null) {
+ s_logger.warn("Unable to generate jpeg image");
+ } else {
+ if(s_logger.isTraceEnabled())
+ s_logger.trace("Generated jpeg image size: " + imgBits.length);
+ }
+
+ int key = ajaxImageCache.putImage(imgBits);
+ StringBuffer sb = new StringBuffer();
+ sb.append("/ajaximg?token=").append(clientToken);
+ sb.append("&key=").append(key);
+ sb.append("&ts=").append(System.currentTimeMillis());
+
+ return sb.toString();
+ }
+
+ private String prepareAjaxSession(boolean init) {
+ if(init) {
+ synchronized(this) {
+ ajaxSessionId++;
+ }
+ }
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("/ajax?token=").append(clientToken).append("&sess=").append(ajaxSessionId);
+ return sb.toString();
+ }
+
+ @Override
+ public String onAjaxClientKickoff() {
+ return "onKickoff();";
+ }
+
+ private boolean waitForViewerReady() {
+ long startTick = System.currentTimeMillis();
+ while(System.currentTimeMillis() - startTick < 5000) {
+ if(getFrameBufferCavas() != null)
+ return true;
+
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+ }
+ return false;
+ }
+
+ private String onAjaxClientConnectFailed() {
+ return "<html><head></head><body><div id=\"main_panel\" tabindex=\"1\"><p>" +
+ "Unable to start console session as connection is refused by the machine you are accessing" +
+ "</p></div></body></html>";
+ }
+
+ @Override
+ public String onAjaxClientStart(String title, List<String> languages, String guest) {
+ updateFrontEndActivityTime();
+
+ if(!waitForViewerReady())
+ return onAjaxClientConnectFailed();
+
+ synchronized(this) {
+ ajaxSessionId++;
+ framebufferResized = false;
+ }
+
+ int tileWidth = tracker.getTileWidth();
+ int tileHeight = tracker.getTileHeight();
+ int width = tracker.getTrackWidth();
+ int height = tracker.getTrackHeight();
+
+ if(s_logger.isTraceEnabled())
+ s_logger.trace("Ajax client start, frame buffer w: " + width + ", " + height);
+
+ int retry = 0;
+ tracker.initCoverageTest();
+ while(!tracker.hasFullCoverage() && retry < 10) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ retry++;
+ }
+
+ List<TileInfo> tiles = tracker.scan(true);
+ String imgUrl = prepareAjaxImage(tiles, true);
+ String updateUrl = prepareAjaxSession(true);
+
+ StringBuffer sbTileSequence = new StringBuffer();
+ int i = 0;
+ for(TileInfo tile : tiles) {
+ sbTileSequence.append("[").append(tile.getRow()).append(",").append(tile.getCol()).append("]");
+ if(i < tiles.size() - 1)
+ sbTileSequence.append(",");
+
+ i++;
+ }
+
+ return getAjaxViewerPageContent(sbTileSequence.toString(), imgUrl,
+ updateUrl, width, height, tileWidth, tileHeight, title,
+ ConsoleProxy.keyboardType == ConsoleProxy.KEYBOARD_RAW,
+ languages, guest);
+ }
+
+ private String getAjaxViewerPageContent(String tileSequence, String imgUrl, String updateUrl, int width,
+ int height, int tileWidth, int tileHeight, String title, boolean rawKeyboard, List<String> languages, String guest) {
+
+ StringBuffer sbLanguages = new StringBuffer("");
+ if(languages != null) {
+ for(String lang : languages) {
+ if(sbLanguages.length() > 0) {
+ sbLanguages.append(",");
+ }
+ sbLanguages.append(lang);
+ }
+ }
+
+ String[] content = new String[] {
+ "<html>",
+ "<head>",
+ "<script type=\"text/javascript\" language=\"javascript\" src=\"/resource/js/jquery.js\"></script>",
+ "<script type=\"text/javascript\" language=\"javascript\" src=\"/resource/js/cloud.logger.js\"></script>",
+ "<script type=\"text/javascript\" language=\"javascript\" src=\"/resource/js/ajaxkeys.js\"></script>",
+ "<script type=\"text/javascript\" language=\"javascript\" src=\"/resource/js/ajaxviewer.js\"></script>",
+ "<script type=\"text/javascript\" language=\"javascript\" src=\"/resource/js/handler.js\"></script>",
+ "<link rel=\"stylesheet\" type=\"text/css\" href=\"/resource/css/ajaxviewer.css\"></link>",
+ "<link rel=\"stylesheet\" type=\"text/css\" href=\"/resource/css/logger.css\"></link>",
+ "<title>" + title + "</title>",
+ "</head>",
+ "<body>",
+ "<div id=\"toolbar\">",
+ "<ul>",
+ "<li>",
+ "<a href=\"#\" cmd=\"sendCtrlAltDel\">",
+ "<span><img align=\"left\" src=\"/resource/images/cad.gif\" alt=\"Ctrl-Alt-Del\" />Ctrl-Alt-Del</span>",
+ "</a>",
+ "</li>",
+ "<li>",
+ "<a href=\"#\" cmd=\"sendCtrlEsc\">",
+ "<span><img align=\"left\" src=\"/resource/images/winlog.png\" alt=\"Ctrl-Esc\" style=\"width:16px;height:16px\"/>Ctrl-Esc</span>",
+ "</a>",
+ "</li>",
+
+ "<li class=\"pulldown\">",
+ "<a href=\"#\">",
+ "<span><img align=\"left\" src=\"/resource/images/winlog.png\" alt=\"Keyboard\" style=\"width:16px;height:16px\"/>Keyboard</span>",
+ "</a>",
+ "<ul>",
+ "<li><a href=\"#\" cmd=\"keyboard_us\"><span>Standard (US) keyboard</span></a></li>",
+ "<li><a href=\"#\" cmd=\"keyboard_jp\"><span>Japanese keyboard</span></a></li>",
+ "</ul>",
+ "</li>",
+ "</ul>",
+ "<span id=\"light\" class=\"dark\" cmd=\"toggle_logwin\"></span>",
+ "</div>",
+ "<div id=\"main_panel\" tabindex=\"1\"></div>",
+ "<script language=\"javascript\">",
+ "var acceptLanguages = '" + sbLanguages.toString() + "';",
+ "var tileMap = [ " + tileSequence + " ];",
+ "var ajaxViewer = new AjaxViewer('main_panel', '" + imgUrl + "', '" + updateUrl + "', tileMap, ",
+ String.valueOf(width) + ", " + String.valueOf(height) + ", " + String.valueOf(tileWidth) + ", " + String.valueOf(tileHeight) + ");",
+
+ "$(function() {",
+ "ajaxViewer.start();",
+ "});",
+
+ "</script>",
+ "</body>",
+ "</html>"
+ };
+
+ StringBuffer sb = new StringBuffer();
+ for(int i = 0; i < content.length; i++)
+ sb.append(content[i]);
+
+ return sb.toString();
+ }
+
+ public String onAjaxClientDisconnected() {
+ return "onDisconnect();";
+ }
+
+ @Override
+ public String onAjaxClientUpdate() {
+ updateFrontEndActivityTime();
+ if(!waitForViewerReady())
+ return onAjaxClientDisconnected();
+
+ synchronized(tileDirtyEvent) {
+ if(!dirtyFlag) {
+ try {
+ tileDirtyEvent.wait(3000);
+ } catch(InterruptedException e) {
+ }
+ }
+ }
+
+ boolean doResize = false;
+ synchronized(this) {
+ if(framebufferResized) {
+ framebufferResized = false;
+ doResize = true;
+ }
+ }
+
+ List<TileInfo> tiles;
+
+ if(doResize)
+ tiles = tracker.scan(true);
+ else
+ tiles = tracker.scan(false);
+ dirtyFlag = false;
+
+ String imgUrl = prepareAjaxImage(tiles, false);
+ StringBuffer sbTileSequence = new StringBuffer();
+ int i = 0;
+ for(TileInfo tile : tiles) {
+ sbTileSequence.append("[").append(tile.getRow()).append(",").append(tile.getCol()).append("]");
+ if(i < tiles.size() - 1)
+ sbTileSequence.append(",");
+
+ i++;
+ }
+
+ return getAjaxViewerUpdatePageContent(sbTileSequence.toString(), imgUrl, doResize,
+ resizedFramebufferWidth, resizedFramebufferHeight,
+ tracker.getTileWidth(), tracker.getTileHeight());
+ }
+
+ private String getAjaxViewerUpdatePageContent(String tileSequence, String imgUrl, boolean resized, int width,
+ int height, int tileWidth, int tileHeight) {
+
+ String[] content = new String[] {
+ "tileMap = [ " + tileSequence + " ];",
+ resized ? "ajaxViewer.resize('main_panel', " + width + ", " + height + " , " + tileWidth + ", " + tileHeight + ");" : "",
+ "ajaxViewer.refresh('" + imgUrl + "', tileMap, false);"
+ };
+
+ StringBuffer sb = new StringBuffer();
+ for(int i = 0; i < content.length; i++)
+ sb.append(content[i]);
+
+ return sb.toString();
+ }
+
+ //
+ // Helpers
+ //
+ private synchronized static int getNextClientId() {
+ return ++s_nextClientId;
+ }
+
+ private void signalTileDirtyEvent() {
+ synchronized(tileDirtyEvent) {
+ dirtyFlag = true;
+ tileDirtyEvent.notifyAll();
+ }
+ }
+
+ public void updateFrontEndActivityTime() {
+ lastFrontEndActivityTime = System.currentTimeMillis();
+ }
+
+ protected abstract FrameBufferCanvas getFrameBufferCavas();
+
+ public ConsoleProxyClientParam getClientParam() {
+ return clientParam;
+ }
+
+ public void setClientParam(ConsoleProxyClientParam clientParam) {
+ this.clientParam = clientParam;
+ ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor(ConsoleProxy.getEncryptorPassword());
+ this.clientToken = encryptor.encryptObject(ConsoleProxyClientParam.class, clientParam);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4869f0ca/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java
----------------------------------------------------------------------
diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java
new file mode 100644
index 0000000..43a0bab
--- /dev/null
+++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientListener.java
@@ -0,0 +1,25 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.consoleproxy;
+
+public interface ConsoleProxyClientListener {
+ void onFramebufferSizeChange(int w, int h);
+ void onFramebufferUpdate(int x, int y, int w, int h);
+
+ void onClientConnected();
+ void onClientClose();
+}
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4869f0ca/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java
----------------------------------------------------------------------
diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java
new file mode 100644
index 0000000..8de4955
--- /dev/null
+++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientParam.java
@@ -0,0 +1,110 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.consoleproxy;
+
+/**
+ *
+ * Data object to store parameter info needed by client to connect to its host
+ */
+public class ConsoleProxyClientParam {
+
+ private String clientHostAddress;
+ private int clientHostPort;
+ private String clientHostPassword;
+ private String clientTag;
+ private String ticket;
+
+ private String clientTunnelUrl;
+ private String clientTunnelSession;
+
+ private String ajaxSessionId;
+
+ public ConsoleProxyClientParam() {
+ clientHostPort = 0;
+ }
+
+ public String getClientHostAddress() {
+ return clientHostAddress;
+ }
+
+ public void setClientHostAddress(String clientHostAddress) {
+ this.clientHostAddress = clientHostAddress;
+ }
+
+ public int getClientHostPort() {
+ return clientHostPort;
+ }
+
+ public void setClientHostPort(int clientHostPort) {
+ this.clientHostPort = clientHostPort;
+ }
+
+ public String getClientHostPassword() {
+ return clientHostPassword;
+ }
+
+ public void setClientHostPassword(String clientHostPassword) {
+ this.clientHostPassword = clientHostPassword;
+ }
+
+ public String getClientTag() {
+ return clientTag;
+ }
+
+ public void setClientTag(String clientTag) {
+ this.clientTag = clientTag;
+ }
+
+ public String getTicket() {
+ return ticket;
+ }
+
+ public void setTicket(String ticket) {
+ this.ticket = ticket;
+ }
+
+ public String getClientTunnelUrl() {
+ return clientTunnelUrl;
+ }
+
+ public void setClientTunnelUrl(String clientTunnelUrl) {
+ this.clientTunnelUrl = clientTunnelUrl;
+ }
+
+ public String getClientTunnelSession() {
+ return clientTunnelSession;
+ }
+
+ public void setClientTunnelSession(String clientTunnelSession) {
+ this.clientTunnelSession = clientTunnelSession;
+ }
+
+ public String getAjaxSessionId() {
+ return this.ajaxSessionId;
+ }
+
+ public void setAjaxSessionId(String ajaxSessionId) {
+ this.ajaxSessionId = ajaxSessionId;
+ }
+
+ public String getClientMapKey() {
+ if(clientTag != null && !clientTag.isEmpty())
+ return clientTag;
+
+ return clientHostAddress + ":" + clientHostPort;
+ }
+}