Significant update for WEB interface in particular.

Now we show the directory page with JPEG images in them.
This commit is contained in:
knut 2008-03-17 00:56:56 +00:00
parent f1bf243e76
commit fc8bc089b0
58 changed files with 1057 additions and 216 deletions

View file

@ -1,9 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" path="src"/> <classpathentry excluding="" kind="src" path="src">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> </classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/servletapi/jars/servletapi-2.4.jar"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> </classpathentry>
<classpathentry kind="lib" path="lib/metadata-extractor-2.3.1.jar" sourcepath="lib/metadata-extractor-2.3.1-src.jar"/> <classpathentry kind="var" path="MAVEN_REPO/org.springframework/jars/spring-2.0.6.jar">
<classpathentry kind="output" path="target"/> </classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/commons-logging/jars/commons-logging-1.0.4.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/commons-digester/jars/commons-digester-1.7.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/commons-collections/jars/commons-collections-3.1.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/commons-lang/jars/commons-lang-2.0.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/postgresql/jars/postgresql-8.1-408.jdbc3.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/velocity/jars/velocity-1.5.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/velocity-tools/jars/velocity-tools-view-1.2.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/com.drew/jars/metadata-extractor-2.3.1.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/servletapi/jars/servletapi-2.4.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/junit/jars/junit-4.1.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/resin-3.1/jars/resin-3.1.5.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/resin-3.1/jars/resin-util-3.1.5.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/resin-3.1/jars/jta-101-3.1.5.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/resin-3.1/jars/webbeans-16-3.1.5.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/resin-3.1/jars/ejb-15-3.1.5.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/resin-3.1/jars/jpa-15-3.1.5.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/resin-3.1/jars/jsdk-15-3.1.5.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/resin-3.1/jars/hessian-3.1.5.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/resin-3.1/jars/jca-15-3.1.5.jar">
</classpathentry>
<classpathentry kind="var" path="MAVEN_REPO/resin-3.1/jars/jstl-11-3.1.5.jar">
</classpathentry>
<classpathentry kind="output" path="target/classes">
</classpathentry>
</classpath> </classpath>

1
.cvsignore Normal file
View file

@ -0,0 +1 @@
build

View file

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<projectDescription> <projectDescription>
<name>album</name> <name>album</name>
<comment></comment> <comment>
</comment>
<projects> <projects>
</projects> </projects>
<buildSpec> <buildSpec>

View file

@ -1,40 +0,0 @@
#
# Java interpreter and compiler
#
JAVA = java
JAVAC = javac
#
# Various tools
#
RM = rm -f
#
# Which libraries do we use?
#
CLASSPATH=$(HOME)/src:$(HOME)/src/album/freeJSX1.0.7.4.jar
CLASSES = Photo.class Album.class Item.class Transform.class
#
# The file types we deal with
#
.SUFFIXES: .java .class
all: $(CLASSES)
@echo Done
clean:
$(RM) *.class
$(RM) *~
.java.class:
$(JAVAC) -classpath $(CLASSPATH) $<
test: $(CLASSES) Test.class
$(JAVA) -cp $(CLASSPATH) album.config.Test /home/knut/src/album/photos
#eof

25
album.launch Normal file
View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/album"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.debug.ui.INCLUDE_EXTERNAL_JARS" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.caucho.server.resin.Resin"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-conf conf/resin.conf"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="album"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Djava.util.logging.manager=com.caucho.log.LogManagerImpl&#13;-Djava.system.class.loader=com.caucho.loader.SystemClassLoader&#13;-Dresin.home=${workspace_loc:resin3}&#13;-Dresin.root=.&#13;-Xmx512m&#13;-Xss1m&#13;-XX:MaxPermSize=128m"/>
<booleanAttribute key="yk-capture-cpu-on-exit" value="false"/>
<booleanAttribute key="yk-capture-memory-on-exit" value="false"/>
<booleanAttribute key="yk-disable-alloc" value="false"/>
<booleanAttribute key="yk-disable-counts" value="false"/>
<booleanAttribute key="yk-launch-ui" value="true"/>
<intAttribute key="yk-mem-threshold" value="90"/>
<booleanAttribute key="yk-startup-with-object-allocations" value="false"/>
</launchConfiguration>

112
conf/resin.conf Normal file
View file

@ -0,0 +1,112 @@
<!--
- Resin 3.1 configuration file.
-->
<resin xmlns="http://caucho.com/ns/resin"
xmlns:resin="http://caucho.com/ns/resin/core">
<!--
- Logging configuration for the JDK logging API.
-->
<log name="" level="all" path="stdout:"
timestamp="[%H:%M:%S.%s] {%{thread}} "/>
<!--
- 'info' for production
- 'fine' or 'finer' for development and troubleshooting
-->
<logger name="com.caucho" level="info"/>
<logger name="com.caucho.java" level="config"/>
<logger name="com.caucho.loader" level="config"/>
<!--
- For production sites, change dependency-check-interval to something
- like 600s, so it only checks for updates every 10 minutes.
-->
<dependency-check-interval>2s</dependency-check-interval>
<!--
- You can change the compiler to "javac", "eclipse" or "internal".
-->
<javac compiler="javac" args="-source 1.5"/>
<!-- Security providers.
- <security-provider>
- com.sun.net.ssl.internal.ssl.Provider
- </security-provider>
-->
<cluster id="app-tier">
<!-- sets the content root for the cluster, relative to server.root -->
<root-directory>.</root-directory>
<server-default>
<!-- The http port -->
<http address="*" port="5080"/>
<!--
- Configures the minimum free memory allowed before Resin
- will force a restart.
-->
<memory-free-min>1M</memory-free-min>
<!-- Maximum number of threads. -->
<thread-max>256</thread-max>
<!-- Configures the socket timeout -->
<socket-timeout>65s</socket-timeout>
<!-- Configures the keepalive -->
<keepalive-max>128</keepalive-max>
<keepalive-timeout>120s</keepalive-timeout>
</server-default>
<!-- define the servers in the cluster -->
<server id="" address="127.0.0.1" port="5079">
<watchdog-port>5078</watchdog-port>
</server>
<!-- includes the web-app-default for default web-app behavior -->
<resin:import path="${resin.home}/conf/app-default.xml"/>
<!--
- Default host configuration applied to all virtual hosts.
-->
<host-default>
<!--
- With another web server, like Apache, this can be commented out
- because the web server will log this information.
-->
<access-log path="build/access.log"
format='%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"'
rollover-period="1W"/>
</host-default>
<!-- configures the default host, matching any host name -->
<host id=''>
<!-- configures the root web-app -->
<web-app id='/album' document-directory='webapp'>
<temp-dir>target/resin-tmp</temp-dir>
<work-dir>target/resin-work</work-dir>
<session-config>
<!-- 30 min timeout -->
<session-timeout>30</session-timeout>
</session-config>
<class-loader>
<compiling-loader path="target"/>
</class-loader>
<path-mapping url-pattern="/demo/*" real-path="/Users/knut/src/album/photos" />
</web-app>
</host>
</cluster>
</resin>

View file

@ -40,7 +40,7 @@ themselves. URLs for the albums are:
/album/index-num.html /album/index-num.html
There is a configuretion switch indicating how many thumbnails to There is a configuration switch indicating how many thumbnails to
show per album page. This makes "num" in index-num.html represent the show per album page. This makes "num" in index-num.html represent the
associated subset of images. There are also one picture per page associated subset of images. There are also one picture per page
pages. These have the pattern: pages. These have the pattern:

Binary file not shown.

Binary file not shown.

1
photos/.cvsignore Normal file
View file

@ -0,0 +1 @@
cache.properties

2
photos/album.properties Normal file
View file

@ -0,0 +1,2 @@
file.l1000729.jpg.caption=Test caption!
file.l1000729.jpg.dimensions=1200x1600

View file

@ -0,0 +1 @@
cache.properties

15
project.properties Normal file
View file

@ -0,0 +1,15 @@
# point to CJ's maven repository
maven.repo.remote=http://www.forkalsrud.org/maven-proxy/repository
# warn about use of deprecated classes/methods
maven.compile.deprecation=true
# WAR plugin
#maven.war.final.name=ROOT.war
maven.war.src=webapp
# PMD plugin
maven.pmd.cpd.enable=true
#maven.jblanket.enable=true

251
project.xml Normal file
View file

@ -0,0 +1,251 @@
<?xml version="1.0" encoding="UTF-8"?>
<project>
<pomVersion>1</pomVersion>
<artifactId>album</artifactId>
<name>album</name>
<groupId>forkalsrud</groupId>
<currentVersion>2</currentVersion>
<organization>
<name>forkalsrud.org</name>
<url>http://www.forkalsrud.org/</url>
</organization>
<inceptionYear>2008</inceptionYear>
<package>org.forkalsrud.album</package>
<shortDescription>Photo Album</shortDescription>
<siteAddress>http://www.forkalsrud.org/album/</siteAddress>
<repository>
<connection>scm:pserver:forkalsrud.org:/home/cvsroot</connection>
</repository>
<developers>
<developer>
<name>Knut Forkalsrud</name>
<id>1</id>
<email>knut@forkalsrud.org</email>
<url>http://www.forkalsrud.org/~knut/</url>
<timezone>PST</timezone>
</developer>
<developer>
<name>Erik Forkalsrud</name>
<id>2</id>
<email>erik@forkalsrud.org</email>
<url>http://www.forkalsrud.org/~erik/</url>
<timezone>PST</timezone>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.0.6</version>
<type>jar</type>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.0.4</version>
<type>jar</type>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>commons-digester</groupId>
<artifactId>commons-digester</artifactId>
<version>1.7</version>
<type>jar</type>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
<type>jar</type>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.0</version>
<type>jar</type>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.1-408.jdbc3</version>
<type>jar</type>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.5</version>
<type>jar</type>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>velocity-tools</groupId>
<artifactId>velocity-tools-view</artifactId>
<version>1.2</version>
<type>jar</type>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>com.drew</groupId>
<artifactId>metadata-extractor</artifactId>
<version>2.3.1</version>
<type>jar</type>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>com.drew</groupId>
<artifactId>metadata-extractor-source</artifactId>
<type>source</type>
<jar>metadata-extractor-2.3.1-src.jar</jar>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>servletapi</groupId>
<artifactId>servletapi</artifactId>
<version>2.4</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.1</version>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>resin</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>resin-util</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>jta-101</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>webbeans-16</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>ejb-15</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>jpa-15</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>jsdk-15</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>hessian</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>jca-15</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>jstl-11</artifactId>
<version>3.1.5</version>
<type>jar</type>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>resin-3.1</groupId>
<artifactId>resin-source</artifactId>
<type>source</type>
<jar>resin-3.1.5-src.zip</jar>
<properties>
<war.bundle>false</war.bundle>
</properties>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
</build>
<reports>
<report>maven-junit-report-plugin</report>
<report>maven-jcoverage-plugin</report>
<report>maven-pmd-plugin</report>
<report>maven-javadoc-plugin</report>
<report>maven-jdepend-plugin</report>
</reports>
</project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,125 +0,0 @@
package album.web;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class AlbumServlet
extends HttpServlet
{
File baseDir = new File("/home/knut");
@Override
public void init()
throws ServletException
{
System.out.println("in init of Album");
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
/* System.out.println("pathInfo: " + req.getPathInfo());
System.out.println("pathXlat: " + req.getPathTranslated());
System.out.println("queryString: " + req.getQueryString());
System.out.println("requestUri: " + req.getRequestURI());
System.out.println("servletPath: " + req.getServletPath());
*/
res.setContentType("text/html");
// Determine which directory to list
String directory = req.getServletPath();
directory = directory.substring(
"/".length(),
directory.length() - ".album".length());
System.out.println("directory: " + directory);
File dir = new File(baseDir, directory);
if (dir == null) {
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
if (!dir.isDirectory()) {
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
if (!dir.canRead()) {
res.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
// Determine sort order
File[] files = dir.listFiles(new FileFilter() {
public boolean accept(File candidate) {
return candidate.isDirectory()
|| candidate.getName().endsWith(".gif")
|| candidate.getName().endsWith(".GIF")
|| candidate.getName().endsWith(".png")
|| candidate.getName().endsWith(".PNG")
|| candidate.getName().endsWith(".jpg")
|| candidate.getName().endsWith(".JPG")
|| candidate.getName().endsWith(".jpeg")
|| candidate.getName().endsWith(".JPEG");
}
});
Arrays.sort(files, new Comparator<File>() {
public int compare(File f1, File f2) {
// Directories first
if (f1.isDirectory() && !f2.isDirectory())
return -1;
if (f2.isDirectory() && !f1.isDirectory())
return 1;
return f1.getName().compareTo(f2.getName());
}
});
// Produce <img> tags for a thumbnail of each image or other
// album
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>" + directory + "</title>");
out.println("</head>");
out.println("<body>");
String baseUrl = req.getContextPath()
+ "/" + directory + "/";
for (int i = 0; i < files.length; ++i) {
File file = files[i];
String anchorUrl = "";
String iconUrl = "";
if (file.isDirectory()) {
anchorUrl = baseUrl + file.getName() + ".album";
iconUrl = req.getContextPath() + "/album.png";
} else if (file.isFile()) {
anchorUrl = baseUrl + file.getName() + ".photo";
iconUrl = baseUrl + file.getName() + ".bitmap?size=100";
} else {
throw new ServletException("Unknown file: " + file);
}
out.print("<a href=\"" + anchorUrl + "\">");
out.print("<img hspace=5 vspace=5 src=\"" + iconUrl + "\">");
out.println("</a>");
}
out.println("</body>");
out.println("</html>");
}
@Override
public String getServletInfo() {
return "Display a directory as an album";
}
}
// eof

View file

@ -1,4 +1,4 @@
package album.config; package org.forkalsrud.album.config;
import java.io.*; import java.io.*;

View file

@ -1,4 +1,4 @@
package album.config; package org.forkalsrud.album.config;
public abstract class Entry { public abstract class Entry {
String name; String name;

View file

@ -1,4 +1,4 @@
package album.config; package org.forkalsrud.album.config;
public class Item public class Item

View file

@ -1,4 +1,4 @@
package album.config; package org.forkalsrud.album.config;
public class Photo public class Photo

View file

@ -1,4 +1,4 @@
package album.config; package org.forkalsrud.album.config;
public class Reference public class Reference
extends Entry extends Entry

View file

@ -1,4 +1,4 @@
package album.config; package org.forkalsrud.album.config;
import java.io.*; import java.io.*;

View file

@ -1,4 +1,4 @@
package album.config; package org.forkalsrud.album.config;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Rectangle; import java.awt.Rectangle;

View file

@ -1,4 +1,4 @@
package album.editor; package org.forkalsrud.album.editor;
import java.awt.*; import java.awt.*;
import java.awt.event.*; import java.awt.event.*;

View file

@ -0,0 +1,74 @@
/*
* Copyright 2008 Estalea, Inc. All rights reserved.
* This software is the proprietary information of Estalea, Inc.
*/
package org.forkalsrud.album.exif;
/**
* TODO (knut - Mar 16, 2008 2:23:56 PM) - add documentation
*
* @author knut
*/
public class Dimension {
int w;
int h;
public Dimension(int w, int h) {
this.w = w;
this.h = h;
}
public Dimension(String s) {
String[] coords = s.split("x");
this.w = Integer.parseInt(coords[0]);
this.h = Integer.parseInt(coords[1]);
}
public Dimension scaled(int max) {
int sw, sh;
if (w > h) {
if (w > max) {
sw = max;
sh = (max * h + max / 2) / w;
} else {
sw = w;
sh = h;
}
} else {
if (h > max) {
sw = (max * w + max / 2) / h;
sh = max;
} else {
sw = w;
sh = h;
}
}
return new Dimension(sw, sh);
}
public Dimension flip() {
return new Dimension(h, w);
}
@Override
public String toString() {
return w + "x" + h;
}
public int getWidth() {
return w;
}
public int getHeight() {
return h;
}
}

View file

@ -1,4 +1,4 @@
package album.exif; package org.forkalsrud.album.exif;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -55,7 +55,7 @@ public class DirectoryTest {
exifDirectory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT)) { exifDirectory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT)) {
int width = exifDirectory.getInt(ExifDirectory.TAG_EXIF_IMAGE_WIDTH); int width = exifDirectory.getInt(ExifDirectory.TAG_EXIF_IMAGE_WIDTH);
int height = exifDirectory.getInt(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT); int height = exifDirectory.getInt(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT);
imgProps.setProperty(base + "dimensions", nf.format(width) + "x" + nf.format(height)); imgProps.setProperty(base + "dimensions", new Dimension(width, height).toString());
} }
if (exifDirectory.containsTag(ExifDirectory.TAG_DATETIME_ORIGINAL)) { if (exifDirectory.containsTag(ExifDirectory.TAG_DATETIME_ORIGINAL)) {
Date captureDate = exifDirectory.getDate(ExifDirectory.TAG_DATETIME_ORIGINAL); Date captureDate = exifDirectory.getDate(ExifDirectory.TAG_DATETIME_ORIGINAL);

View file

@ -0,0 +1,111 @@
/*
* Copyright 2008 Estalea, Inc. All rights reserved.
* This software is the proprietary information of Estalea, Inc.
*/
package org.forkalsrud.album.exif;
import java.util.Date;
/**
* Represents one item inside the album, typically file or directory, aka photo or album
*
* @author knut
*/
public class Entry {
String name;
Dimension size;
String caption;
Date date;
int orientation;
/**
* @return Returns the name.
*/
public String getName() {
return name;
}
/**
* @param name The name to set.
*/
public void setName(String name) {
this.name = name;
}
/**
* @return Returns the size.
*/
public Dimension getSize() {
return size;
}
/**
* @param size The size to set.
*/
public void setSize(Dimension size) {
this.size = size;
}
/**
* @return Returns the caption.
*/
public String getCaption() {
return caption;
}
/**
* @param caption The caption to set.
*/
public void setCaption(String caption) {
this.caption = caption;
}
/**
* @return Returns the date.
*/
public Date getDate() {
return date;
}
/**
* @param date The date to set.
*/
public void setDate(Date date) {
this.date = date;
}
/**
* @return Returns the orientation.
*/
public int getOrientation() {
return orientation;
}
/**
* @param orientation The orientation to set.
*/
public void setOrientation(int orientation) {
this.orientation = orientation;
}
}

View file

@ -0,0 +1,154 @@
/*
* Copyright 2008 Estalea, Inc. All rights reserved.
* This software is the proprietary information of Estalea, Inc.
*/
package org.forkalsrud.album.exif;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.imaging.jpeg.JpegProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataException;
import com.drew.metadata.exif.ExifDirectory;
/**
* Responsible for translating entries to and from file
*
* @author knut
*/
public class EntryDao {
final static String CACHE_FILE = "cache.properties";
final static String OVERRIDE_FILE = "album.properties";
public List<Entry> read(File directory) throws FileNotFoundException, IOException, JpegProcessingException, MetadataException, ParseException {
List<Entry> entries = new ArrayList<Entry>();
Properties cachedProps = new Properties();
File cache = new File(directory, CACHE_FILE);
if (cache.exists() && cache.isFile() && cache.canRead()) {
cachedProps.load(new FileInputStream(cache));
} else {
generateEntries(directory, cachedProps);
}
Properties overrideProps = new Properties(cachedProps);
File override = new File(directory, OVERRIDE_FILE);
if (override.exists() && override.isFile() && override.canRead()) {
overrideProps.load(new FileInputStream(override));
}
Properties combined = new Properties();
combined.putAll(cachedProps);
combined.putAll(overrideProps);
populate(combined, entries);
Collections.sort(entries, new Comparator<Entry>() {
public int compare(Entry e1, Entry e2) {
Date d1 = e1.getDate();
Date d2 = e2.getDate();
if (d1 != null && d2 != null) {
return d1.compareTo(d2);
} else if (d1 != null) {
return -1;
} else if (d2 != null) {
return +1;
} else {
return 0;
}
}
});
return entries;
}
private void populate(Properties cachedProps, List<Entry> entries) throws ParseException {
HashMap<String, Entry> entryMap = new HashMap<String, Entry>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss");
Iterator i = cachedProps.keySet().iterator();
while (i.hasNext()) {
String key = (String)i.next();
if (key.startsWith("file.") && key.endsWith(".dimensions")) {
String name = key.substring("file.".length(), key.length() - ".dimensions".length());
if (!entryMap.containsKey(name)) {
Entry entry = new Entry();
entry.setName(name);
entry.setDate(sdf.parse(cachedProps.getProperty("file." + name + ".captureDate")));
entry.setSize(new Dimension(cachedProps.getProperty("file." + name + ".dimensions")));
entry.setCaption(cachedProps.getProperty("file." + name + ".caption"));
entry.setOrientation(Integer.parseInt(cachedProps.getProperty("file." + name + ".orientation")));
entries.add(entry);
}
}
}
}
void generateEntries(File directory, Properties cachedProps) throws JpegProcessingException, MetadataException, FileNotFoundException, IOException {
File[] files = directory.listFiles(new FileFilter() {
public boolean accept(File file) {
return !file.isHidden() && !file.isDirectory() && !CACHE_FILE.equals(file.getName()) && !OVERRIDE_FILE.equals(file.getName());
}
});
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss");
NumberFormat nf = new DecimalFormat("0");
for (File f : files) {
String name = f.getName();
String base = "file." + name + ".";
Metadata metadata = JpegMetadataReader.readMetadata(f);
Directory exifDirectory = metadata.getDirectory(ExifDirectory.class);
if (exifDirectory.containsTag(ExifDirectory.TAG_ORIENTATION)) {
int orientation = exifDirectory.getInt(ExifDirectory.TAG_ORIENTATION);
cachedProps.setProperty(base + "orientation", nf.format(orientation));
}
if (exifDirectory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_WIDTH) &&
exifDirectory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT)) {
int width = exifDirectory.getInt(ExifDirectory.TAG_EXIF_IMAGE_WIDTH);
int height = exifDirectory.getInt(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT);
cachedProps.setProperty(base + "dimensions", new Dimension(width, height).toString());
}
if (exifDirectory.containsTag(ExifDirectory.TAG_DATETIME_ORIGINAL)) {
Date captureDate = exifDirectory.getDate(ExifDirectory.TAG_DATETIME_ORIGINAL);
cachedProps.setProperty(base + "captureDate", sdf.format(captureDate));
}
if (exifDirectory.containsTag(ExifDirectory.TAG_USER_COMMENT)) {
String comment = exifDirectory.getString(ExifDirectory.TAG_USER_COMMENT);
cachedProps.setProperty(base + "comment", comment);
}
}
File dst = new File(directory, CACHE_FILE);
if (directory.canWrite()) {
cachedProps.store(new FileOutputStream(dst), "Extra Comments");
}
}
}

View file

@ -1,4 +1,4 @@
package album.exif; package org.forkalsrud.album.exif;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;

View file

@ -3,19 +3,23 @@ package org.forkalsrud.album.web;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.*; import java.io.File;
import java.io.IOException;
import java.text.ParseException; import java.text.ParseException;
import java.util.*; import java.util.Iterator;
import java.util.List;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam; import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader; import javax.imageio.ImageReader;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter; import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.ImageOutputStream;
import javax.servlet.*; import javax.servlet.RequestDispatcher;
import javax.servlet.http.*; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.forkalsrud.album.exif.Dimension; import org.forkalsrud.album.exif.Dimension;
import org.forkalsrud.album.exif.Entry; import org.forkalsrud.album.exif.Entry;
@ -41,15 +45,16 @@ public class AlbumServlet
public void doGet(HttpServletRequest req, HttpServletResponse res) public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException throws ServletException, IOException
{ {
/*
System.out.println("pathInfo: " + req.getPathInfo()); System.out.println("pathInfo: " + req.getPathInfo());
System.out.println("pathXlat: " + req.getPathTranslated()); System.out.println("pathXlat: " + req.getPathTranslated());
System.out.println("queryString: " + req.getQueryString()); System.out.println("queryString: " + req.getQueryString());
System.out.println("requestUri: " + req.getRequestURI()); System.out.println("requestUri: " + req.getRequestURI());
System.out.println("servletPath: " + req.getServletPath()); System.out.println("servletPath: " + req.getServletPath());
*/
String path = req.getPathTranslated(); String path = req.getPathTranslated();
System.out.println("file: " + path); // System.out.println("file: " + path);
File file = new File(path); File file = new File(path);
if (file == null) { if (file == null) {

View file

@ -1,4 +1,4 @@
package album.web; package org.forkalsrud.album.web;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
@ -16,7 +16,7 @@ public class BitmapServlet
implements ImageObserver implements ImageObserver
{ {
File baseDir = new File("photos"); File baseDir = new File("photos");
File cacheDir = new File("/var/album"); File cacheDir = new File("/var/org.forkalsrud.album");
final static int BUF_SIZE = 1024; final static int BUF_SIZE = 1024;
protected volatile boolean complete = false; protected volatile boolean complete = false;

View file

@ -1,4 +1,4 @@
package album.web; package org.forkalsrud.album.web;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
@ -16,7 +16,7 @@ public class PhotoServlet
implements ImageObserver implements ImageObserver
{ {
File baseDir = new File("/home/knut"); File baseDir = new File("/home/knut");
File cacheDir = new File("/var/album"); File cacheDir = new File("/var/org.forkalsrud.album");
final static int BUF_SIZE = 1024; final static int BUF_SIZE = 1024;
protected volatile boolean complete = false; protected volatile boolean complete = false;

1
webapp/.cvsignore Normal file
View file

@ -0,0 +1 @@
target

View file

@ -0,0 +1,48 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width = 600" />
<title>$directory</title>
<style type="text/css">
body {
font-size: 11px;
font-family: "Lucida Grande", Arial, sans-serif;
color: #6d6d6d;
background: #acc95f;
width: 600px;
margin: 10px auto;
text-align: center;
}
a:link, a:visited {
text-decoration: none;
color: #4c4c4c;
font-weight: 700;
}
a:hover, a:focus {
text-decoration: underline;
color: #6a6a6a;
}
</style>
</head>
<body>
<h1>$directory</h1>
<hr/>
#set($thmb = 150)
#foreach($entry in $entries)
#if($entry.orientation == 6)
#set($dim = $entry.size.scaled($thmb).flip())
#else
#set($dim = $entry.size.scaled($thmb))
#end
<div class="home_entry_table">
<span class="name">$entry.name</span><br/>
<a href="$entry.name"><img src="$entry.name?size=$thmb" border="0" width="$dim.width" height="$dim.height"/></a><br/>
<span class="caption">$!entry.caption</span>
</div>
#end
</body>
</html>

View file

@ -1,10 +1,38 @@
<web-app> <web-app>
<servlet-mapping url-pattern="*.album" <servlet>
servlet-name="album.AlbumServlet" /> <servlet-name>album</servlet-name>
<servlet-mapping url-pattern="*.photo" <servlet-class>org.forkalsrud.album.web.AlbumServlet</servlet-class>
servlet-name="album.PhotoServlet" /> </servlet>
<servlet-mapping url-pattern="*.bitmap"
servlet-name="album.BitmapServlet" /> <servlet>
<servlet-name>photo</servlet-name>
<servlet-class>org.forkalsrud.album.web.PhotoServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>bitmap</servlet-name>
<servlet-class>org.forkalsrud.album.web.BitmapServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>velocity</servlet-name>
<servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resin-file</servlet-name>
<url-pattern>/js/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>velocity</servlet-name>
<url-pattern>/WEB-INF/velocity/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>album</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app> </web-app>

View file

@ -0,0 +1,132 @@
/*
$Id: pxapplication.css,v 1.1 2008/03/17 01:01:36 knut Exp $
Pixory - the personal image server
Copyright 2004-2005 Joe Panico
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2, as
published by the Free Software Foundation.
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* predefined elements */
html { height: 100%; text-align:center; font-family: verdana, arial, helvetica, sans-serif; }
body { height: 100%; margin: 0px; padding: 1px; }
hr { width: 100%; height: 1px; border: none; background-color:#7070a1; }
/* pseudo classes */
a:link {color:#606091;}
a:visited {color:#606091;}
/* force vertical scrollbar in Moz */
#force-scrollbar { position: absolute; top: 0; bottom: -1px; width: 1em; z-index: -1; }
/* generic classes */
/* i.e. css side orderings: top, right, bottom, left */
.thintable { width: 100%; border: none; border-collapse: collapse; padding: 0px; }
.error_message { font-size: medium; font-style: italic; }
/* a piece of text styled to look like a button */
.text_button { border: 1px solid black; color: black; background-color: rgb(80%,80%,80%); text-align: center; padding: 3px 5px 3px 5px; text-decoration: none;}
/* currently used only on ViewerHome */
.dropShadowContent { float: left; position: relative; left: -2px; top: -2px; border: none; background: #9fc;}
.dropShadowContainer { float: left; position: relative; background: #666; margin: 4px; border: none;}
/* PXShowMessage */
.show_message_table { border: none; height: 35px;}
.show_message_table .success_message_cell { color: black;}
.show_message_table .running_message_cell { color: black;}
.show_message_table .error_message_cell { color: red;}
.show_message_table .warn_message_cell { color: red;}
/* PXPageDecorator */
#page_decorator_table { height: 100%; width: 100%; border: none; color:#606091; background-color: #fffffb; vertical-align: top; text-align: left; margin: 0px; padding: 0px;}
#page_decorator_cell { height: 90%; border:none; vertical-align: top;}
#footer_cell { border: none; text-align: right; vertical-align: bottom; font-size: small; font-style: italic; color: #666; padding: 0px 4px 4px 0px; }
/* Message page */
#message_table {width: 30%; height: 30%; vertical-align: middle; text-align: center; margin-left: auto; margin-right:auto; border: 1px solid orange; background-color: #ddd;}
#message_table td { padding: 10px; }
/* all pages */
#content_table { width: 100%; border: none; text-align: center; border-collapse: collapse; }
/* pxtapestry:PXPropertiesTable */
table.properties_table { border: 1px dotted #ccc; text-align: left; font-size: xx-small; margin: 0px 7px 0px 7px; }
table.properties_table caption { font-size: small; font-weight: bold; text-decoration: underline; text-align: center; }
table.properties_table tr.even { background-color: #ccb; }
table.properties_table td { padding: 0px 5px 0px 5px; }
/* all/any viewer pages */
.image { border: 1px solid #606091; padding: 0px;}
.caption { border: none; font-size: xx-small; padding: 0px; vertical-align: top;}
.filename { border: none; font-size: xx-small; font-style: italic; padding: 0px; vertical-align: top;}
.photo_grid_table { width: 100%; height: 100%; vertical-align: top; padding: 0px 0px 0px 0px; border: none; border-spacing: 7px; empty-cells: hide; background-color: #ddd; }
.photo_grid_cell { vertical-align: top; background-color: #fffffb; border: 1px dotted #ccc; padding: 0px 0px 0px 0px; }
/* ViewerHome */
#viewer_home_title_cell {border: none; text-align: left; font-size: x-large; padding: 10px 20px 10px 10px; }
#viewer_home_greeting_cell {border: none; text-align: right; font-size: xx-small; padding: 5px 20px 5px 0px; }
#viewer_home_noalbums_cell {border: none; text-align: center; padding: 60px 0px 30px 0px; }
td.home_year { text-align: left; font-size: large; font-weight: bold; padding: 5px 5px 5px 5px; }
table.home_entry_table { width: 100%; vertical-align: top; text-align: center; font-size: xx-small; color:#606091; }
table.home_entry_table td.caption { padding: 0px 5px 5px 5px; }
/* album/index pages */
#album_header_table { border: none; width: 100%; padding: 0px; vertical-align: top; border-collapse: collapse; }
#album_name_cell { width: 50%; text-align: left; font-weight: bold; }
#album_control_cell { width: 50%; text-align: left; font-weight: bold; }
#album_control_cell .button { width: 15% }
#album_control_cell .control { width: 35%; text-align: right; }
#album_control_cell .label { width: 35%; text-align: center; font-size: small; color: #888; }
/* album pages */
.album_image_table { border: none; border-collapse: collapse; margin-left: auto; margin-right:auto; }
.album_image_table td { padding: 0px; }
.album_image_table .image_properties { height: 16px; font-size: xx-small; color: #888; }
.album_narrative_cell { padding: 0px 10px 20px 10px; white-space: normal; border: none; }
/* index page */
table.index_entry_table { border: none; height: 100%; width: 100%; vertical-align: middle; text-align: center; font-size: xx-small; color:#606091; }
/* FullPhoto page */
table.full_photo { border: none; width: 100%; height: 100%; vertical-align: middle; text-align: center; border-collapse: collapse; padding: 0px; margin-left: auto; margin-right:auto;}
/* AboutPanel */
#about_table { border: 1px dotted #ccc; text-align: left; font-size: small; margin: 0px 7px 0px 0px; }
#about_table .heading { text-align: center; padding: 7px; }
#about_table .properties_table { text-align: left; border-top: 1px solid #ccc;}
#about_table .author { font-weight: bold; }
#about_table .label { width: 20%; text-align: right; }
#about_table .odd { background-color: #ccb; }
#about_table .centered { text-align: center; }
/* AlbumDownload */
#download_table { border: 1px dotted #ccc; text-align: center; font-size: small; margin: 0px 7px 0px 7px; }
/* Login, SendCredentials */
.icon_table { width: 100%; border: none; text-align: right; }
.centering_table { width: 100%; height: 100%; vertical-align: middle; text-align: center; border: none; }
#login_table { border: 2px solid orange; background-color: #ddd; margin-left: auto; margin-right:auto; text-align: left; empty-cells: show;}
#login_table .error { height: 35px; }
#login_table .label { text-align: right; }
#login_table .submit { text-align: right; }
.submit_button { padding: 0px ; border: 1px solid black; background-color: orange; }
.form_field { padding: 0px 0px 0px 5px; border: 1px solid #888; }