diff options
-rw-r--r-- | org/madore/damlengine/Context.java | 6 | ||||
-rw-r--r-- | org/madore/damlengine/DamlEngine.java | 30 | ||||
-rw-r--r-- | org/madore/damlengine/TodoWXrefAttr.java | 2 | ||||
-rw-r--r-- | org/madore/damlengine/TodoWeblogIndexSelectElement.java | 2 | ||||
-rw-r--r-- | org/madore/damlengine/TodoWeblogSelectionElement.java | 2 | ||||
-rw-r--r-- | org/madore/damlengine/TodoWrefAttr.java | 2 | ||||
-rw-r--r-- | org/madore/damlengine/WeblogDatabaseConnection.java | 84 | ||||
-rw-r--r-- | org/madore/damlengine/WeblogSelect.java | 9 | ||||
-rw-r--r-- | org/madore/damlengine/WeblogServlet.java | 163 | ||||
-rw-r--r-- | org/madore/damlengine/WeblogSummary.java | 7 | ||||
-rw-r--r-- | org/madore/damlengine/cmdlines | 2 |
11 files changed, 261 insertions, 48 deletions
diff --git a/org/madore/damlengine/Context.java b/org/madore/damlengine/Context.java index 9249fe5..c1f0dce 100644 --- a/org/madore/damlengine/Context.java +++ b/org/madore/damlengine/Context.java @@ -68,6 +68,12 @@ public class Context implements Cloneable { public WeblogSelectionContext wsc; + public static class DynamicContext { + public long modTime; + } + + public DynamicContext dc; + public static class EntryContext { public String year; public String month; diff --git a/org/madore/damlengine/DamlEngine.java b/org/madore/damlengine/DamlEngine.java index a0299ad..4826cf1 100644 --- a/org/madore/damlengine/DamlEngine.java +++ b/org/madore/damlengine/DamlEngine.java @@ -81,8 +81,10 @@ public final class DamlEngine { } public static final class IncantDOM { - static DOMImplementation domi; + static ThreadLocal<DOMImplementation> pdomi + = new ThreadLocal<DOMImplementation>(); public static DOMImplementation getDOMI() { + DOMImplementation domi = pdomi.get(); if ( domi == null ) { DOMImplementationSource source = new DOMImplementationSourceImpl(); @@ -90,6 +92,7 @@ public final class DamlEngine { if ( domi == null ) throw new MissingResourceException("failed to obtain DOM implementation", "org.w3c.dom.ls.DOMImplementationLS", ""); + pdomi.set(domi); } return domi; } @@ -112,20 +115,28 @@ public final class DamlEngine { } public static void processDocument(Document doc, - Context.WeblogSelectionContext wsc) { + Context.WeblogSelectionContext wsc, + Context.DynamicContext dc) { TodoDeque todoDeque = new TodoDeque(); Context ctx = new Context(doc); ctx.wsc = wsc; + ctx.dc = dc; todoDeque.registerAtEnd(new RootTodo(ctx)); todoDeque.dispatchLoop(); } + public static void processDocument(Document doc, + Context.WeblogSelectionContext wsc) { + processDocument(doc, wsc, null); + } + public static void processDocument(Document doc) { - processDocument(doc, null); + processDocument(doc, null, null); } public static void fullProcess(InputStream in, OutputStream out, - Context.WeblogSelectionContext wsc) + Context.WeblogSelectionContext wsc, + Context.DynamicContext dc) throws Exception { final DOMImplementationLS domils @@ -139,7 +150,7 @@ public final class DamlEngine { LSInput input = domils.createLSInput(); input.setByteStream(in); Document doc = par.parse(input); - processDocument(doc, wsc); + processDocument(doc, wsc, dc); doc.normalizeDocument(); Unparser unparser = new Unparser(doc, new OutputStreamWriter(out, "UTF-8"), @@ -148,11 +159,18 @@ public final class DamlEngine { } + public static void fullProcess(InputStream in, OutputStream out, + Context.WeblogSelectionContext wsc) + throws Exception { + fullProcess(in, out, wsc, null); + } + public static void fullProcess(InputStream in, OutputStream out) throws Exception { - fullProcess(in, out, null); + fullProcess(in, out, null, null); } + public static boolean runAsServlet = false; public static Properties appProps; public static Path basePath; public static Path templatePath; diff --git a/org/madore/damlengine/TodoWXrefAttr.java b/org/madore/damlengine/TodoWXrefAttr.java index 130ba53..bb1c861 100644 --- a/org/madore/damlengine/TodoWXrefAttr.java +++ b/org/madore/damlengine/TodoWXrefAttr.java @@ -26,7 +26,7 @@ public class TodoWXrefAttr extends TodoAttr { if ( ctx.wsc == null ) throw new IllegalStateException("wxref attribute encountered with no weblog selection state"); int prev = ctx.wsc.sel.first() - 1; - WeblogSummary sum = WeblogSummary.getSummary(); + WeblogSummary sum = WeblogSummary.getSummary(ctx.dc); if ( sum != null && sum.entries != null && sum.entries.containsKey(new Integer(prev)) ) { WeblogSummary.EntrySummary ent diff --git a/org/madore/damlengine/TodoWeblogIndexSelectElement.java b/org/madore/damlengine/TodoWeblogIndexSelectElement.java index 004e330..21ed367 100644 --- a/org/madore/damlengine/TodoWeblogIndexSelectElement.java +++ b/org/madore/damlengine/TodoWeblogIndexSelectElement.java @@ -29,7 +29,7 @@ public final class TodoWeblogIndexSelectElement extends TodoDefaultElement { @Override public void handleNodeOnly() { - WeblogSummary wsum = WeblogSummary.getSummary(); + WeblogSummary wsum = WeblogSummary.getSummary(ctx.dc); if ( wsum == null || wsum.entries == null ) { throw new IllegalStateException("weblog-index-select element encountered with no weblog summary available"); } diff --git a/org/madore/damlengine/TodoWeblogSelectionElement.java b/org/madore/damlengine/TodoWeblogSelectionElement.java index 637bc85..9d557d4 100644 --- a/org/madore/damlengine/TodoWeblogSelectionElement.java +++ b/org/madore/damlengine/TodoWeblogSelectionElement.java @@ -159,7 +159,7 @@ public final class TodoWeblogSelectionElement extends TodoDefaultElement { case SINGLE_TITLE: if ( ! ( ctx.wsc instanceof Context.WeblogSingleSelectionContext ) ) throw new IllegalStateException("weblog-selection-single-title element encountered while not in weblog single entry selection state"); - WeblogSummary wsum = WeblogSummary.getSummary(); + WeblogSummary wsum = WeblogSummary.getSummary(ctx.dc); if ( wsum == null || wsum.entries == null ) throw new IllegalStateException("failed to obtain weblog summary for weblog-selection-single-title"); WeblogSummary.EntrySummary esum diff --git a/org/madore/damlengine/TodoWrefAttr.java b/org/madore/damlengine/TodoWrefAttr.java index aeb8bb2..b707a95 100644 --- a/org/madore/damlengine/TodoWrefAttr.java +++ b/org/madore/damlengine/TodoWrefAttr.java @@ -42,7 +42,7 @@ public class TodoWrefAttr extends TodoAttr { } String targetFile; String targetStdDir = (ctx.gc.uriToTop==null)?"":(ctx.gc.uriToTop+"weblog/"); - WeblogSummary wsum = WeblogSummary.getSummary(); + WeblogSummary wsum = WeblogSummary.getSummary(ctx.dc); WeblogSummary.EntrySummary esum; // Get entry for target link from weblog summary if ( wsum != null && wsum.entries != null ) diff --git a/org/madore/damlengine/WeblogDatabaseConnection.java b/org/madore/damlengine/WeblogDatabaseConnection.java index 900f3a0..16d0973 100644 --- a/org/madore/damlengine/WeblogDatabaseConnection.java +++ b/org/madore/damlengine/WeblogDatabaseConnection.java @@ -15,73 +15,88 @@ public final class WeblogDatabaseConnection { throw new AssertionError("WeblogDatabaseConnection cannot be instantiated"); } - public static Connection conn; + public static ThreadLocal<Connection> pconn + = new ThreadLocal<Connection>(); public static Connection getConnection() throws SQLException { + Connection conn = pconn.get(); if ( conn == null ) { - String dbHost = System.getenv("DAMLENGINE_PGHOST"); + String dbHost = null; + if ( ! DamlEngine.runAsServlet ) + dbHost = System.getenv("DAMLENGINE_PGHOST"); if ( dbHost == null ) dbHost = DamlEngine.appProps.getProperty("pghost"); - if ( dbHost == null ) + if ( dbHost == null && ! DamlEngine.runAsServlet ) dbHost = System.getenv("PGHOST"); if ( dbHost == null ) dbHost = "localhost"; - String dbPort = System.getenv("DAMLENGINE_PGPORT"); + String dbPort = null; + if ( ! DamlEngine.runAsServlet ) + dbPort = System.getenv("DAMLENGINE_PGPORT"); if ( dbPort == null ) dbPort = DamlEngine.appProps.getProperty("pgport"); - if ( dbPort == null ) + if ( dbPort == null && ! DamlEngine.runAsServlet ) dbPort = System.getenv("PGPORT"); if ( dbPort == null ) dbPort = "5432"; - String dbName = System.getenv("DAMLENGINE_DBNAME"); + String dbName = null; + if ( ! DamlEngine.runAsServlet ) + dbName = System.getenv("DAMLENGINE_DBNAME"); if ( dbName == null ) dbName = DamlEngine.appProps.getProperty("dbname"); if ( dbName == null ) dbName = "weblog"; - String dbUser = System.getenv("DAMLENGINE_PGUSER"); + String dbUser = null; + if ( ! DamlEngine.runAsServlet ) + dbUser = System.getenv("DAMLENGINE_PGUSER"); if ( dbUser == null ) dbUser = DamlEngine.appProps.getProperty("pguser"); - if ( dbUser == null ) + if ( dbUser == null && ! DamlEngine.runAsServlet ) dbUser = System.getenv("PGUSER"); - if ( dbUser == null ) + if ( dbUser == null && ! DamlEngine.runAsServlet ) dbUser = System.getenv("USER"); if ( dbUser == null ) dbUser = System.getProperty("user.name"); if ( dbUser == null ) dbUser = dbName; - String dbPass = System.getenv("DAMLENGINE_PGPASSWORD"); + String dbPass = null; + if ( ! DamlEngine.runAsServlet ) + dbPass = System.getenv("DAMLENGINE_PGPASSWORD"); if ( dbPass == null ) dbPass = DamlEngine.appProps.getProperty("pgpassword"); - if ( dbPass == null ) + if ( dbPass == null && ! DamlEngine.runAsServlet ) dbPass = System.getenv("PGPASSWORD"); - if ( dbPass == null ) - try { - String dbPassFile = System.getenv("DAMLENGINE_PGPASSFILE"); - if ( dbPassFile == null ) - dbPassFile = DamlEngine.appProps.getProperty("pgpassfile"); - if ( dbPassFile == null ) - dbPassFile = System.getenv("PGPASSFILE"); - if ( dbPassFile == null ) - dbPassFile = System.getProperty("user.home") - + "/.pgpass"; + if ( dbPass == null ) { + String dbPassFile = null; + if ( ! DamlEngine.runAsServlet ) + dbPassFile = System.getenv("DAMLENGINE_PGPASSFILE"); + if ( dbPassFile == null ) + dbPassFile = DamlEngine.appProps.getProperty("pgpassfile"); + if ( dbPassFile == null && ! DamlEngine.runAsServlet ) + dbPassFile = System.getenv("PGPASSFILE"); + if ( dbPassFile == null && ! DamlEngine.runAsServlet ) + dbPassFile = System.getProperty("user.home") + + "/.pgpass"; + if ( dbPassFile != null ) try { BufferedReader buf = new BufferedReader(new InputStreamReader(new FileInputStream(dbPassFile))); - String line; - while ( ( line = buf.readLine() ) != null ) { - String[] elts = line.split(":"); - if ( elts.length == 5 - && ( elts[0].equals("*") || elts[0].equals(dbHost) ) - && ( elts[1].equals("*") || elts[1].equals(dbPort) ) - && ( elts[2].equals("*") || elts[2].equals(dbName) ) - && ( elts[3].equals("*") || elts[3].equals(dbUser) ) ) { - dbPass = elts[4]; - break; + String line; + while ( ( line = buf.readLine() ) != null ) { + String[] elts = line.split(":"); + if ( elts.length == 5 + && ( elts[0].equals("*") || elts[0].equals(dbHost) ) + && ( elts[1].equals("*") || elts[1].equals(dbPort) ) + && ( elts[2].equals("*") || elts[2].equals(dbName) ) + && ( elts[3].equals("*") || elts[3].equals(dbUser) ) ) { + dbPass = elts[4]; + break; + } } + } catch (IOException e) { + // Ignore } - } catch (IOException e) { - // Ignore - } + } if ( dbPass == null ) dbPass = ""; final String dbUrl @@ -93,6 +108,7 @@ public final class WeblogDatabaseConnection { dbProps.setProperty("sslfactory", "org.postgresql.ssl.NonValidatingFactory"); conn = (new Driver()).connect(dbUrl, dbProps); conn.createStatement().execute("SET TIME ZONE 0"); + pconn.set(conn); } return conn; } diff --git a/org/madore/damlengine/WeblogSelect.java b/org/madore/damlengine/WeblogSelect.java index a15a672..4e759cc 100644 --- a/org/madore/damlengine/WeblogSelect.java +++ b/org/madore/damlengine/WeblogSelect.java @@ -15,6 +15,7 @@ public final class WeblogSelect { } public static void fullProcess(Context.WeblogSelectionContext wsc, + Context.DynamicContext dc, OutputStream out) throws Exception { @@ -52,8 +53,14 @@ public final class WeblogSelect { } DamlEngine.fullProcess(Files.newInputStream(DamlEngine.templatePath.resolve(templateResourceName)), - out, wsc); + out, wsc, dc); } + public static void fullProcess(Context.WeblogSelectionContext wsc, + OutputStream out) + throws Exception { + fullProcess(wsc, null, out); + } + } diff --git a/org/madore/damlengine/WeblogServlet.java b/org/madore/damlengine/WeblogServlet.java new file mode 100644 index 0000000..b68f114 --- /dev/null +++ b/org/madore/damlengine/WeblogServlet.java @@ -0,0 +1,163 @@ +package org.madore.damlengine; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.Properties; +import java.util.Enumeration; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/* Thinkos about the mess that is tomcat: + + * - Make sure that /usr/share/java/postgresql-jdbc3.jar (or whatever + * contains the class org.postgresql.Driver) is symlinked from + * /var/lib/tomcat7/common (or some path listed in the common.loader + * property of the catalina.properties file). Similarly, + * xercesImpl.jar and probably xml-resolver.jar need to be linked. + + * - The following parameters need to be defined in <init-param> tags + * inside the <servlet> block: base_path, template_path, pghost, + * pgport, dbname, pguser and pgpass or pgpassfile. + + */ + +public class WeblogServlet extends HttpServlet { + + private static final long serialVersionUID = 2014100902L; + + @Override + public void init(ServletConfig cfg) + throws ServletException + { + super.init(cfg); // Important! + synchronized ( DamlEngine.class ) { if ( DamlEngine.appProps == null ) { + DamlEngine.runAsServlet = true; + DamlEngine.appProps = new Properties(); + for (Enumeration<String> e = cfg.getInitParameterNames() ; e.hasMoreElements() ; ) { + String k = e.nextElement(); + DamlEngine.appProps.setProperty(k, cfg.getInitParameter(k)); + } + if ( DamlEngine.appProps.getProperty("base_path") != null ) + DamlEngine.basePath = Paths.get(DamlEngine.appProps.getProperty("base_path")); + if ( DamlEngine.basePath == null ) + DamlEngine.basePath = Paths.get(getServletContext().getRealPath("/")); + if ( DamlEngine.appProps.getProperty("template_path") != null ) + DamlEngine.templatePath = Paths.get(DamlEngine.appProps.getProperty("template_path")); + if ( DamlEngine.templatePath == null ) + DamlEngine.templatePath = DamlEngine.basePath.resolve("templates"); + } } + } + + static abstract class RequestPath { + public static RequestPath parsePath(String pathInfo) + throws ServletException { + Matcher matcher; + if ( pathInfo == null ) + return new NoSuchPath(); + if ( (matcher=Pattern.compile("/d\\.(\\d{4}-\\d{2}-\\d{2})\\.(\\d{4})\\.html").matcher(pathInfo)).matches() ) { + final String date; + final int number; + try { + date = matcher.group(1); + number = Integer.parseInt(matcher.group(2)); + } catch (NumberFormatException e) { + return new NoSuchPath(); + } + try { + final Connection conn = WeblogDatabaseConnection.getConnection(); + final PreparedStatement selSt + = conn.prepareStatement("SELECT id , extract(epoch from mdate) FROM entries WHERE edate=? AND id=?"); + selSt.setString(1,date); + selSt.setInt(2,number); + final ResultSet selRes = selSt.executeQuery(); + if ( selRes.next() ) { + int id = selRes.getInt(1); + double mdate = selRes.getDouble(2); + if ( id != number ) + throw new ServletException("this cannot happen"); + long lastModified = (long)(mdate*1000); + long thisLastModified; + try { + // Attempts to detect when *this* class was last modified. + thisLastModified = WeblogServlet.class.getResource("WeblogServlet.class").openConnection().getLastModified(); + } catch (Exception e) { + thisLastModified = 0; + } + if ( thisLastModified > lastModified ) + lastModified = thisLastModified; + // O'Reilly recommends not using milliseconds part: + lastModified = (lastModified/1000)*1000; + return new SingleBlogEntryPath(number, lastModified); + } else + return new NoSuchPath(); + } catch (SQLException e) { + throw new ServletException(e); + } + } + return new NoSuchPath(); + } + } + + static final class NoSuchPath extends RequestPath { } + + static final class SingleBlogEntryPath extends RequestPath { + public int number; + public long lastModified; + public SingleBlogEntryPath(int number, long lastModified) { + this.number = number; + this.lastModified = lastModified; + } + } + + @Override + public void doGet(HttpServletRequest request, + HttpServletResponse response) + throws IOException, ServletException + { + final RequestPath rp = RequestPath.parsePath(request.getPathInfo()); + if ( rp instanceof NoSuchPath ) { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + if ( ! (rp instanceof SingleBlogEntryPath) ) + throw new ServletException("this cannot happen"); + final int number = ((SingleBlogEntryPath)rp).number; + response.setContentType("text/html; charset=utf-8"); + response.setCharacterEncoding("UTF-8"); + OutputStream out = response.getOutputStream(); + try { + Context.DynamicContext dc = new Context.DynamicContext(); + dc.modTime = ((SingleBlogEntryPath)rp).lastModified; + WeblogSelect.fullProcess(new Context.WeblogSingleSelectionContext(number), + out); + } catch (Exception e) { + throw new ServletException("exception during WeblogSelect.fullProcess()", e); + } + } + + @Override + public long getLastModified(HttpServletRequest request) { + final RequestPath rp; + try { + rp = RequestPath.parsePath(request.getPathInfo()); + } catch (Exception e) { + return -1; + } + if ( rp instanceof SingleBlogEntryPath ) { + final long lastModified = ((SingleBlogEntryPath)rp).lastModified; + return lastModified; + } + return -1; + } +} diff --git a/org/madore/damlengine/WeblogSummary.java b/org/madore/damlengine/WeblogSummary.java index 50ab143..4f560e8 100644 --- a/org/madore/damlengine/WeblogSummary.java +++ b/org/madore/damlengine/WeblogSummary.java @@ -30,6 +30,7 @@ public final class WeblogSummary { } public HashMap<Integer,EntrySummary> entries; + private long obtainedTime; private static WeblogSummary singleton; @@ -37,11 +38,13 @@ public final class WeblogSummary { this.entries = new HashMap<Integer,EntrySummary>(); } - public static WeblogSummary getSummary() { - if ( singleton != null ) + public static synchronized WeblogSummary getSummary(Context.DynamicContext dc) { + if ( singleton != null + && ( dc == null || singleton.obtainedTime < dc.modTime ) ) return singleton; singleton = new WeblogSummary(); try { + singleton.obtainedTime = System.currentTimeMillis(); final Connection conn = WeblogDatabaseConnection.getConnection(); final PreparedStatement selSt = conn.prepareStatement("SELECT id , edate , lang , title , title_xml , do_single_page FROM entries"); diff --git a/org/madore/damlengine/cmdlines b/org/madore/damlengine/cmdlines index e5be081..29ecd0f 100644 --- a/org/madore/damlengine/cmdlines +++ b/org/madore/damlengine/cmdlines @@ -1,2 +1,2 @@ -export CLASSPATH=$HOME/java/damlengine:/usr/share/java/xercesImpl.jar:/usr/share/java/xml-resolver-1.2.jar:/usr/share/java/xml-commons-resolver-1.1.jar:/usr/share/java/postgresql-jdbc3.jar +export CLASSPATH=$HOME/java/damlengine:/usr/share/java/xercesImpl.jar:/usr/share/java/xml-resolver-1.2.jar:/usr/share/java/xml-commons-resolver-1.1.jar:/usr/share/java/postgresql-jdbc3.jar:/usr/share/java/servlet-api-3.0.jar java org.madore.damlengine.DamlEngine some/file.daml |