From 6e890b81e1824eedb964c14ed314e46f40eb94fa Mon Sep 17 00:00:00 2001 From: Ziver Koc Date: Sun, 5 Feb 2012 21:31:22 +0000 Subject: [PATCH] RESOLVED - # 99: Change StringBuffer deleteCharAt() to integer http://bugs.koc.se/view.php?id=99 Added Base64 decoder --- src/zutil/db/bean/DBBean.java | 7 + src/zutil/db/bean/DBBeanSQLResultHandler.java | 69 ++++++++- src/zutil/net/http/HttpServer.java | 9 +- .../net/http/soap/SOAPClientFactory.java | 1 + .../threaded/ThreadedTCPNetworkServer.java | 20 ++- .../net/threaded/ThreadedUDPNetwork.java | 9 +- src/zutil/parser/Base64Decoder.java | 145 ++++++++++++++++++ src/zutil/test/Base64Test.java | 48 ++++++ 8 files changed, 292 insertions(+), 16 deletions(-) create mode 100644 src/zutil/parser/Base64Decoder.java create mode 100644 src/zutil/test/Base64Test.java diff --git a/src/zutil/db/bean/DBBean.java b/src/zutil/db/bean/DBBean.java index d3dae93..d9abc3a 100644 --- a/src/zutil/db/bean/DBBean.java +++ b/src/zutil/db/bean/DBBean.java @@ -488,6 +488,13 @@ public abstract class DBBean { return id; } + /** + * This function cancels the internal cache garbage collector in DBBean + */ + public static void cancelGBC(){ + DBBeanSQLResultHandler.cancelGBC(); + } + /** * Will be called whenever the bean has been updated from the database. */ diff --git a/src/zutil/db/bean/DBBeanSQLResultHandler.java b/src/zutil/db/bean/DBBeanSQLResultHandler.java index 39d8122..5c608bd 100644 --- a/src/zutil/db/bean/DBBeanSQLResultHandler.java +++ b/src/zutil/db/bean/DBBeanSQLResultHandler.java @@ -26,11 +26,12 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.sql.ResultSet; -import java.util.Collections; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import zutil.db.DBConnection; @@ -42,10 +43,12 @@ import zutil.log.LogUtil; public class DBBeanSQLResultHandler implements SQLResultHandler{ public static final Logger logger = LogUtil.getLogger(); /** This is the time to live for the cached items **/ - public static final long CACHE_TTL = 1000*60*1; // 1 min in ms + public static final long CACHE_TTL = 1000*60*5; // 5 min in ms /** A cache for detecting recursion **/ protected static Map, Map> cache = - Collections.synchronizedMap(new HashMap, Map>()); + new ConcurrentHashMap, Map>(); + private static Timer timer; + /** * A cache container that contains a object and last read time */ @@ -113,8 +116,64 @@ public class DBBeanSQLResultHandler implements SQLResultHandler{ this.list = list; this.db = db; this.bean_config = DBBean.getBeanConfig( cl ); + + // Initiate DBBeanGarbageCollector + if( timer == null ){ + timer = new Timer( true ); // Run as daemon + timer.schedule( new DBBeanGarbageCollector(), 10000, CACHE_TTL ); + } } + /** + * This function cancels the internal cache garbage collector in DBBean + */ + public static void cancelGBC(){ + if( timer != null ){ + timer.cancel(); + timer = null; + } + } + + /** + * This class acts as an garbage collector that removes old DBBeans + * + * @author Ziver + */ + private static class DBBeanGarbageCollector extends TimerTask { + public void run(){ + logger.fine("DBBean GarbageCollector has started."); + if( cache == null ){ + logger.severe("DBBeanSQLResultHandler not initialized, stopping DBBeanGarbageCollector timer."); + this.cancel(); + return; + } + + int removed = 0; + long time = System.currentTimeMillis(); + Object[] class_keys = cache.keySet().toArray(); + for(Object key : class_keys){ + if( key == null ) continue; + + Map class_cache = cache.get(key); + Object[] bean_keys = class_cache.keySet().toArray(); + for(Object sub_key : bean_keys){ + if( sub_key == null ) continue; + + DBBeanCache beanCache = class_cache.get(sub_key); + // Check if session is still valid + if( beanCache.timestamp + CACHE_TTL*2 < time ){ + class_cache.remove(sub_key); + removed++; + logger.finer("Removing old DBBean(id: "+beanCache.bean.getId()+") from cache."); + } + } + } + + if( removed > 0 ) + logger.info("DBBeanGarbageCollector has cleard "+removed+" beans from cache."); + } + } + /** * Is called to handle a result from a query. * @@ -289,7 +348,7 @@ public class DBBeanSQLResultHandler implements SQLResultHandler{ if( cache.containsKey(obj.getClass()) ) cache.get(obj.getClass()).put(id, item); else{ - HashMap map = new HashMap(); + Map map = new ConcurrentHashMap(); map.put(id, item); cache.put(obj.getClass(), map); } diff --git a/src/zutil/net/http/HttpServer.java b/src/zutil/net/http/HttpServer.java index 97ba9a1..f411fbc 100644 --- a/src/zutil/net/http/HttpServer.java +++ b/src/zutil/net/http/HttpServer.java @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -54,7 +55,7 @@ public class HttpServer extends ThreadedTCPNetworkServer{ public final String server_url; public final int server_port; - private HashMap pages; + private Map pages; private HttpPage defaultPage; private Map> sessions; private int nextSessionId; @@ -83,12 +84,12 @@ public class HttpServer extends ThreadedTCPNetworkServer{ this.server_url = url; this.server_port = port; - pages = new HashMap(); - sessions = Collections.synchronizedMap(new HashMap>()); + pages = new ConcurrentHashMap(); + sessions = new ConcurrentHashMap>(); nextSessionId = 0; Timer timer = new Timer(); - timer.schedule(new GarbageCollector(), 0, SESSION_TTL / 2); + timer.schedule(new GarbageCollector(), 10000, SESSION_TTL / 2); logger.info("HTTP"+(keyStore==null?"":"S")+" Server ready!"); } diff --git a/src/zutil/net/http/soap/SOAPClientFactory.java b/src/zutil/net/http/soap/SOAPClientFactory.java index 2b879a1..cf7401e 100644 --- a/src/zutil/net/http/soap/SOAPClientFactory.java +++ b/src/zutil/net/http/soap/SOAPClientFactory.java @@ -37,6 +37,7 @@ import zutil.net.ws.WebServiceDef; * This is an factory that generates clients for web services * * @author Ziver + * TODO: Incomplete */ public class SOAPClientFactory { diff --git a/src/zutil/net/threaded/ThreadedTCPNetworkServer.java b/src/zutil/net/threaded/ThreadedTCPNetworkServer.java index 5eb0100..f04035e 100644 --- a/src/zutil/net/threaded/ThreadedTCPNetworkServer.java +++ b/src/zutil/net/threaded/ThreadedTCPNetworkServer.java @@ -94,12 +94,12 @@ public abstract class ThreadedTCPNetworkServer extends Thread{ } } catch(Exception e) { logger.log(Level.SEVERE, null, e); - } - - if( ss!=null ){ - try{ - ss.close(); - }catch(IOException e){ logger.log(Level.SEVERE, null, e); } + } finally { + if( ss!=null ){ + try{ + ss.close(); + }catch(IOException e){ logger.log(Level.SEVERE, null, e); } + } } } @@ -135,4 +135,12 @@ public abstract class ThreadedTCPNetworkServer extends Thread{ System.setProperty("javax.net.ssl.keyStore", keyStore.getAbsolutePath()); System.setProperty("javax.net.ssl.keyStorePassword", keyStorePass); } + + /** + * Stops the server and interrupts its internal thread. + * This is a permanent action that will not be able to recover from + */ + public void close(){ + this.interrupt(); + } } diff --git a/src/zutil/net/threaded/ThreadedUDPNetwork.java b/src/zutil/net/threaded/ThreadedUDPNetwork.java index 642ee9b..8131ad5 100644 --- a/src/zutil/net/threaded/ThreadedUDPNetwork.java +++ b/src/zutil/net/threaded/ThreadedUDPNetwork.java @@ -132,5 +132,12 @@ public class ThreadedUDPNetwork extends Thread{ this.thread = thread; } - + /** + * Stops the server and interrupts its internal thread. + * This is a permanent action that will not be able to recover from + */ + public void close(){ + this.interrupt(); + socket.close(); + } } diff --git a/src/zutil/parser/Base64Decoder.java b/src/zutil/parser/Base64Decoder.java new file mode 100644 index 0000000..1231730 --- /dev/null +++ b/src/zutil/parser/Base64Decoder.java @@ -0,0 +1,145 @@ +package zutil.parser; + +public class Base64Decoder { + public static final char[] B64_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', + 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', + 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + + '0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', '+', '/' + }; + + private StringBuilder output; + private byte rest_data; + private int rest = 0; + + public Base64Decoder(){ + output = new StringBuilder(); + } + + public void decode( String data ){ + byte[] buffer = new byte[ (data.length()*6/8) + 1 ]; + int buffi = 0; + if( rest != 0 ) + buffer[0] = rest_data; + + for( int i=0; i