API improvements

This commit is contained in:
Michael Jumper
2011-01-02 02:36:31 -08:00
parent bda0d83ff1
commit 1430bed43e
10 changed files with 263 additions and 387 deletions

View File

@@ -1,6 +1,8 @@
package net.sourceforge.guacamole.net; package net.sourceforge.guacamole.net;
import java.util.HashMap;
/* /*
* Guacamole - Clientless Remote Desktop * Guacamole - Clientless Remote Desktop
* Copyright (C) 2010 Michael Jumper * Copyright (C) 2010 Michael Jumper
@@ -19,111 +21,25 @@ package net.sourceforge.guacamole.net;
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import javax.servlet.ServletContext; public class Configuration {
import net.sourceforge.guacamole.GuacamoleException;
public abstract class Configuration { private String protocol;
private HashMap<String, String> parameters = new HashMap<String, String>();
protected String humanReadableList(Object... values) {
String list = "";
for (int i=0; i<values.length; i++) {
if (i >= 1)
list += ", ";
if (i == values.length -1)
list += " or ";
list += "\"" + values[i] + "\"";
}
return list;
public String getProtocol() {
return protocol;
} }
protected String readParameter(String name) throws GuacamoleException { public void setProtocol(String protocol) {
String value = GuacamoleProperties.getProperty(name); this.protocol = protocol;
return value;
} }
protected String readParameter(String name, String defaultValue, String... allowedValues) throws GuacamoleException { public String getParameter(String name) {
return parameters.get(name);
String value = GuacamoleProperties.getProperty(name);
// Use default if not specified
if (value == null) {
if (defaultValue == null)
throw new GuacamoleException("Parameter \"" + name + "\" is required.");
return defaultValue;
}
// If not restricted to certain values, just return whatever is given.
if (allowedValues.length == 0)
return value;
// If restricted, only return value within given list
for (String allowedValue : allowedValues)
if (value.equals(allowedValue))
return value;
throw new GuacamoleException("Parameter \"" + name + "\" must be " + humanReadableList((Object) allowedValues));
} }
protected boolean readBooleanParameter(String name, Boolean defaultValue) throws GuacamoleException { public void setParameter(String name, String value) {
parameters.put(name, value);
String value = GuacamoleProperties.getProperty(name);
// Use default if not specified
if (value == null) {
if (defaultValue == null)
throw new GuacamoleException("Parameter \"" + name + "\" is required.");
return defaultValue;
}
value = value.trim();
if (value.equals("true"))
return true;
if (value.equals("false"))
return false;
throw new GuacamoleException("Parameter \"" + name + "\" must be \"true\" or \"false\".");
}
protected int readIntParameter(String name, Integer defaultValue, Integer... allowedValues) throws GuacamoleException {
String parmString = GuacamoleProperties.getProperty(name);
// Use default if not specified
if (parmString== null) {
if (defaultValue == null)
throw new GuacamoleException("Parameter \"" + name + "\" is required.");
return defaultValue;
}
try {
int value = Integer.parseInt(parmString);
// If not restricted to certain values, just return whatever is given.
if (allowedValues.length == 0)
return value;
// If restricted, only return value within given list
for (int allowedValue : allowedValues)
if (value == allowedValue)
return value;
throw new GuacamoleException("Parameter \"" + name + "\" must be " + humanReadableList((Object) allowedValues));
}
catch (NumberFormatException e) {
throw new GuacamoleException("Parameter \"" + name + "\" must be an integer.", e);
}
} }
} }

View File

@@ -1,80 +0,0 @@
package net.sourceforge.guacamole.net;
/*
* Guacamole - Clientless Remote Desktop
* Copyright (C) 2010 Michael Jumper
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import net.sourceforge.guacamole.net.authentication.GuacamoleSessionProvider;
import java.lang.reflect.InvocationTargetException;
import javax.servlet.http.HttpSession;
import net.sourceforge.guacamole.GuacamoleException;
public class GuacamoleConfiguration extends Configuration {
private String guacd_hostname;
private int guacd_port;
private GuacamoleSessionProvider sessionProvider;
public GuacamoleConfiguration() throws GuacamoleException {
guacd_hostname = readParameter("guacd-hostname");
guacd_port = readIntParameter("guacd-port", null);
// Get session provider instance
try {
String sessionProviderClassName = readParameter("session-provider");
Object obj = Class.forName(sessionProviderClassName).getConstructor().newInstance();
if (!(obj instanceof GuacamoleSessionProvider))
throw new GuacamoleException("Specified session provider class is not a GuacamoleSessionProvider");
sessionProvider = (GuacamoleSessionProvider) obj;
}
catch (ClassNotFoundException e) {
throw new GuacamoleException("Session provider class not found", e);
}
catch (NoSuchMethodException e) {
throw new GuacamoleException("Default constructor for session provider not present", e);
}
catch (SecurityException e) {
throw new GuacamoleException("Creation of session provider disallowed; check your security settings", e);
}
catch (InstantiationException e) {
throw new GuacamoleException("Unable to instantiate session provider", e);
}
catch (IllegalAccessException e) {
throw new GuacamoleException("Unable to access default constructor of session provider", e);
}
catch (InvocationTargetException e) {
throw new GuacamoleException("Internal error in constructor of session provider", e.getTargetException());
}
}
public int getProxyPort() {
return guacd_port;
}
public String getProxyHostname() {
return guacd_hostname;
}
public GuacamoleSession createSession(HttpSession session) throws GuacamoleException {
return sessionProvider.createSession(session);
}
}

View File

@@ -21,8 +21,10 @@ package net.sourceforge.guacamole.net;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties; import java.util.Properties;
import net.sourceforge.guacamole.GuacamoleException; import net.sourceforge.guacamole.GuacamoleException;
import net.sourceforge.guacamole.net.authentication.GuacamoleClientProvider;
public class GuacamoleProperties { public class GuacamoleProperties {
@@ -47,9 +49,146 @@ public class GuacamoleProperties {
} }
public static String getProxyHostname() throws GuacamoleException {
return GuacamoleProperties.getProperty("guacd-hostname");
}
public static int getProxyPort() throws GuacamoleException {
return GuacamoleProperties.getIntProperty("guacd-port", null);
}
public static GuacamoleClientProvider getClientProvider() throws GuacamoleException {
// Get client provider instance
try {
String sessionProviderClassName = GuacamoleProperties.getProperty("client-provider");
Object obj = Class.forName(sessionProviderClassName).getConstructor().newInstance();
if (!(obj instanceof GuacamoleClientProvider))
throw new GuacamoleException("Specified client provider class is not a GuacamoleClientProvider");
return (GuacamoleClientProvider) obj;
}
catch (ClassNotFoundException e) {
throw new GuacamoleException("Session provider class not found", e);
}
catch (NoSuchMethodException e) {
throw new GuacamoleException("Default constructor for client provider not present", e);
}
catch (SecurityException e) {
throw new GuacamoleException("Creation of client provider disallowed; check your security settings", e);
}
catch (InstantiationException e) {
throw new GuacamoleException("Unable to instantiate client provider", e);
}
catch (IllegalAccessException e) {
throw new GuacamoleException("Unable to access default constructor of client provider", e);
}
catch (InvocationTargetException e) {
throw new GuacamoleException("Internal error in constructor of client provider", e.getTargetException());
}
}
public static String getProperty(String name) throws GuacamoleException { public static String getProperty(String name) throws GuacamoleException {
if (exception != null) throw exception; if (exception != null) throw exception;
return properties.getProperty(name); return properties.getProperty(name);
} }
protected static String humanReadableList(Object... values) {
String list = "";
for (int i=0; i<values.length; i++) {
if (i >= 1)
list += ", ";
if (i == values.length -1)
list += " or ";
list += "\"" + values[i] + "\"";
}
return list;
}
public static String getProperty(String name, String defaultValue, String... allowedValues) throws GuacamoleException {
String value = getProperty(name);
// Use default if not specified
if (value == null) {
if (defaultValue == null)
throw new GuacamoleException("Parameter \"" + name + "\" is required.");
return defaultValue;
}
// If not restricted to certain values, just return whatever is given.
if (allowedValues.length == 0)
return value;
// If restricted, only return value within given list
for (String allowedValue : allowedValues)
if (value.equals(allowedValue))
return value;
throw new GuacamoleException("Parameter \"" + name + "\" must be " + humanReadableList((Object) allowedValues));
}
public static boolean getBooleanProperty(String name, Boolean defaultValue) throws GuacamoleException {
String value = getProperty(name);
// Use default if not specified
if (value == null) {
if (defaultValue == null)
throw new GuacamoleException("Parameter \"" + name + "\" is required.");
return defaultValue;
}
value = value.trim();
if (value.equals("true"))
return true;
if (value.equals("false"))
return false;
throw new GuacamoleException("Parameter \"" + name + "\" must be \"true\" or \"false\".");
}
public static int getIntProperty(String name, Integer defaultValue, Integer... allowedValues) throws GuacamoleException {
String parmString = getProperty(name);
// Use default if not specified
if (parmString== null) {
if (defaultValue == null)
throw new GuacamoleException("Parameter \"" + name + "\" is required.");
return defaultValue;
}
try {
int value = Integer.parseInt(parmString);
// If not restricted to certain values, just return whatever is given.
if (allowedValues.length == 0)
return value;
// If restricted, only return value within given list
for (int allowedValue : allowedValues)
if (value == allowedValue)
return value;
throw new GuacamoleException("Parameter \"" + name + "\" must be " + humanReadableList((Object) allowedValues));
}
catch (NumberFormatException e) {
throw new GuacamoleException("Parameter \"" + name + "\" must be an integer.", e);
}
}
} }

View File

@@ -1,83 +0,0 @@
package net.sourceforge.guacamole.net;
/*
* Guacamole - Clientless Remote Desktop
* Copyright (C) 2010 Michael Jumper
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import net.sourceforge.guacamole.GuacamoleException;
public abstract class GuacamoleServlet extends HttpServlet {
private GuacamoleConfiguration config;
@Override
public void init() throws ServletException {
try {
this.config = new GuacamoleConfiguration();
}
catch (GuacamoleException e) {
throw new ServletException(e);
}
}
@Override
protected final void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
handleRequest(req, resp);
}
catch (GuacamoleException e) {
throw new ServletException(e);
}
}
@Override
protected final void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
handleRequest(req, resp);
}
catch (GuacamoleException e) {
throw new ServletException(e);
}
}
private final void handleRequest(HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
HttpSession httpSession = request.getSession(shouldCreateSession());
if (httpSession != null) {
GuacamoleSession session = config.createSession(httpSession);
handleRequest(session, request, response);
}
else
throw new GuacamoleException("No session");
}
protected abstract void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException;
protected boolean shouldCreateSession() {
return false;
}
}

View File

@@ -19,7 +19,6 @@ package net.sourceforge.guacamole.net;
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import java.util.HashMap;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingEvent;
@@ -30,31 +29,10 @@ import net.sourceforge.guacamole.GuacamoleException;
public class GuacamoleSession { public class GuacamoleSession {
private GuacamoleConfiguration config;
private final HttpSession session; private final HttpSession session;
private SessionClient client; private SessionClient client;
private ReentrantLock instructionStreamLock; private ReentrantLock instructionStreamLock;
private String protocol;
private HashMap<String, String> parameters = new HashMap<String, String>();
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getParameter(String name) {
return parameters.get(name);
}
public void setParameter(String name, String value) {
parameters.put(name, value);
}
public class SessionClient extends Client implements HttpSessionBindingListener { public class SessionClient extends Client implements HttpSessionBindingListener {
private Client client; private Client client;
@@ -96,34 +74,22 @@ public class GuacamoleSession {
throw new GuacamoleException("User has no session."); throw new GuacamoleException("User has no session.");
this.session = session; this.session = session;
synchronized (session) {
// Read configuration parameters synchronized (session) {
config = new GuacamoleConfiguration();
client = (SessionClient) session.getAttribute("CLIENT"); client = (SessionClient) session.getAttribute("CLIENT");
instructionStreamLock = (ReentrantLock) session.getAttribute("INSTRUCTION_STREAM_LOCK"); instructionStreamLock = (ReentrantLock) session.getAttribute("INSTRUCTION_STREAM_LOCK");
} }
} }
public void connect() throws GuacamoleException { public void attachClient(GuacamoleClient client) throws GuacamoleException {
synchronized (session) { synchronized (session) {
if (client != null) this.client = new SessionClient(client);
client.disconnect(); session.setAttribute("CLIENT", this.client);
client = new SessionClient(
new GuacamoleClient (
config.getProxyHostname(),
config.getProxyPort()
)
);
// TODO: Send "select" and "connect" messages here.
session.setAttribute("CLIENT", client);
instructionStreamLock = new ReentrantLock(); instructionStreamLock = new ReentrantLock();
session.setAttribute("INSTRUCTION_STREAM_LOCK", instructionStreamLock); session.setAttribute("INSTRUCTION_STREAM_LOCK", instructionStreamLock);
@@ -132,21 +98,11 @@ public class GuacamoleSession {
} }
public boolean isConnected() {
synchronized (session) {
return client != null;
}
}
public GuacamoleConfiguration getConfiguration() {
return config;
}
public SessionClient getClient() throws GuacamoleException { public SessionClient getClient() throws GuacamoleException {
synchronized (session) { synchronized (session) {
if (client == null) if (client == null)
throw new GuacamoleException("Client not yet connected."); throw new GuacamoleException("Client not yet attached.");
return client; return client;
} }
@@ -156,7 +112,7 @@ public class GuacamoleSession {
session.invalidate(); session.invalidate();
} }
public void disconnect() throws GuacamoleException { public void detachClient() throws GuacamoleException {
synchronized (session) { synchronized (session) {

View File

@@ -2,8 +2,8 @@
package net.sourceforge.guacamole.net.authentication; package net.sourceforge.guacamole.net.authentication;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import net.sourceforge.guacamole.GuacamoleClient;
import net.sourceforge.guacamole.GuacamoleException; import net.sourceforge.guacamole.GuacamoleException;
import net.sourceforge.guacamole.net.GuacamoleSession;
/* /*
* Guacamole - Clientless Remote Desktop * Guacamole - Clientless Remote Desktop
@@ -23,8 +23,8 @@ import net.sourceforge.guacamole.net.GuacamoleSession;
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
public interface GuacamoleSessionProvider { public interface GuacamoleClientProvider {
public GuacamoleSession createSession(HttpSession session) throws GuacamoleException; public GuacamoleClient createClient(HttpSession session) throws GuacamoleException;
} }

View File

@@ -2,8 +2,8 @@
package net.sourceforge.guacamole.net.authentication; package net.sourceforge.guacamole.net.authentication;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import net.sourceforge.guacamole.GuacamoleClient;
import net.sourceforge.guacamole.GuacamoleException; import net.sourceforge.guacamole.GuacamoleException;
import net.sourceforge.guacamole.net.GuacamoleSession;
/* /*
* Guacamole - Clientless Remote Desktop * Guacamole - Clientless Remote Desktop
@@ -23,10 +23,10 @@ import net.sourceforge.guacamole.net.GuacamoleSession;
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
public class NullGuacamoleSessionProvider implements GuacamoleSessionProvider { public class NullGuacamoleClientProvider implements GuacamoleClientProvider {
public GuacamoleSession createSession(HttpSession session) throws GuacamoleException { public GuacamoleClient createClient(HttpSession session) throws GuacamoleException {
throw new GuacamoleException("Null provider will not create sessions"); throw new GuacamoleException("Null provider will not create clients.");
} }
} }

View File

@@ -18,30 +18,35 @@ package net.sourceforge.guacamole.net.tunnel;
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import net.sourceforge.guacamole.GuacamoleException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import net.sourceforge.guacamole.net.GuacamoleServlet; import javax.servlet.http.HttpSession;
import net.sourceforge.guacamole.GuacamoleException;
import net.sourceforge.guacamole.net.GuacamoleProperties;
import net.sourceforge.guacamole.net.GuacamoleSession; import net.sourceforge.guacamole.net.GuacamoleSession;
public class Connect extends GuacamoleServlet {
public class Connect extends HttpServlet {
@Override @Override
protected boolean shouldCreateSession() { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {
return true;
}
@Override HttpSession httpSession = request.getSession(false);
protected void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
// Disconnect if already connected try {
if (session.isConnected())
session.disconnect();
// Obtain new connection GuacamoleSession session = new GuacamoleSession(httpSession);
session.connect(); session.attachClient(
GuacamoleProperties.getClientProvider().createClient(httpSession)
);
}
catch (GuacamoleException e) {
throw new ServletException(e);
}
} }

View File

@@ -22,33 +22,43 @@ import net.sourceforge.guacamole.GuacamoleException;
import java.io.Reader; import java.io.Reader;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import net.sourceforge.guacamole.net.GuacamoleServlet; import javax.servlet.http.HttpSession;
import net.sourceforge.guacamole.net.GuacamoleSession; import net.sourceforge.guacamole.net.GuacamoleSession;
public class Inbound extends GuacamoleServlet {
public class Inbound extends HttpServlet {
@Override @Override
protected void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {
// Send data HttpSession httpSession = request.getSession(false);
try { try {
Reader input = request.getReader(); GuacamoleSession session = new GuacamoleSession(httpSession);
char[] buffer = new char[8192];
int length; // Send data
while ((length = input.read(buffer, 0, buffer.length)) != -1) try {
session.getClient().write(buffer, 0, length);
Reader input = request.getReader();
char[] buffer = new char[8192];
int length;
while ((length = input.read(buffer, 0, buffer.length)) != -1)
session.getClient().write(buffer, 0, length);
}
catch (IOException e) {
throw new GuacamoleException("I/O Error sending data to server: " + e.getMessage(), e);
}
}
catch (IOException e) {
throw new GuacamoleException("I/O Error sending data to server: " + e.getMessage(), e);
} }
catch (GuacamoleException e) { catch (GuacamoleException e) {
throw new GuacamoleException("Error sending data to server: " + e.getMessage(), e); throw new ServletException(e);
} }
} }

View File

@@ -22,73 +22,86 @@ import java.io.Writer;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import net.sourceforge.guacamole.Client; import net.sourceforge.guacamole.Client;
import net.sourceforge.guacamole.net.GuacamoleServlet;
import net.sourceforge.guacamole.GuacamoleException; import net.sourceforge.guacamole.GuacamoleException;
import net.sourceforge.guacamole.net.GuacamoleSession; import net.sourceforge.guacamole.net.GuacamoleSession;
public class Outbound extends GuacamoleServlet { public class Outbound extends HttpServlet {
@Override @Override
protected void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {
ReentrantLock instructionStreamLock = session.getInstructionStreamLock(); HttpSession httpSession = request.getSession(false);
instructionStreamLock.lock();
try { try {
response.setContentType("text/plain"); GuacamoleSession session = new GuacamoleSession(httpSession);
Writer out = response.getWriter();
ReentrantLock instructionStreamLock = session.getInstructionStreamLock();
instructionStreamLock.lock();
try { try {
// Query new update from server response.setContentType("text/plain");
Client client = session.getClient(); Writer out = response.getWriter();
// For all messages, until another stream is ready (we send at least one message) try {
char[] message;
while ((message = client.read()) != null) {
// Get message output bytes // Query new update from server
out.write(message, 0, message.length); Client client = session.getClient();
// For all messages, until another stream is ready (we send at least one message)
char[] message;
while ((message = client.read()) != null) {
// Get message output bytes
out.write(message, 0, message.length);
out.flush();
response.flushBuffer();
// No more messages another stream can take over
if (instructionStreamLock.hasQueuedThreads())
break;
}
if (message == null) {
session.detachClient();
throw new GuacamoleException("Disconnected.");
}
}
catch (GuacamoleException e) {
out.write("error:" + e.getMessage() + ";");
out.flush(); out.flush();
response.flushBuffer(); response.flushBuffer();
// No more messages another stream can take over
if (instructionStreamLock.hasQueuedThreads())
break;
} }
if (message == null) { // End-of-instructions marker
session.disconnect(); out.write(';');
throw new GuacamoleException("Disconnected.");
}
}
catch (GuacamoleException e) {
out.write("error:" + e.getMessage() + ";");
out.flush(); out.flush();
response.flushBuffer(); response.flushBuffer();
}
catch (UnsupportedEncodingException e) {
throw new ServletException("UTF-8 not supported by Java.", e);
}
catch (IOException e) {
throw new ServletException("I/O error writing to servlet output stream.", e);
}
finally {
instructionStreamLock.unlock();
} }
// End-of-instructions marker
out.write(';');
out.flush();
response.flushBuffer();
} }
catch (UnsupportedEncodingException e) { catch (GuacamoleException e) {
throw new GuacamoleException("UTF-8 not supported by Java.", e); throw new ServletException(e);
}
catch (IOException e) {
throw new GuacamoleException("I/O error writing to servlet output stream.", e);
}
finally {
instructionStreamLock.unlock();
} }
} }