/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ /** * Connection.java * */ package userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.dataObjects; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.Hashtable; import java.util.LinkedList; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import staticContent.framework.clock.Clock; import staticContent.framework.config.Settings; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.mix.ConnectionPoolInterface; /** * @author bash * * * This class represent a connection. * * It contains all information that is necessary to handle a connection * This class is very important for the whole framework. * * A connection represents a socket which connects the nodes to a * webbrowser on entry side or to a webserver on exit side. Each * connection has a unique id. It knows the type of the incoming and * outgoing messages (see HttpPartType). Each direction is a * synchronized buffer to buffer the message exchange. * * Each connection writes its own log. The directory of the log is * specified in the properties file. * */ public class Connection { /** * ID of the Connection */ private int id; /** * Channel to Application via HTTP */ private SocketChannel serverSocket; /** * Variable for the responding connectionpool. The connectionpool manages * the connections */ public ConnectionPoolInterface selector; /** * Cache */ private Cache cache; /** * Logger is used for logging of connection events */ // public TimeLog logger; /** * The following three variables are in use for logging purposes. Do NOT * touch */ private int packageCounter; private ConcurrentLinkedQueue<Integer> packageCounterQueue; private Integer packageCounterTail; /** * Statusnotation of the connection is used for the communication via HTTP * statusHTTPFromApp symbolized the incoming connection from webbrowser or * webserver * statusHTTPFromMix symbolized the incoming connection from Mix */ private HttpInfo statusHTTPFromApp; private HttpInfo statusHTTPFromMix; /** * Buffers the message from mix or from connection to browser or webserver */ private SynchronizedBuffer mixBuffer; private SynchronizedBuffer connectionBuffer; private byte[] headerBuffer; /** * sendBuffer buffer an incomplete header. Only in use if header incomplete */ private String sendBuffer; /** * The mix connections checks following boolean to prevent double using of * the from two or more threads. Is true if a thread uses the connection */ private boolean inProgressReceive; private boolean inProgressSend; /** * The following booleans show if a message in a buffer is incomplete and * further messages are required */ private boolean mixMessageIncomplete; private boolean connectionMessageIncomplete; /** * The methodqueue contains the method of the sent request * Main reason: A HEAD-request has no body. This is the only possibility to detect it */ private ConcurrentLinkedQueue<String> methodQueue; /** * Counter for requests * May used to check the order of the responses * */ public int requestId; /** * Variable to link a header to the body (see activeCacheImprovement) */ public int actualRequestId; /** * Queue of requestIds */ public ConcurrentLinkedQueue<Integer> requestQueue; /** * Same as above but contains the uri of the request */ private LinkedList<String> identifierQueue; /** * Hashtable to maintain the correct order of messages from mix if * counterpart adds responses of its own * The key is the requestId * The value is a queue of uri in the correct order * * Example * Two initialrequests * RequestId 0 (index) and 1 (home) * * { 0: [ index ] 1: [ home ] } * * The counterpart generates two additional (img1 and img2) requests from * request 0 * * { 0: [ index, img1, img2 ] 1: [ home ] } * */ public Hashtable<Integer, Queue<String>> requestTable; /** * @param serverSocket * @param id * @param status */ public Connection(SocketChannel serverSocket, int id, Clock clock, Settings settings, ConnectionPoolInterface selector, Cache cache) { this.mixBuffer = new SynchronizedBuffer(); this.connectionBuffer = new SynchronizedBuffer(); this.serverSocket = serverSocket; this.id = id; packageCounter = 0; this.selector = selector; packageCounterQueue = new ConcurrentLinkedQueue<Integer>(); // logger = new TimeLog(settings.getProperty("LOG_PATH"), "connection" + // String.valueOf(id), clock); methodQueue = new ConcurrentLinkedQueue<String>(); inProgressReceive = false; inProgressSend = false; packageCounterTail = 0; this.cache = cache; requestId = 0; requestQueue = new ConcurrentLinkedQueue<Integer>(); identifierQueue = new LinkedList<String>(); sendBuffer = ""; this.requestTable = new Hashtable<Integer, Queue<String>>(); } /** * Method to write Data to the connection * * @param payload */ public void writeChunk(ByteBuffer payload) { synchronized (serverSocket) { try { // System.out.println("Connection: Start writting Message on Socket"); serverSocket.write(payload); while (payload.remaining() > 0) { Thread.sleep(5); serverSocket.write(payload); } // System.out.println("Connection: Message ( " + // payload.capacity() + " bytes) is written on Socket"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * Writes message to connction * * @param message */ public void writeData(byte[] message) { // System.out.println("-------------------------------------------------------"+new // String(message)); writeChunk(ByteBuffer.wrap(message)); identifierQueue.poll(); } /** * Adds a request to requestTable * * @param id * requestId * @param uri */ public void addIdToTable(int id, String uri) { if (!requestTable.containsKey(id)) { requestTable.put(id, new LinkedList<String>()); } requestTable.get(id).add(uri); } /** * Method to add the name of an HTTP method to a queue. Necessary to detect a * HEAD Request * * @param method */ public void addMethodToQueue(String method) { methodQueue.add(method); } /** * Returns the method of an HTTP Request * Necessary to detect a HEAD Request * * @return and remove method */ public String returnMethodFromQueue() { return methodQueue.poll(); } /** * Returns the method of an HTTP Request * Necessary to detect a HEAD Request * * @return method */ public String peekMethodFromQueue() { return methodQueue.peek(); } /** * Method to identify received Message * * @return messageID */ public int incrementPackageCounter() { packageCounterQueue.add(packageCounter); packageCounterTail = packageCounter; packageCounter += 1; return packageCounter - 1; } /** * Adds messageid to queue * * @param messageId */ public void addPackageCounter(int messageId) { packageCounterTail = messageId; packageCounterQueue.add(messageId); } /** * return messageid from queue but does not remove it * * @return messageid from top of queue */ public int peekPackageCounter() { return packageCounterQueue.peek(); } /** * returns last element of packagequeue * * @return last element of queue */ public int inspectPackageCounterTail() { return packageCounterTail; } /** * Method to identify sendt Messages * * @return messageID */ public int removeCounterFromList() { return packageCounterQueue.poll(); } /** * Add an element to buffer * * @param payload */ public void addElementToMixBuffer(byte[] payload) { mixBuffer.addArrayToBuffer(payload); } // Getter and Setter part /** * Checks if mixbuffer contains elements * * @return amount of bytes */ public boolean isMixBuffer() { if (mixBuffer.getByteSize() > 0) { return true; } else { return false; } } /** * Checks if ConnectionBuffer contains bytes * * @return true if the form the connection contains data, false otherwise */ public boolean isConnectionBuffer() { if (connectionBuffer.getByteSize() > 0) { return true; } else { return false; } } /** * Returns serverChannel * * @return serverChannel */ public SocketChannel getServerSocket() { return serverSocket; } /** * Returns the connection id * * @return connectionID */ public int getId() { return id; } /** * @return the cache */ public Cache getCache() { return cache; } /** * @return the requestQueue */ public LinkedList<String> getIdentifierQueue() { return identifierQueue; } /** * @param inProgressSend * the inProgressSend to set */ public void setInProgressSend(boolean inProgressSend) { this.inProgressSend = inProgressSend; } /** * @return the inProgressSend */ public boolean isInProgressSend() { return inProgressSend; } /** * @param inProgressReceive * the inProgressReceive to set */ public void setInProgressReceive(boolean inProgressReceive) { this.inProgressReceive = inProgressReceive; } /** * @return the inProgressReceive */ public boolean isInProgressReceive() { return inProgressReceive; } /** * @param statusHTTPFromMix * the statusHTTPFromMix to set */ public void setStatusHTTPFromMix(HttpInfo statusHTTPFromMix) { this.statusHTTPFromMix = statusHTTPFromMix; } /** * @return the statusHTTPFromMix */ public HttpInfo getStatusHTTPFromMix() { return statusHTTPFromMix; } /** * @param statusHTTPFromApp * the statusHTTPFromApp to set */ public void setStatusHTTPFromApp(HttpInfo statusHTTPFromApp) { this.statusHTTPFromApp = statusHTTPFromApp; } /** * @return the statusHTTPFromApp */ public HttpInfo getStatusHTTPFromApp() { return statusHTTPFromApp; } /** * @return the connectionBuffer */ public SynchronizedBuffer getConnectionBuffer() { return connectionBuffer; } /** * get the buffer for mix messages * * @return the synchronized buffer for messages from the mix */ public SynchronizedBuffer getMixBuffer() { return mixBuffer; } /** * @return the improvementBuffer */ public String getSendBuffer() { return sendBuffer; } /** * @param improvementBuffer * the improvementBuffer to set */ public void setSendBuffer(String improvementBuffer) { this.sendBuffer = improvementBuffer; } /** * @return the headerBuffer */ public byte[] getHeaderBuffer() { return headerBuffer; } /** * @param headerBuffer * the headerBuffer to set */ public void setHeaderBuffer(byte[] headerBuffer) { this.headerBuffer = headerBuffer; } /** * @return the mixMessageIncomplete */ public boolean isMixMessageIncomplete() { return mixMessageIncomplete; } /** * @param mixMessageIncomplete * the mixMessageIncomplete to set */ public void setMixMessageIncomplete(boolean mixMessageIncomplete) { this.mixMessageIncomplete = mixMessageIncomplete; } /** * @return the connectionMessageIncomplete */ public boolean isConnectionMessageIncomplete() { return connectionMessageIncomplete; } /** * @param connectionMessageIncomplete * the connectionMessageIncomplete to set */ public void setConnectionMessageIncomplete(boolean connectionMessageIncomplete) { this.connectionMessageIncomplete = connectionMessageIncomplete; } /** * Debugmethod to dump the complete requesttable * * @return the complete request table as string */ public String dumpRequestTable() { String returnString = "TableSize: " + requestTable.size() + "\n" + "\n"; Set<Integer> keys = requestTable.keySet(); for (int key : keys) { returnString = returnString + key + ": " + "\r\n"; for (String entry : requestTable.get(key)) { returnString = returnString + entry + "\r\n"; } } return returnString; } }