diff --git a/.gitignore b/.gitignore
index 51eab7f..506d561 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
-target
+/target
cache.properties
-build
+/build
+/db
+
diff --git a/pom.xml b/pom.xml
index dbcddd9..2587ee3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -98,7 +98,7 @@
runtime
- com.drew
+ com.drewnoakes
metadata-extractor
2.3.1
@@ -108,11 +108,6 @@
2.4
provided
-
- net.sf.ehcache
- ehcache
- 1.5.0
-
log4j
log4j
@@ -123,15 +118,34 @@
junit
4.7
+
+ com.sleepycat
+ je
+ 4.0.92
+
+ com.cauchoresin3.1.8
- forkalsrud
+ central
forkalsrud.org maven proxy
true
- false
- http://forkalsrud.org:8080/maven-proxy/repository
- legacy
+ true
+ http://forkalsrud.org:8081/nexus/content/groups/public
+
diff --git a/src/org/forkalsrud/album/db/ThumbnailDatabase.java b/src/org/forkalsrud/album/db/ThumbnailDatabase.java
new file mode 100644
index 0000000..18f5e61
--- /dev/null
+++ b/src/org/forkalsrud/album/db/ThumbnailDatabase.java
@@ -0,0 +1,117 @@
+/**
+ *
+ */
+package org.forkalsrud.album.db;
+
+import java.io.File;
+import java.nio.charset.Charset;
+
+import org.forkalsrud.album.web.CachedImage;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseConfig;
+import com.sleepycat.je.DatabaseEntry;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.EnvironmentConfig;
+import com.sleepycat.je.OperationStatus;
+import com.sleepycat.je.Transaction;
+
+/**
+ * @author knut
+ *
+ */
+public class ThumbnailDatabase extends TupleBinding {
+
+ private static Charset UTF8 = Charset.forName("utf-8");
+
+ private Environment environment;
+ private Database db;
+
+ public void init(File dir) {
+
+ String dbname = "thumbnails";
+
+ EnvironmentConfig environmentConfig = new EnvironmentConfig();
+ environmentConfig.setAllowCreate(true);
+ environmentConfig.setTransactional(true);
+ // perform other environment configurations
+ environment = new Environment(dir, environmentConfig);
+ DatabaseConfig databaseConfig = new DatabaseConfig();
+ databaseConfig.setAllowCreate(true);
+ databaseConfig.setTransactional(true);
+ // perform other database configurations
+ this.db = environment.openDatabase(null, dbname, databaseConfig);
+ }
+
+ public void destroy() {
+ db.close();
+ environment.close();
+ }
+
+ public long size() {
+ return db.count();
+ }
+
+ public CachedImage load(String key) {
+
+ DatabaseEntry data = new DatabaseEntry();
+ Transaction txn = environment.beginTransaction(null, null);
+ OperationStatus status = db.get(txn, key(key), data, null);
+ txn.commitNoSync();
+ if (OperationStatus.SUCCESS.equals(status)) {
+ return entryToObject(data);
+ } else {
+ return null;
+ }
+ }
+
+ public void store(String key, CachedImage img) {
+
+ DatabaseEntry data = new DatabaseEntry();
+ objectToEntry(img, data);
+ DatabaseEntry binKey = key(key);
+ Transaction txn = environment.beginTransaction(null, null);
+ db.delete(txn, binKey);
+ db.put(txn, binKey, data);
+ txn.commitSync();
+ }
+
+
+ private DatabaseEntry key(String key) {
+ DatabaseEntry returnValue = new DatabaseEntry();
+ returnValue.setData(key.getBytes(UTF8));
+ return returnValue;
+ }
+
+
+ @Override
+ public CachedImage entryToObject(TupleInput in) {
+
+ CachedImage img = new CachedImage();
+ int version = in.readInt();
+ if (version != 1) {
+ throw new RuntimeException("I only understand version 1");
+ }
+ img.lastModified = in.readLong();
+ img.mimeType = in.readString();
+ int lobLen = in.readInt();
+ img.bits = new byte[lobLen];
+ in.read(img.bits, 0, lobLen);
+ return img;
+ }
+
+
+ @Override
+ public void objectToEntry(CachedImage img, TupleOutput out) {
+
+ out.writeInt(1); // version 1
+ out.writeLong(img.lastModified);
+ out.writeString(img.mimeType);
+ out.writeInt(img.bits.length);
+ out.write(img.bits);
+ }
+
+}
diff --git a/src/org/forkalsrud/album/web/AlbumServlet.java b/src/org/forkalsrud/album/web/AlbumServlet.java
index 83012cd..b029e42 100644
--- a/src/org/forkalsrud/album/web/AlbumServlet.java
+++ b/src/org/forkalsrud/album/web/AlbumServlet.java
@@ -4,6 +4,7 @@ import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
+import java.util.Properties;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
@@ -11,12 +12,8 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.CacheManager;
-import net.sf.ehcache.Element;
-
-import org.apache.commons.logging.LogFactory;
import org.apache.log4j.PropertyConfigurator;
+import org.forkalsrud.album.db.ThumbnailDatabase;
import org.forkalsrud.album.exif.DirectoryEntry;
import org.forkalsrud.album.exif.Entry;
import org.forkalsrud.album.exif.FileEntry;
@@ -27,38 +24,54 @@ public class AlbumServlet
{
File base;
String basePrefix;
- Cache imageCache;
- CacheManager cacheManager;
+// Cache imageCache;
+// CacheManager cacheManager;
PictureScaler pictureScaler;
long lastCacheFlushTime;
+ ThumbnailDatabase db = new ThumbnailDatabase();
@Override
public void init()
throws ServletException
{
+ log4jInit("/log4j.properties");
System.out.println("in init of Album");
base = new File(getServletConfig().getInitParameter("base")).getAbsoluteFile();
basePrefix = "/" + base.getName();
- PropertyConfigurator.configure("log4j.properties");
- LogFactory.getFactory().setAttribute("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.Log4JLogger");
- cacheManager = CacheManager.create();
- imageCache = cacheManager.getCache("imageCache");
+ String dbDirName = getServletConfig().getInitParameter("dbdir");
+ File dbDir = dbDirName != null ? new File(dbDirName) : new File(System.getProperty("java.io.tmpdir"), "album");
+ dbDir.mkdir();
+ db.init(dbDir);
+// cacheManager = CacheManager.create();
+// imageCache = cacheManager.getCache("imageCache");
pictureScaler = new PictureScaler();
lastCacheFlushTime = System.currentTimeMillis();
}
+ private void log4jInit(String resource) {
+ try {
+ Properties props = new Properties();
+ props.load(getClass().getResourceAsStream(resource));
+ PropertyConfigurator.configure(props);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
@Override
public void destroy() {
- imageCache.flush();
- cacheManager.shutdown();
+ System.out.println("Shutting down Album");
+ db.destroy();
+// imageCache.flush();
+ // cacheManager.shutdown();
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
- if (req.getParameter("cacheFlush") != null) imageCache.flush();
+// if (req.getParameter("cacheFlush") != null) imageCache.flush();
req.setAttribute("assets", req.getContextPath() + "/assets");
req.setAttribute("req", req);
@@ -166,28 +179,32 @@ public class AlbumServlet
String key = file.getPath() + ":" + size;
- CachedImage cimg = null;
- Element element = imageCache.get(key);
- if (element != null) {
- cimg = (CachedImage) element.getObjectValue();
+ CachedImage cimg = db.load(key);
+// Element element = imageCache.get(key);
+// if (element != null) {
+// cimg = (CachedImage) element.getObjectValue();
+ if (cimg != null) {
if (cimg.lastModified == file.lastModified()) {
System.out.println("cache hit on " + key);
} else {
System.out.println(" " + key + " has changed so cache entry wil be refreshed");
- imageCache.remove(key);
+ // imageCache.remove(key);
cimg = null;
}
}
if (cimg == null) {
// try {
cimg = pictureScaler.scalePicture(file, thumbnail, size);
- imageCache.put(new Element(key, cimg));
+ db.store(key, cimg);
+// imageCache.put(new Element(key, cimg));
+ /*
long millisSinceLastFlush = System.currentTimeMillis() - lastCacheFlushTime;
if (millisSinceLastFlush > 10 * 60 * 1000L) {
imageCache.flush();
lastCacheFlushTime = System.currentTimeMillis();
}
- System.out.println(" " + key + " added to the cache with size " + cimg.bits.length + " -- now " + imageCache.getSize() + " entries");
+ */
+ System.out.println(" " + key + " added to the cache with size " + cimg.bits.length + " -- now " + db.size() + " entries");
// } catch (TimeoutException e) {
// res.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
// return;
diff --git a/src/org/forkalsrud/album/web/PictureScaler.java b/src/org/forkalsrud/album/web/PictureScaler.java
index bf22fb5..0859888 100644
--- a/src/org/forkalsrud/album/web/PictureScaler.java
+++ b/src/org/forkalsrud/album/web/PictureScaler.java
@@ -171,6 +171,7 @@ public class PictureScaler {
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
BufferedImage img = reader.read(0, param);
+ iis.close();
// The orientation is about flipping and rotating. Here is what an 'F' looks like
// on pictures with each orientation.
diff --git a/webapp/WEB-INF/web.xml b/webapp/WEB-INF/web.xml
index e3677c2..01424c2 100644
--- a/webapp/WEB-INF/web.xml
+++ b/webapp/WEB-INF/web.xml
@@ -19,6 +19,10 @@
base
photos
+
+ dbdir
+ db
+