diff options
Diffstat (limited to 'org/madore')
| -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 | 
