org.apache.guacamole.io.ReaderGuacamoleReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of guacamole-common Show documentation
Show all versions of guacamole-common Show documentation
The base Java API of the Guacamole project, providing Java support for
the Guacamole stack.
/*
* 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 org.apache.guacamole.io;
import java.io.IOException;
import java.io.Reader;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Deque;
import java.util.LinkedList;
import org.apache.guacamole.GuacamoleConnectionClosedException;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
import org.apache.guacamole.protocol.GuacamoleInstruction;
/**
* A GuacamoleReader which wraps a standard Java Reader, using that Reader as
* the Guacamole instruction stream.
*
* @author Michael Jumper
*/
public class ReaderGuacamoleReader implements GuacamoleReader {
/**
* Wrapped Reader to be used for all input.
*/
private Reader input;
/**
* Creates a new ReaderGuacamoleReader which will use the given Reader as
* the Guacamole instruction stream.
*
* @param input The Reader to use as the Guacamole instruction stream.
*/
public ReaderGuacamoleReader(Reader input) {
this.input = input;
}
/**
* The location within the received data buffer that parsing should begin
* when more data is read.
*/
private int parseStart;
/**
* The buffer holding all received, unparsed data.
*/
private char[] buffer = new char[20480];
/**
* The number of characters currently used within the data buffer. All
* other characters within the buffer are free space available for
* future reads.
*/
private int usedLength = 0;
@Override
public boolean available() throws GuacamoleException {
try {
return input.ready() || usedLength != 0;
}
catch (IOException e) {
throw new GuacamoleServerException(e);
}
}
@Override
public char[] read() throws GuacamoleException {
try {
// While we're blocking, or input is available
for (;;) {
// Length of element
int elementLength = 0;
// Resume where we left off
int i = parseStart;
// Parse instruction in buffer
while (i < usedLength) {
// Read character
char readChar = buffer[i++];
// If digit, update length
if (readChar >= '0' && readChar <= '9')
elementLength = elementLength * 10 + readChar - '0';
// If not digit, check for end-of-length character
else if (readChar == '.') {
// Check if element present in buffer
if (i + elementLength < usedLength) {
// Get terminator
char terminator = buffer[i + elementLength];
// Move to character after terminator
i += elementLength + 1;
// Reset length
elementLength = 0;
// Continue here if necessary
parseStart = i;
// If terminator is semicolon, we have a full
// instruction.
if (terminator == ';') {
// Copy instruction data
char[] instruction = new char[i];
System.arraycopy(buffer, 0, instruction, 0, i);
// Update buffer
usedLength -= i;
parseStart = 0;
System.arraycopy(buffer, i, buffer, 0, usedLength);
return instruction;
}
// Handle invalid terminator characters
else if (terminator != ',')
throw new GuacamoleServerException("Element terminator of instruction was not ';' nor ','");
}
// Otherwise, read more data
else
break;
}
// Otherwise, parse error
else
throw new GuacamoleServerException("Non-numeric character in element length.");
}
// If past threshold, resize buffer before reading
if (usedLength > buffer.length/2) {
char[] biggerBuffer = new char[buffer.length*2];
System.arraycopy(buffer, 0, biggerBuffer, 0, usedLength);
buffer = biggerBuffer;
}
// Attempt to fill buffer
int numRead = input.read(buffer, usedLength, buffer.length - usedLength);
if (numRead == -1)
return null;
// Update used length
usedLength += numRead;
} // End read loop
}
catch (SocketTimeoutException e) {
throw new GuacamoleUpstreamTimeoutException("Connection to guacd timed out.", e);
}
catch (SocketException e) {
throw new GuacamoleConnectionClosedException("Connection to guacd is closed.", e);
}
catch (IOException e) {
throw new GuacamoleServerException(e);
}
}
@Override
public GuacamoleInstruction readInstruction() throws GuacamoleException {
// Get instruction
char[] instructionBuffer = read();
// If EOF, return EOF
if (instructionBuffer == null)
return null;
// Start of element
int elementStart = 0;
// Build list of elements
Deque elements = new LinkedList();
while (elementStart < instructionBuffer.length) {
// Find end of length
int lengthEnd = -1;
for (int i=elementStart; i