/*******************************************************************************
* 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/>.
*******************************************************************************/
/**
*/
package userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.dataObjects;
import java.nio.ByteBuffer;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import staticContent.framework.util.Util;
/**
* @author bash * Cache.java
*
* This class represents the cache. It is needed for improvements with
* caching. The underlying construct is a hashtable. This hashtable
* contains the cacheentry and an identifier as string. There is also a
* capacity. There is no obligation to use a capacity but then the cache
* may grow without border.
*
*/
public class Cache {
// Eventlist
private Hashtable<String, LinkedList<Connection>> eventList;
// Capacity
private int size;
private ConcurrentLinkedQueue<String> capacityQueue;
private Hashtable<String, CacheEntry> cacheStorage;
// private ConcurrentHashMap<Integer, Connection> connectionMap;
/**
* Standard constructor
*
* @param ConcurrentHashMap
* with ConnectionId and connection
*/
public Cache(int size, ConcurrentHashMap<Integer, Connection> connectionMap) {
this.size = size;
cacheStorage = new Hashtable<String, CacheEntry>();
// this.connectionMap = connectionMap;
this.capacityQueue = new ConcurrentLinkedQueue<String>();
eventList = new Hashtable<String, LinkedList<Connection>>();
}
/**
* Add event to eventlist
*
* @param the
* identifier of the response (the request)
* @param event
* from the connection
*/
public void addEventToEventList(String identifier, Connection connection) {
if (!eventList.containsKey(identifier)) {
eventList.put(identifier, new LinkedList<Connection>());
}
eventList.get(identifier).add(connection);
}
/**
* Methode wird von EntryDataToMix aufgerufen
*
* @param connection
*/
public void invokeEventsOfConnection(Connection connection, String uri) {
Cache cache = connection.getCache();
if (connection.getIdentifierQueue().size() == 0) {
synchronized (eventList) {
if (cache.containsEntry(uri) && cache.isMessageComplete(uri)) {
byte[] nachricht = cache.getCacheEntry(uri);
connection.writeChunk(ByteBuffer.wrap(nachricht));
// connection.writeChunk(ByteBuffer.wrap(cache.getCacheEntry(uri)));
} else {
addEventToEventList(uri, connection);
connection.getIdentifierQueue().add(uri);
}
}
} else {
connection.getIdentifierQueue().add(uri);
}
}
/**
* Invoke the registered event for a given request
* Only call if method complete
*
* @param identifier
* from the request
*/
public void invokeEvents(String identifier) {
// Check if Event is registered for identifier
synchronized (eventList) {
if (!eventList.containsKey(identifier)) {
return;
}
// iterate all registered events
for (Connection connection : eventList.get(identifier)) {
String uri = identifier;
do {
connection.writeData(getCacheEntry(uri));
uri = connection.getIdentifierQueue().peek();
} while (uri != null && isMessageComplete(uri));
if (connection.getIdentifierQueue().size() > 0) {
addEventToEventList(uri, connection);
}
}
eventList.remove(identifier);
}
}
/**
* Adds a new cacheentry to the cache
* The new entry is a byte[]
*
* @param identifier
* @param cacheentry
* as byte[]
*/
public void addNewCacheEntry(String identifier, byte[] cache) {
CacheEntry value = new CacheEntry(false, false, cache);
cacheStorage.put(identifier, value);
capacityQueue.add(identifier);
if (cacheStorage.size() >= size) {
cacheStorage.remove(capacityQueue.poll());
}
}
/**
* Same as addNewCacheEntry but awaits an cacheentry as parameter
*
* @param identifier
* @param cache
*/
public void addCacheEntry(String identifier, CacheEntry cache) {
cacheStorage.put(identifier, cache);
capacityQueue.add(identifier);
if (cacheStorage.size() >= size) {
cacheStorage.remove(capacityQueue.poll());
}
}
/**
* Append a body to an existing cacheentry
*
* @param message
*/
public void appendMessageToEntry(String identifier, byte[] message) {
byte[] cacheEntry = cacheStorage.get(identifier).getCache();
cacheEntry = Util.concatArrays(cacheEntry, message);
cacheStorage.get(identifier).setCache(cacheEntry);
}
/**
* Set the completeflag for an entry
*
* @param identifier
* @param isComplete
*/
public void setMessageComplete(String identifier, boolean isComplete) {
cacheStorage.get(identifier).setComplete(isComplete);
}
/**
* Return if entry complete
*
* @param identifier
* @return true if a entry is complete, false otherwise
*/
public boolean isMessageComplete(String identifier) {
return cacheStorage.get(identifier).isComplete();
}
/**
* Method to modify a cacheentry
* This flag show if a connection requested an entry
*
* @param identifier
* @param connectionId
*/
public void setBrowserRequest(String identifier, int connectionId) {
try{
cacheStorage.get(identifier).setBrowserRequest(true, connectionId);
} catch (Exception e){
}
}
/**
* Method to modify a cacheentry
* This flag shows if an entry is already requested by the exitnode
*
* @param identifier
*/
public void setWebRequest(String identifier) {
cacheStorage.get(identifier).setWebRequest(true);
}
/**
* Returns a cacheentry by its identifier
*
* @param identifier
* @return cacheentry or null if entry not exists
*/
public byte[] getCacheEntry(String identifier) {
if (cacheStorage.containsKey(identifier)) {
return cacheStorage.get(identifier).getCache();
} else {
return null;
}
}
/**
* Method to check if entry exists
*
* @param identifier
* @return if entry already in the cache
*/
public boolean containsEntry(String identifier) {
return cacheStorage.containsKey(identifier);
}
/**
* Shows if entry is requested by exitnode
*
* @param identifier
* @return true if requested, false otherwise
*/
public boolean getWebRequestStatus(String identifier) {
return cacheStorage.get(identifier).isWebRequest();
}
/**
* Shows if entry is requested by a connection
*
* @param identifier
* @return true if requested, false otherwise
*/
public boolean getBrowserRequestStatus(String identifier) {
return cacheStorage.get(identifier).isBrowserRequest();
}
/**
* Debug method, dumps complete cache
*
* @return complete cache
*/
public String getCompleteCache() {
String returnString = "CacheSize: " + cacheStorage.size() + "\n" + "\n";
Set<String> keys = cacheStorage.keySet();
for (String key : keys) {
returnString = returnString + key + ": " + cacheStorage.get(key) + " " + cacheStorage.get(key).isComplete()
+ "\r\n";
}
return returnString;
}
/**
* Debug method, dumps complete eventlist
*
* @return complete eventlist
*/
public String getCompleteEventList() {
String returnString = "EventListSize: " + eventList.size() + "\n";
Set<String> keys = eventList.keySet();
for (String key : keys) {
returnString = returnString + key + ": " + eventList.get(key) + "\r\n";
}
return returnString;
}
}