HI all:
I have implemented and External Cache Invalidator object and server to
implement a synchorized way for invalidating Cocoon2's cached pages
(from outside) through an simple XSP page that interpret an HTTP post
message.
This solution is implemented by DB Prism Generator for Cocoon2 and is
usefull for making (using content agregation) news feeds "a la" My
yahoo, this kind of news feed are implemented selecting the news from a
simple table inside the database, this news are only invalidated when
the database process an update/insert or delete on the corrected table
that fires a trigger which send an http messages to the invalidator XSP
page, this message includes information about the url arguments and
cookies used to indentified the page. When this message is processed by
the invalidator XSP page contact the invalidator server and enqueue the
message.
Cocoon will ask to the ExternalCacheValidity which implements the
interface CacheValidity for the validity of the page, this object
contact the invalidator server and return true or false depending on the
response of the External invalidator server.
Here the class which implement the previous functionality:
- com.prism.components.cache.Server (Interface which is an Avalon
component and defines the functionality of External Invalidator Servers)
- com.prism.components.cache.InMemoryServerImpl (Concrete Class
which implements the previous one interface and provide an InMemory
implementation of the Invalidator container, this class uses a HashMap
for storing cached pages keys and a linked list for implementing the
queue of invalidation messages)
- com.prism.caching.ExternalCacheValidity ( implements
org.apache.cocoon.caching.CacheValidity and return true or false if the
pages is cached by the external invalidator server.
Note: Cached by the external invalidator server means that this page is
not stored by the server, the pages is stored in the Cocoon cache
system, this server only register an encoded url which identified the
page and return true or false if the page is valid.
- An XSP page invalidte.xsp, which receives http post messages whith
the pages to be removed from the server.
Usabilty, DBPrismGenerator uses this technique interpreting an internal
sitemap argument named Cache-Control, here an example of this setting:
<map:match pattern="header/**.xml">
<map:generate type="db" src="/cms/CMSj.header">
<map:parameter name="Cache-Control" value="External"/>
<map:parameter name="source" value="/{1}.xml"/>
<map:parameter name="printable" value="no"/>
</map:generate>
<map:serialize/>
</map:match>
This means that the resource will be resolved by DBPrismGenerator
(type=db) and this generator returns on the method generateValidity() an
object of type ExternalCacheValidity, here the code:
....
} else if (this.cacheControl==Server.EXTERNAL) {
if (getLogger().isDebugEnabled())
getLogger().debug("generateValidity called returning
ExternalCacheValidity");
return new
ExternalCacheValidity(this.generateKey(),this.cacheServer,this.encodedURL);
....
This external cache validity objects is identified by a unique key
(generated by the CacheServer) and the encoded url, the encoded url is
generated with the syntax
/path/object?arg1=val1&arg2=val&Coookie:name1=val1&Cookie:name2=val2,
this url is used then to ask for the availibilty of the page on the
server or not.
DBPrismGenerator uses this object but, its possible to use into an
XSP page too.
Here an example of the invalidation message sent by the database to
Cocoon to invalidate an specific page:
POST /dbprism/x-dbprism-cache-invalidate HTTP/1.0
Authorization: BASIC YWRtaW5pc3RyYXRvcjoxMjM=
Content-Length: 439
<?xml version="1.0"?>
<invalidation>
<url exp="/cms/CMSj.content" prefix="no">
<validity level="0"/>
<sitemap-argument name="source" value="/Home.xml"/>
<sitemap-argument name="printable" value="no"/>
</url>
<url exp="/cms/CMSj.header" prefix="no">
<validity level="0"/>
<sitemap-argument name="source" value="/Home.xml"/>
<sitemap-argument name="printable" value="no"/>
</url>
</invalidation>
Obviusly the External Cache Invalidator server use an username and
password to validate the request which arrive from outside Cocoon, this
user/password is encoded into http header Authorization in Base 64 form
(in this case Administrator/123), this invalidation xml message means
that the XSP will invalidate two pages (/cms/CMSj.content and
/cms/CMSj.header) with the corresponding arguments, the option
prefix=yes|no is reserved for future use for implementing massive
invalidation message using regexp. Also validity=0..9 is reserved for
future use on the Cache Server if it imlement and prioritized list of
invalidation messages, this mean that messages with different validities
will re-order in the invalidation queue.
Here an example of an Oracle trigger which sends an invalidation
message to Cocoon when a user update a CMS page :
CREATE OR REPLACE TRIGGER CMS_INVALID_TRIG
AFTER UPDATE on pages
for each row
BEGIN
cms_invalidate_proc('cocoonhost',8888,:new.path||:new.name||'.xml');
END;
It uses an stored procedure cms_invalidate_proc which compose the xml
post message showed above.
I attached the sources of the class and examples for more information.
Best regards, Marcelo.
PD: Sorry for a long email, but I thing that this implementation could
be usefull for other users.
/*****************************************************************************
* Copyright (C) Marcelo F. Ochoa. All rights reserved. *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in *
* the LICENSE file. *
*****************************************************************************/
package com.prism.caching;
import org.apache.cocoon.caching.CacheValidity;
import com.prism.components.cache.Server;
/**
* A validation object for External invalidation functionality.
* This is might be the most used CacheValidity object for DB Prism.
*
* @author
Marcelo F. Ochoa
*/
public final class ExternalCacheValidity
implements CacheValidity {
private long key;
private Server cacheServer;
private String url;
public ExternalCacheValidity(long keyToBeCached,
Server globalCacheServer,
String encodedURL) {
this.key = keyToBeCached;
this.cacheServer = globalCacheServer;
this.url = encodedURL;
}
public boolean isValid(CacheValidity validity) {
if (validity instanceof ExternalCacheValidity) {
return this.cacheServer.isRegistered(url);
}
return false;
}
public long getKey() {
return this.key;
}
public String toString() {
return "ExternalCacheValidity: " + this.key;
}
}
/*****************************************************************************
* Copyright (C) Marcelo F. Ochoa. All rights reserved. *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in *
* the LICENSE file. *
*****************************************************************************/
package com.prism.components.cache;
import org.apache.avalon.framework.activity.Startable;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLoggable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.parameters.ParameterException;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.components.store.Store;
import org.apache.cocoon.Constants;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.util.HashUtil;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.Random;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Cookie;
/**
* This class runs as Thread to invalidate pages from DB Prism.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Marcelo F. Ochoa</a>
*/
public class InMemoryServerImpl extends AbstractLoggable implements Server,
Configurable,
ThreadSafe,
Composable,
Runnable,
Startable {
/** Provide a random key to avoid file caching by MRUStore */
private Random rnd = null;
/** HashMap to store cached pages */
private HashMap cachedPages = null;
/** How many second wait to get messages from invalidation queue*/
private int cleanupthreadinterval;
/** Queue used for invalidation purpose,
* an XSP page will populate this list with the key of pages to be invalidated
* "run" method of the Thread class consume this list
* In a future could be replaced by an JMS queue sorted by priority
*/
private LinkedList invalidationQueue = null;
/** Check if the server has already been started */
private boolean started = false;
/** boolean flag to stop the server */
private boolean stop = false;
private String invalidatorUser;
private String invalidatorPassword;
private ComponentManager manager;
/**
* Get components of the ComponentManager
*
* @param the ComponentManager
*/
public void compose(ComponentManager manager) throws ComponentException {
this.manager = manager;
}
/**
* Initialize the InMemoryServerImpl.
* it requires severel parameters into cocoon.xconf, also requires an entry in
cocoon.roles
* cocoon.roles:
<role name="com.prism.components.cache.Server"
shorthand="cache-server"
default-class="com.prism.components.cache.InMemoryServerImpl"/>
* cocoon.xconf:
<cache-server class="com.prism.components.cache.InMemoryServerImpl"
pool-max="1" pool-min="1">
<parameter name="initialCapacity" value="500"/>
<parameter name="loadFactor" value="50"/>
<parameter name="invalidator-user" value="administrator"/>
<parameter name="invalidator-pass" value="123"/>
<parameter name="Cleanup-Thread-Interval" value="1"/>
</cache-server>
* where:
* - initialCapacity and loadFactor: controls the initialization of the HashMap used
for storing pages keys
* - invalidator-user and invalidator-pass: username and password used for
autorization purposes
* - Cleanup-Thread-Interval: greace periods in seconds betwen every chech into
invalidation queue
*/
public void configure(Configuration conf) throws ConfigurationException {
Parameters params = Parameters.fromConfiguration(conf);
invalidationQueue = new LinkedList();
int initialCapacity = params.getParameterAsInteger("initialCapacity",10);
int loadFactor = params.getParameterAsInteger("loadFactor",5);
cleanupthreadinterval = params.getParameterAsInteger("Cleanup-Thread-Interval",1);
invalidatorUser = params.getParameter("invalidator-user","invalidator");
invalidatorPassword = params.getParameter("invalidator-pass","invalidator");
//System.out.println("Server configure initialCapacity="+initialCapacity+"
loadFactor="+loadFactor);
cachedPages = new HashMap(initialCapacity,loadFactor);
rnd = new Random(System.currentTimeMillis());
if (getLogger().isDebugEnabled())
this.getLogger().debug("Configure InMemoryServerImpl with loadFactor="+
loadFactor+" initialCapacity="+initialCapacity+" Cleanup-Thread-Interval="+
cleanupthreadinterval);
}
/**
* Start the server
*/
public void start() {
//System.out.println("Server start");
Thread server = new Thread(this);
if (getLogger().isDebugEnabled())
this.getLogger().debug("Starting DB Prism External Cache Server Invalidator
thread");
server.setPriority(Thread.MIN_PRIORITY); // nice process, runs with low priority
server.setDaemon(true);
server.setName("DB Prism Cache Server");
server.start();
}
/**
* Stop the server by signalling the stop flags
*/
public void stop() {
//System.out.println("Server stop");
if (getLogger().isDebugEnabled())
this.getLogger().debug("Stoping DB Prism External Cache Server thread");
this.stop = true;
}
/**
* Run the server
* Gets an element from invalidation queue every Cleanup-Thread-Interval seconds
* and remove it from cachedPages HashMap.
*/
public void run() {
if(!started) {
//System.out.println("Server run");
started = true;
if (getLogger().isDebugEnabled())
getLogger().debug("DB Prism External Cache Server running");
while(!this.stop) {
// to be implement;
synchronized(invalidationQueue) {
if (!this.invalidationQueue.isEmpty()) {
// consume once page
String key = (String)this.invalidationQueue.getFirst();
if (getLogger().isDebugEnabled())
getLogger().debug("DB Prism External Cache Server remove "+key);
synchronized(cachedPages) {
cachedPages.remove(key);
}
invalidationQueue.removeFirst();
//System.out.println("Remove key="+key);
}
}
try {
Thread.currentThread().sleep(this.cleanupthreadinterval * 1000);
} catch (InterruptedException ignore) {}
}
}
}
public long generateKey(String encodedURL)
throws ParameterException {
if (this.cachedPages.containsKey(encodedURL))
return java.lang.Long.parseLong((String)this.cachedPages.get(encodedURL));
else {
StringBuffer keyString = new
StringBuffer(String.valueOf(Math.abs(rnd.nextInt())));
keyString.append(":").append(encodedURL);
return HashUtil.hash(keyString.toString());
}
}
/**
* Generates an unique key for a given page.
* This key is generated using the url, the parameter list and the cookies
* cancatenated as a string like this:
* /cms/get.Content?source=/Home.xml&printable=no&Cookie:lang=es
* then uses HashUtil.hash method to generate an unique long value
*/
public String encodeURL(String url,
Parameters sitemapParameters,
HttpServletRequest httpRequest)
throws ParameterException {
boolean includeHttpParameters = false;
Cookie [] cookies = null;
StringBuffer keyString = new StringBuffer(url);
String names[] = sitemapParameters.getNames();
if (names.length>0)
keyString.append("?");
for(int i=0;i<names.length;i++)
if (names[i].equalsIgnoreCase(this.COPY_ARG_NAME))
includeHttpParameters = true;
else if (names[i].equalsIgnoreCase(this.CACHE_CONTROL_ARG_NAME))
continue;
else
keyString.append(names[i]).append("=").append(sitemapParameters.getParameter(names[i])).append("&");
if (includeHttpParameters && httpRequest!=null) {
Enumeration ite = httpRequest.getParameterNames();
while(ite.hasMoreElements()) {
String name = (String)ite.nextElement();
String value = (String)httpRequest.getParameter(name);
keyString.append(name).append("=").append(value).append("&");
}
}
if (includeHttpParameters && httpRequest!=null && (cookies =
httpRequest.getCookies())!=null)
for(int i=0;i<cookies.length;i++)
keyString.append("Cookie:").append(cookies[i].getName()).append("=").append(cookies[i].getValue()).append("&");
//System.out.println("generate key called with "+keyString.toString());
String key = keyString.toString();
int l = key.length();
if (getLogger().isDebugEnabled())
getLogger().debug("DB Prism Cache Server generating key: "+key);
if (key.charAt(l-1)=='?' || key.charAt(l-1)=='&')
return key.substring(0,l-1);
else
return key;
}
/**
* Checks is the given key is cached
*/
public boolean isRegistered(String encodedURL) {
//System.out.println("isRegisterd key: "+key);
if (getLogger().isDebugEnabled())
getLogger().debug("DB Prism Cache Server is register key: "+encodedURL);
return cachedPages.containsKey(encodedURL);
}
/**
* Register the key as external cacheable page
*/
public void registerCacheablePage(String encodedURL, long key)
throws ParameterException {
//System.out.println("registerCacheablePage key: "+key);
if (getLogger().isDebugEnabled())
getLogger().debug("DB Prism Cache Server register cacheable page with key:
"+encodedURL);
this.cachedPages.put(encodedURL,java.lang.Long.toString(key));
}
/**
* Removes the key as external cacheable page
* requires a valid username and password
* validity value is reserved for future uses to implement a prioritized queue
*/
public void removeCacheablePage(String username,
String password,
String encodedURL,
int validity)
throws ProcessingException {
//System.out.println("removeCacheablePage key: "+encodedURL);
if (getLogger().isDebugEnabled())
getLogger().debug("DB Prism Cache Server queue remove cacheable page with key:
"+encodedURL+" validity="+validity);
if (!this.invalidatorUser.equals(username) ||
!this.invalidatorPassword.equals(password))
throw new ProcessingException("External Cache Invalidation Server: bad
username/password");
if (this.cachedPages.containsKey(encodedURL))
this.invalidationQueue.addLast(encodedURL);
else
throw new ProcessingException("External Cache Invalidation Server: this page not
found");
}
}
/*****************************************************************************
* Copyright (C) Marcelo F. Ochoa. All rights reserved. *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in *
* the LICENSE file. *
*****************************************************************************/
package com.prism.components.cache;
import javax.servlet.http.HttpServletRequest;
import org.apache.avalon.framework.component.Component;
import org.apache.cocoon.ProcessingException;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.parameters.ParameterException;
/**
*
* @author
Marcelo F. Ochoa
*/
public interface Server extends Component {
String ROLE = "com.prism.components.cache.Server";
public static final int NONE = 0;
public static final int EXTERNAL = 1;
public static final int NOP = 2;
public static final String COPY_ARG_NAME = "Copy-Request-Arguments";
public static final String CACHE_CONTROL_ARG_NAME = "Cache-Control";
public static final String CACHE_CONTROL_EXTERNAL = "External";
public static final String CACHE_CONTROL_NOP = "NOP";
public static final String CACHE_CONTROL_NONE = "no-cache";
/**
* Checks is the given key is cached
*/
public boolean isRegistered(String encodedURL);
/**
* Register the key as external cacheable page
*/
public void registerCacheablePage(String encodedURL, long key)
throws ParameterException;
/**
* Removes the key as external cacheable page
* requires a valid username and password
* validity value is reserved for future uses to implement a prioritized queue
*/
public void removeCacheablePage(String username,
String password,
String encodedURL,
int validity)
throws ProcessingException;
/**
* Generates an unique key for a given encoded url page.
* This key is generated using generateKey and random value
*/
public long generateKey(String encodedURL)
throws ParameterException;
/**
* Generates an unique key for a given page.
* This key is generated using the url, the parameter list and the cookies
* cancatenated as a string like this:
* /cms/get.Content?source=/Home.xml&printable=no&Cookie:lang=es
* then uses HashUtil.hash method to generate an unique long value
*/
public String encodeURL(String url, Parameters sitemapParameters, HttpServletRequest httpRequest)
throws ParameterException;
}
/*****************************************************************************
* Copyright (C) Marcelo F. Ochoa. All rights reserved. *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in *
* the LICENSE file. *
*****************************************************************************/
package com.prism;
import com.prism.components.cache.Server;
import com.prism.caching.ExternalCacheValidity;
import com.prism.utils.Block;
import com.prism.utils.CocoonRequestWrapper;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.StringReader;
import java.io.Reader;
import java.security.Principal;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;
import java.util.StringTokenizer;
import java.util.Calendar;
import java.util.TimeZone;
import org.apache.avalon.excalibur.pool.Recyclable;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLoggable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.parameters.ParameterException;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.caching.CacheValidity;
import org.apache.cocoon.caching.Cacheable;
import org.apache.cocoon.caching.NOPCacheValidity;
import org.apache.cocoon.Constants;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.components.parser.Parser;
import org.apache.cocoon.environment.Context;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.environment.Response;
import org.apache.cocoon.environment.SourceResolver;
import org.apache.cocoon.environment.http.HttpEnvironment;
import org.apache.cocoon.generation.ComposerGenerator;
import org.apache.cocoon.xml.AbstractXMLProducer;
import org.apache.cocoon.xml.XMLProducer;
import org.apache.cocoon.util.HashUtil;
import org.apache.log.Logger;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.InputStreamReader;
/**
* Parallel content agregation
* Problem: DBPrismGenrator divides the XML generation in two stages
* at setup time DBPrismGenerator create a private Thread (delayer) which
* calls to DBPrism.makePage method, then permits to Cocoon2's threads to continue
* with other process.
* At setup time DBPrismGenerator uses Block class (blocker) as private semaphore
which
* locks generate method until the makePage operation is finished.
* When makePage finish (run method of Delayer) unlock the semaphore permiting
* to DBPrismGenerator to continue with the generation step calling to
DBPrism.getPage()
* flow:
* - Cocoon2 setup stage (in parallel)
--> setup (part1) (Start a private engine 1 in parallel)
--> setup (part2) (Start a private engine 2 in parallel)
--> setup (partn) (Start a private engine 1 in parallel)
- Cocoon2 generate stage (Sequencially gets the content of the parts)
--> generate (part1) (Wait until engine 1 finish)
--> generate (part2) (Wait until engine 2 finish)
--> generate (partn) (Wait until engine n finish)
- Serialize stage.
*/
/**
* Allows DB Prism to be used as a Cocoon2's generator.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Marcelo F. Ochoa</a>
*/
public class DBPrismGenerator extends ComposerGenerator
implements Composable, Configurable, Recyclable, Cacheable, Contextualizable{
protected Request request=null;
protected Response response=null;
protected Context context=null;
protected HttpServletRequest httpRequest = null;
protected DBPrism engine = null;
protected CocoonRequestWrapper req = null;
protected Delayer delayer = null;
protected Block blocker = null;
protected Server cacheServer = null;
protected long key = 0;
protected String encodedURL;
protected int cacheControl = 0;
/** Contextualize this class
* Stores context information to be use by configure method in order to resolve
* /prism.properties as $WEBAPP/prism.properties in servlet 2.2+ containers
*/
public void contextualize(org.apache.avalon.framework.context.Context ctx) throws
ContextException {
//System.out.println("contextualize called id="+this.toString());
this.context = (Context)ctx.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
}
/**
* Set the sitemap-provided configuration.
* @param conf The configuration information
* @exception ConfigurationException
* For example (on Cocoon sitemap.xmap):
* <map:generators default="file">
* ....
* <map:generator name="db" src="com.prism.DBPrismGenerator"
properties="./applications/dbprism/dbprism/prism.properties"/>
* ....
* </map:generators>
**/
public void configure(Configuration conf) throws ConfigurationException {
String propfilename = conf.getAttribute("properties",DBPrism.PROPERTIES);
try {
if (propfilename.equals(DBPrism.PROPERTIES)) // converts to CONTEXT form
propfilename =
this.context.getResource("/").toExternalForm()+DBPrism.PROPERTIES.substring(1);
//System.out.println("configure called properties="+propfilename);
DBPrism.initDBPrism(propfilename);
} catch (Exception e) {
throw new ConfigurationException("Can't init DB Prism Engine: "+e.toString());
}
}
/**
* Generate the unique key.
* This key must be unique inside the space of this component.
*
* @return The generated key hashes the src
*/
public long generateKey() {
try {
if (this.key!=0)
return key; // reduce overhead to generateKey method
else
this.key = cacheServer.generateKey(this.encodedURL);
return this.key;
} catch (Exception pe) {
if (getLogger().isDebugEnabled())
getLogger().debug("generateKey Exception, returning 0",pe);
return 0;
}
}
/**
* Generate the validity object.
*
* @return The generated validity object or <code>null</code> if the
* component is currently not cacheable.
*/
public CacheValidity generateValidity() {
//System.out.println("generateValidity called");
if (this.cacheControl==Server.NOP) {
if (getLogger().isDebugEnabled())
getLogger().debug("generateValidity called returning NOPCacheValidity");
return new NOPCacheValidity();
} else if (this.cacheControl==Server.EXTERNAL) {
if (getLogger().isDebugEnabled())
getLogger().debug("generateValidity called returning
ExternalCacheValidity");
return new
ExternalCacheValidity(this.generateKey(),this.cacheServer,this.encodedURL);
} else {// default null
if (getLogger().isDebugEnabled())
getLogger().debug("generateValidity called returning null");
return null;
}
}
/**
* Recycle the generator by removing references
*/
public void recycle() {
if (blocker!=null) {
blocker.lock(engine); // Block this engine, wait until Delayer finish
try {
// free dbprism connection
engine.recycle(this.req);
} catch (SQLException sqe) { }; // Ignore
}
super.recycle();
this.request = null;
this.response = null;
this.context = null;
this.httpRequest = null;
this.engine = null;
this.req = null;
this.delayer = null;
this.blocker = null;
this.cacheServer = null;
this.key = 0;
this.encodedURL = null;
if (getLogger().isDebugEnabled())
getLogger().debug("Recycle called");
//System.out.println("Recycle called id="+this.toString());
}
public void setup(SourceResolver resolver, Map objectModel, String src, Parameters
par)
throws ProcessingException, SAXException, IOException {
String cacheControlStr;
//System.out.println("setup called id="+this.toString());
super.setup(resolver, objectModel, src, par);
if (getLogger().isDebugEnabled())
getLogger().debug("setup called url: " + src + " instance="+this.toString());
try {
this.cacheServer = (Server) this.manager.lookup(Server.ROLE);
} catch (ComponentException ce) {
throw new ProcessingException("Can't get External invalidator server
instance");
}
this.request = (Request) objectModel.get(Constants.REQUEST_OBJECT);
this.context = (Context) objectModel.get(Constants.CONTEXT_OBJECT);
this.response = (Response) objectModel.get(Constants.RESPONSE_OBJECT);
cacheControlStr =
par.getParameter(Server.CACHE_CONTROL_ARG_NAME,Server.CACHE_CONTROL_NONE);
if (cacheControlStr.startsWith(Server.CACHE_CONTROL_EXTERNAL)) {
cacheControl = Server.EXTERNAL;
} else if (cacheControlStr.equalsIgnoreCase(Server.CACHE_CONTROL_NOP))
cacheControl = Server.NOP;
else
cacheControl = Server.NONE;
// ensure that we are running in a servlet environment
this.httpRequest =
(HttpServletRequest)objectModel.get(HttpEnvironment.HTTP_REQUEST_OBJECT);
if (this.httpRequest == null) {
throw new ProcessingException("HttpServletRequest object not available");
}
String url = this.resolver.resolve(this.source).getSystemId();
// Guarantee src parameter is a file
if (!url.startsWith("file:/"))
throw new IOException("Protocol not supported: " + url);
url = url.substring(5);
req = new CocoonRequestWrapper(httpRequest,url,par);
try {
this.encodedURL =
cacheServer.encodeURL(
this.source,this.parameters,this.httpRequest);
} catch (ParameterException pe) {
throw new ProcessingException("Can't encode the url",pe);
}
if (this.cacheControl==Server.EXTERNAL &&
cacheServer.isRegistered(this.encodedURL)) {
// do not start page generation
if (getLogger().isDebugEnabled())
getLogger().debug("found a cached page for key: " + this.generateKey());
//System.out.println("setup found a cached page");
return;
}
if (getLogger().isDebugEnabled())
getLogger().debug("executing request:" + url + " instance="+this.toString());
engine = new DBPrism();
blocker = new Block();
blocker.lock(engine); // Block this engine until the delayer is started
delayer = new Delayer(engine,req,blocker);
delayer.start(); // Sent engine.makePage() message in background
//System.out.println("setup starts a page generation with
engine="+engine.toString());
}
/**
* Generate XML data from DB Prism Engine.
* Wait until delayer finish his engine.MakePage call
* and call to engine.getPage if everything is OK.
*/
public void generate()
throws ProcessingException, IOException {
long startTime = System.currentTimeMillis();
//System.out.println("generate called id="+this.toString());
//System.out.println("generate called with engine="+engine.toString());
Parser parser = null;
HttpServletResponse httpResponse =
(HttpServletResponse)this.objectModel.get(HttpEnvironment.HTTP_RESPONSE_OBJECT);
if (httpResponse == null) {
throw new ProcessingException("HttpServletResponse object not available");
}
try {
// pipe the results into the parser
parser = (Parser)this.manager.lookup(Parser.ROLE);
parser.setConsumer(this.xmlConsumer);
blocker.lock(engine); // Block this engine, wait until Delayer finish
if (delayer.status == Delayer.NOTAUTOHRIZED) {
sendUnauthorized(httpResponse,delayer.msg);
try {
parser = (Parser)this.manager.lookup(Parser.ROLE);
parser.setConsumer(this.xmlConsumer);
parser.parse(new InputSource(new
StringReader(DBPrism.UnauthorizedText)));
} catch (SAXException e) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("SAXException in generate()", e);
getLogger().debug("Embedded SAXException generate()",
e.getException());
}
throw new ProcessingException("SAXException
DBPrismGenerator.generate()",e.getException());
} catch (ComponentException e) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("ComponentException in generate()", e);
getLogger().debug("Embedded ComponentException in generate()", e);
}
throw new ProcessingException("ComponentException in
DBPrismGenerator.generate()",e);
}
} else if (delayer.status == Delayer.GENERALERROR) {
if (getLogger().isDebugEnabled())
getLogger().debug("GeneralError in generate(): " + delayer.msg);
throw new ProcessingException("GeneralError in
DBPrismGenerator.generate(): " + delayer.msg);
} else if (delayer.status == Delayer.STOPED) {
if (getLogger().isDebugEnabled())
getLogger().debug("GeneralError in generate(): Delayer not
started");
throw new ProcessingException("General Error
DBPrismGenerator.generate(): Delayer not started");
} else if (delayer.status == Delayer.FINISHED) {
// if everything is OK, get the page from BLOB buffer.
showPage(httpResponse,parser,engine.getPage(req));
if (this.cacheControl==Server.EXTERNAL) // if eveything is OK, caches
this page
this.cacheServer.registerCacheablePage(this.encodedURL,this.generateKey());
}
} catch (SAXException e) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("SAXException generate()", e);
getLogger().debug("Embedded SAXException generate()", e.getException());
}
throw new ProcessingException("SAXException
DBPrismGenerator.generate()",e.getException());
} catch (IOException e) {
if (getLogger().isDebugEnabled())
getLogger().debug("IOException in generate()", e);
throw new ProcessingException("IOException DBPrismGenerator.generate()",e);
} catch (Exception e) {
if (getLogger().isDebugEnabled())
getLogger().debug("Exception in generate()", e);
throw new ProcessingException("Exception DBPrismGenerator.generate()",e);
} finally {
blocker.unlock(engine); // unlock the engine
// Cleanup local resources
if (parser != null) this.manager.release(parser);
if (getLogger().isDebugEnabled())
getLogger().debug("processing time
"+(System.currentTimeMillis()-startTime)+"ms.");
}
}
public void sendUnauthorized(HttpServletResponse res, String msg)
throws IOException {
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// put your realm here
res.setHeader("WWW-authenticate","basic realm=\"" + msg + "\"");
}
/**
* Process cookie definicion from Jxtp.sendCookie or Jxtp.removeCookie
* Syntax definition from http://www.netscape.com/newsref/std/cookie_spec.html
* See Jxtp package
* Don't work with cookie definition greater than 255 char's (muti-line cookies)
*/
public Cookie Make_Cookie(String s)
{ Cookie choc_chip;
long age;
// Divide the string cookie format in fields by the ;
StringTokenizer st = new StringTokenizer(s,";");
// the name = value pairs is required
String s1 = (String) st.nextElement();
choc_chip = new
Cookie(s1.substring(0,s1.indexOf("=")),s1.substring(s1.indexOf("=")+1));
//System.out.println("Name =>" + choc_chip.getName());
//System.out.println("Value =>" + choc_chip.getValue());
while(st.hasMoreElements()) {
s1 = (String) st.nextElement();
// Proccess the expires field
if (s1.startsWith(" expires=")) {
s1 = s1.substring(s1.indexOf("=")+1);
try {
// Convert the Date especification to Age format of Servlets Cookie
// Acording to non deprected api of JDK 1.1
DateFormat df = new SimpleDateFormat("EEE MMM-dd-yy HH:mm:ss zzz",
java.util.Locale.US);
Date expire = df.parse(s1);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTime(expire);
if (cal.get(1)==1990 && cal.get(2)==0 && cal.get(0)==1) {
// Option remove cookie
age = 0;
} else {
// Session cookie or expire in the future
java.util.Date now = new java.util.Date();
age = (expire.getTime()-now.getTime())/1000;
age = (age < 0 ? -1 : age);
}
choc_chip.setMaxAge((int)age);
//System.out.println("Age =>" + choc_chip.getMaxAge());
} catch(Exception e) {
if (getLogger().isDebugEnabled())
getLogger().warn("Invalid Date format en cookie String");
}
} // end if expires=
// Proccess the path field
if (s1.startsWith(" path=")) {
// Set Path
choc_chip.setPath(s1.substring(s1.indexOf("=")+1));
//System.out.println("Path =>" + choc_chip.getPath());
} // end if path=
// Proccess the domain field
if (s1.startsWith(" domain=")) {
// Set Domain
choc_chip.setDomain(s1.substring(s1.indexOf("=")+1));
//System.out.println("Domain =>" + choc_chip.getDomain());
} // end if domain=
// Proccess the secure flags
if (s1.startsWith(" secure")) {
// Set Secure
choc_chip.setSecure(true);
//System.out.println("Secure");
} // end if secure
} // end while st.hasMoreElements
// Return the cookie to the caller
return choc_chip;
}
/**
* Returns the generated page (Reader) to the browser
* check for the header to set the response object*/
public void showPage(HttpServletResponse res,
Parser parser,
Reader page) throws Exception
{
char [] buff_out = new char[8192];
BufferedReader in = new BufferedReader(page,8192);
StringBuffer xmlPage = new StringBuffer(8192);
int i;
String s;
s=in.readLine();
if (s.startsWith("Location: ")) {
s = s.substring(10);
res.sendRedirect(s);
if (getLogger().isDebugEnabled())
getLogger().debug("Location header: "+s);
return;
} else if (s.startsWith("Content-type: ") || s.startsWith("Status: "))
// Verify if the position 1..n have the Syntax "xxx : yyy"
// handle special case of Cookie definition or Content-type
// generated by owa_cookie.send or owa_util.mime_header
// Cache-Control: public, is interpreted as Document Cacheable.
// others header definitions are pased as is
do { // Process each line of header
if (s.startsWith("Content-type: "))
continue; // Ignore Content-type, Cocoon handles the output method
else if (s.startsWith("WWW-authenticate: ")) {
String reason = s.substring(18).trim();
if (getLogger().isDebugEnabled())
getLogger().debug("NotAuthorizedException Header ("+reason+")");
sendUnauthorized(res,reason);
parser.parse(new InputSource(new
StringReader(DBPrism.UnauthorizedText)));
return;
} else if (s.startsWith("Set-Cookie: ")) { // Makes cookies
// Parse the cookie line
Cookie choc_chip = Make_Cookie(s.substring(12));
res.addCookie(choc_chip);
if (getLogger().isDebugEnabled())
getLogger().debug("Set-Cookie header: "+s.substring(12));
} else
try { // if dosn't cookie is another header info
res.setHeader(s.substring(0,s.indexOf(':')),
s.substring(s.indexOf(':')+2));
if (getLogger().isDebugEnabled())
getLogger().debug("SetHeader : "+s);
} catch (Exception e) { };
} while ((s=in.readLine())!=null && s.length()>0); // End while header lines
// Output the rest of generated page in htp.htbuf
// send it without pay attention to new lines
while ((i=in.read(buff_out))>0)
xmlPage.append(buff_out,0,i);
parser.parse(new InputSource(new StringReader(xmlPage.toString())));
}
/**
* Parallel content agregation
* Problem: DBPrismGenrator divides the XML generation in two stages
* at setup time DBPrismGenerator create a private Thread (delayer) which
* calls to DBPrism.makePage method, then permits to Cocoon2's threads to continue
* with other process.
* At setup time DBPrismGenerator uses Block class as private semaphore which
* locks generate method until the makePage operation is finished.
* When makePage finish (run method of Delayer) unlock the semaphore permiting
* to DBPrismGenerator to continue with the generation step calling to
DBPrism.getPage()
* flow:
* - Cocoon2 setup stage (in parallel)
--> setup (part1) (Start a private engine 1 in parallel)
--> setup (part2) (Start a private engine 2 in parallel)
--> setup (partn) (Start a private engine 1 in parallel)
- Cocoon2 generate stage (Sequencially gets the content of the parts)
--> generate (part1) (Wait until engine 1 finish)
--> generate (part2) (Wait until engine 2 finish)
--> generate (partn) (Wait until engine n finish)
- Serialize stage.
*/
class Delayer extends Thread {
static final int STOPED = 0;
static final int RUNNING = 1;
static final int NOTAUTOHRIZED = 2;
static final int GENERALERROR = 3;
static final int FINISHED = 4;
int status = STOPED;
String msg = "OK";
private DBPrism dbEngine = null;
private CocoonRequestWrapper theRequest = null;
private Block theBlock = null;
public Delayer(DBPrism eng, CocoonRequestWrapper request, Block blocker) throws
ProcessingException {
if (eng==null || request==null) // sanity checks
throw new ProcessingException("DBPrism engine or request is null in
Delayer");
dbEngine = eng;
theRequest = request;
theBlock = blocker;
status = STOPED;
}
public void run() {
//System.out.println("Starting request to engine = "+dbEngine.toString());
status = RUNNING;
try {
dbEngine.makePage(req);
status = FINISHED;
} catch (NotAuthorizedException ne) {
status = NOTAUTOHRIZED;
msg = ne.getMessage();
} catch (Exception e) {
status = GENERALERROR;
msg = e.toString();
} finally {
theBlock.unlock(dbEngine); // Unblock the engine
//System.out.println("Ending request to engine = "+dbEngine.toString());
//System.out.println("Return Status ="+status);
//System.out.println("Return Message="+msg);
dbEngine = null;
theRequest = null;
theBlock = null;
}
}
} // End inher class Delayer
}
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsp:page
language="java"
xmlns:xsp="http://apache.org/xsp"
>
<xsp:structure>
<xsp:include>org.apache.cocoon.components.language.markup.xsp.XSPUtil</xsp:include>
<xsp:include>org.apache.avalon.framework.context.ContextException</xsp:include>
<xsp:include>org.apache.avalon.framework.parameters.Parameters</xsp:include>
<xsp:include>org.apache.avalon.framework.parameters.ParameterException</xsp:include>
<xsp:include>org.apache.cocoon.xml.XMLConsumer</xsp:include>
<xsp:include>com.prism.components.cache.Server</xsp:include>
<xsp:include>java.io.BufferedReader</xsp:include>
<xsp:include>java.io.InputStreamReader</xsp:include>
<xsp:include>java.util.Enumeration</xsp:include>
<xsp:include>java.util.Hashtable</xsp:include>
<xsp:include>java.util.HashMap</xsp:include>
<xsp:include>java.util.Locale</xsp:include>
<xsp:include>java.util.Vector</xsp:include>
<xsp:include>java.security.Principal</xsp:include>
<xsp:include>javax.servlet.http.HttpServletRequest</xsp:include>
<xsp:include>javax.servlet.http.Cookie</xsp:include>
<xsp:include>javax.servlet.http.HttpSession</xsp:include>
<xsp:include>javax.servlet.ServletInputStream</xsp:include>
<xsp:include>javax.servlet.RequestDispatcher</xsp:include>
<xsp:include>org.apache.cocoon.environment.http.HttpEnvironment</xsp:include>
<xsp:include>org.w3c.dom.Document</xsp:include>
<xsp:include>org.xml.sax.Attributes</xsp:include>
<xsp:include>org.apache.cocoon.xml.AbstractXMLPipe</xsp:include>
<xsp:include>sun.misc.BASE64Decoder</xsp:include>
</xsp:structure>
<xsp:logic><![CDATA[
// Class attributes
private static String EXTRACT_URI="http://www.plenix.com/dbprism/invalidation";
private static String EXTRACT_ELEMENT_INVALIDATION="invalidation";
private static String EXTRACT_ELEMENT_URI="url";
private static String EXTRACT_ELEMENT_VALIDITY="validity";
private static String EXTRACT_ELEMENT_COOKIE="cookie";
private static String EXTRACT_ELEMENT_HTTP="http-argument";
private static String EXTRACT_ELEMENT_ARGUMENT="sitemap-argument";
Server cacheServer = null;
String name = "";
String passwd = "";
]]></xsp:logic>
<xsp:logic><![CDATA[
// Inher class, private request
// Note that it only implement getter/setter class for parameters and cookies
// other methots returns null, be carefull with this.
public class MyHtppRequest implements HttpServletRequest
{
HttpServletRequest request;
Hashtable parameters;
Vector cookies;
MyHtppRequest(HttpServletRequest request) {
this.cookies = null;
this.request = request;
this.parameters = new Hashtable();
}
public String getAuthType(){ return request.getAuthType(); }
public Cookie[] getCookies() {
if (cookies.isEmpty())
return null;
else
return (Cookie[])cookies.toArray();
}
public void setCookies(Cookie cookie) {
cookies.add(cookie);
}
public long getDateHeader(String s){ return request.getDateHeader(s); }
public String getHeader(String s){ return request.getHeader(s); }
public Enumeration getHeaders(String s){ return request.getHeaders(s); }
public Enumeration getHeaderNames(){ return request.getHeaderNames(); }
public int getIntHeader(String s){ return request.getIntHeader(s); }
public String getMethod(){ return request.getMethod(); }
public String getPathInfo(){ return request.getPathInfo(); }
public String getPathTranslated(){ return request.getPathTranslated(); }
public String getContextPath(){ return request.getContextPath(); }
public String getQueryString(){ return request.getQueryString(); }
public String getRemoteUser(){ return request.getRemoteUser(); }
public boolean isUserInRole(String s){ return request.isUserInRole(s); }
public Principal getUserPrincipal(){ return request.getUserPrincipal(); }
public String getRequestedSessionId(){ return request.getRequestedSessionId();
}
public String getRequestURI(){ return request.getRequestURI(); }
public String getServletPath(){ return this.getServletPath(); }
public HttpSession getSession(boolean flag){ return request.getSession(flag); }
public HttpSession getSession(){ return request.getSession(); }
public boolean isRequestedSessionIdValid(){ return
request.isRequestedSessionIdValid(); }
public boolean isRequestedSessionIdFromCookie(){ return
request.isRequestedSessionIdFromCookie(); }
public boolean isRequestedSessionIdFromURL(){ return
request.isRequestedSessionIdFromURL(); }
public boolean isRequestedSessionIdFromUrl(){ return
request.isRequestedSessionIdFromUrl(); }
public Object getAttribute(String s){ return request.getAttribute(s); }
public Enumeration getAttributeNames(){ return request.getAttributeNames(); }
public String getCharacterEncoding(){ return request.getCharacterEncoding(); }
public int getContentLength(){ return request.getContentLength(); }
public String getContentType(){ return request.getContentType(); }
public ServletInputStream getInputStream() throws IOException{ return
request.getInputStream(); }
public String getParameter(String s)
{
return (String)this.parameters.get(s);
}
public Enumeration getParameterNames()
{
return this.parameters.keys();
}
public String[] getParameterValues(String s) {
String [] val = new String[1];
val[0] = (String)parameters.get(s);
return val;
}
public void setParameter(String name, String value)
{
this.parameters.put(name,value);
}
public String getProtocol(){ return request.getProtocol(); }
public String getScheme(){ return request.getScheme(); }
public String getServerName(){ return request.getServerName(); }
public int getServerPort(){ return request.getServerPort(); }
public BufferedReader getReader()
throws IOException{ return request.getReader(); }
public String getRemoteAddr(){ return request.getRemoteAddr(); }
public String getRemoteHost(){ return request.getRemoteHost(); }
public void setAttribute(String s, Object obj){ request.setAttribute(s,obj); }
public void removeAttribute(String s){ request.removeAttribute(s); }
public Locale getLocale(){ return request.getLocale(); }
public Enumeration getLocales(){ return request.getLocales(); }
public boolean isSecure(){ return request.isSecure(); }
public RequestDispatcher getRequestDispatcher(String s){ return
request.getRequestDispatcher(s); }
public String getRealPath(String s){ return request.getRealPath(s); }
public java.lang.StringBuffer getRequestURL() { return null; }
public java.util.Map getParameterMap()
{
return null;
}
public void setCharacterEncoding(java.lang.String $1) { }
} // end inher class
]]></xsp:logic>
<xsp:logic><![CDATA[
// Inher class
public class InvalidateExtractor extends AbstractXMLPipe
{
HttpServletRequest request;
private int state = 0;
int count = 0;
private String url;
private boolean prefix;
private int validity;
Parameters par;
Cookie cookie;
MyHtppRequest httpParameter;
AttributesImpl xspAttr;
InvalidateExtractor(XMLConsumer consumer, HttpServletRequest request) {
this.xspAttr = new AttributesImpl();
this.setConsumer(consumer);
this.url = "";
this.prefix = false;
this.validity = 0;
this.par = new Parameters();
this.request = request;
this.httpParameter = new MyHtppRequest(this.request);
this.cookie = null;
this.xspAttr.clear();
}
/**
* Receive notification of the beginning of an element.
*
* @param uri The Namespace URI, or the empty string if the element has no
* Namespace URI or if Namespace
* processing is not being performed.
* @param loc The local name (without prefix), or the empty string if
* Namespace processing is not being performed.
* @param raw The raw XML 1.0 name (with prefix), or the empty string if
* raw names are not available.
* @param a The attributes attached to the element. If there are no
* attributes, it shall be an empty Attributes object.
*/
public void startElement(String uri, String loc, String raw,
Attributes a) throws SAXException {
//System.out.println(" Start uri="+uri+" loc="+loc+" raw="+raw+" state="+state);
if (state == 0 && EXTRACT_ELEMENT_INVALIDATION.equals(loc))
state = 1;
else if (state == 1 && EXTRACT_ELEMENT_URI.equals(loc)) {
url = a.getValue("exp");
prefix = "yes".equalsIgnoreCase(a.getValue("prefix"));
state = 2;
//System.out.println("url="+url + " prefix="+prefix);
} else if (state == 2 && EXTRACT_ELEMENT_ARGUMENT.equals(loc)) {
String name = a.getValue("name");
String value = a.getValue("value");
par.setParameter(name,value);
//System.out.println("sitemap argument name="+name+" value="+value);
} else if (state == 2 && EXTRACT_ELEMENT_HTTP.equals(loc)) {
String name = a.getValue("name");
String value = a.getValue("value");
httpParameter.setParameter(name,value);
//System.out.println("http argument name="+name+" value="+value);
} else if (state == 2 && EXTRACT_ELEMENT_VALIDITY.equals(loc)) {
validity = Integer.parseInt(a.getValue("level"));
//System.out.println("url="+url + " prefix="+prefix + " validity="+validity);
} else if (state == 2 && EXTRACT_ELEMENT_COOKIE.equals(loc)) {
String name = a.getValue("name");
String value = a.getValue("value");
cookie = new Cookie(name,value);
httpParameter.setCookies(cookie);
//System.out.println("cookie name="+name+" value="+value);
}
}
/**
* Receive notification of the end of an element.
*
* @param uri The Namespace URI, or the empty string if the element has no
* Namespace URI or if Namespace
* processing is not being performed.
* @param loc The local name (without prefix), or the empty string if
* Namespace processing is not being performed.
* @param raw The raw XML 1.0 name (with prefix), or the empty string if
* raw names are not available.
*/
public void endElement(String uri, String loc, String raw)
throws SAXException {
//System.out.println("End uri="+uri+" loc="+loc+" raw="+raw+"
state="+state);
if (state==2 && EXTRACT_ELEMENT_URI.equals(loc)) {
state=1;
try {
String key = cacheServer.encodeURL(url,par,httpParameter);
count++;
//System.out.println("remove key ="+key);
cacheServer.removeCacheablePage(name,passwd,key,validity);
xspAttr.addAttribute("", "expr", "expr", "CDATA", url);
xspAttr.addAttribute("", "id", "id", "CDATA", ""+count);
xspAttr.addAttribute("", "status", "status", "CDATA", "ok");
xspAttr.addAttribute("", "numinv", "numinv", "CDATA", "0");
} catch (Exception e) {
xspAttr.addAttribute("", "expr", "expr", "CDATA", url);
xspAttr.addAttribute("", "id", "id", "CDATA", ""+count);
xspAttr.addAttribute("", "status", "status", "CDATA", "not found");
xspAttr.addAttribute("", "numinv", "numinv", "CDATA", "0");
System.out.println("Error in remove ="+e.getMessage());
} finally {
this.contentHandler.startElement("", "url", "url", xspAttr);
this.url = "";
this.prefix = false;
this.validity = 0;
this.par = new Parameters();
this.httpParameter = new MyHtppRequest(this.request);
this.cookie = null;
this.xspAttr.clear();
this.contentHandler.endElement("", "url", "url");
}
}
else if (state==1 && EXTRACT_ELEMENT_INVALIDATION.equals(loc))
state=0;
}
}
]]></xsp:logic>
<invalidationresult><xsp:logic><![CDATA[
try {
this.cacheServer = (Server) this.manager.lookup(Server.ROLE);
} catch (ComponentException ce) {
System.out.println("invalidate.xsp: Can't get External Invalidator Server
instance");
}
HttpServletRequest httpRequest =
(HttpServletRequest)objectModel.get(HttpEnvironment.HTTP_REQUEST_OBJECT);
if (httpRequest == null) {
throw new ProcessingException("HttpServletRequest object not available");
}
Parser parser = null;
BASE64Decoder B64 = new BASE64Decoder();
int i;
String str;
try {
str = httpRequest.getHeader("Authorization").substring(6);
str = new String(B64.decodeBuffer(str),"iso-8859-1");
} catch (Exception e) {
str = ":";
}
i = str.indexOf(':');
name = str.substring(0,i);
passwd = str.substring(i+1);
BufferedReader ir = new BufferedReader(new
InputStreamReader(httpRequest.getInputStream()));
String line;
StringBuffer msg = new StringBuffer();
while((line=ir.readLine())!=null)
msg.append(line+"\n");
//System.out.println("User=>"+name);
//System.out.println("Password=>"+passwd);
//System.out.println("POST Message=>");
//System.out.println(msg.toString());
InvalidateExtractor extractor = new
InvalidateExtractor((XMLConsumer)this.contentHandler,httpRequest);
try {
parser = (Parser)this.manager.lookup(Parser.ROLE);
if (parser==null)
System.out.println("Parser is null");
else {
parser.setConsumer(extractor);
parser.parse(new InputSource(new StringReader(msg.toString())));
}
} catch (ComponentException e) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("ComponentException in invalidate.generate()", e);
getLogger().debug("Embedded ComponentException in invalidate.generate()", e);
}
//System.out.println("ComponentException="+e.getMessage());
} catch (SAXException e) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("SAXException in invalidate.generate()", e);
getLogger().debug("Embedded SAXException in invalidate.generate()", e);
}
//System.out.println("SAXException="+e.getMessage());
} catch (IOException e) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("IOException in invalidate.generate()", e);
getLogger().debug("Embedded IOException in invalidate.generate()", e);
}
//System.out.println("IOException="+e.getMessage());
} finally {
// Cleanup local resources
if (parser != null) this.manager.release(parser);
}
]]></xsp:logic></invalidationresult>
</xsp:page>
POST /dbprism/x-dbprism-cache-invalidate HTTP/1.0
Authorization: BASIC YWRtaW5pc3RyYXRvcjoxMjM=
Content-Length: 439
<?xml version="1.0"?>
<invalidation>
<url exp="/cms/CMSj.content" prefix="no">
<validity level="0"/>
<sitemap-argument name="source" value="/Home.xml"/>
<sitemap-argument name="printable" value="no"/>
</url>
<url exp="/cms/CMSj.header" prefix="no">
<validity level="0"/>
<sitemap-argument name="source" value="/Home.xml"/>
<sitemap-argument name="printable" value="no"/>
</url>
</invalidation>
-- Purpose: Demo Invalidation Procedure
-- This file is part of DB Prism External Cache invalidator samples directory
-- Name: cms_invalidate_proc
--
-- Usage:
-- SQL> set serveroutput on (When debugging to see dbms_output.put_line's)
-- SQL> exec cms_invalidate_proc('cocoonhost',8888,'/Home.xml');
--
-- Next: 1. Needs Base64 password routine and params passed.
-- By the default include administrator/123 as username/password
-- 2. Needs yes/no param..for prefix=yes|no but changes content-length
--
create or replace procedure cms_invalidate_proc (
machine in varchar2,
port in integer,
uri in varchar2 ) is
d integer;
c utl_tcp.connection; -- TCP/IP connection to the Web server
DQUOTE constant varchar2(1) := chr(34);
CR constant varchar2(1) := chr(13);
content_length integer;
invalidate_msg varchar2(4000);
BEGIN
-- Note: The 421 + Length of uri to invalidate * 2 = Content-Length
content_length := LENGTH(uri)*2 + 421;
-- open connection
c := utl_tcp.open_connection(machine, port);
-- Send the HTP Protocol Header
-- send HTTP POST for DB Prism invalidator page
-- requires an pipeline setting like this
-- <map:match pattern="x-dbprism-cache-invalidate">
-- <map:generate type="serverpages" src="invalidate.xsp"/>
-- <map:serialize/>
-- </map:match>
d := utl_tcp.write_line(c, 'POST /dbprism/x-dbprism-cache-invalidate HTTP/1.0');
-- Note: The Authorization passes the User:Password as a base64 encoded
-- string. ie. administrator:123 =>
d := utl_tcp.write_line(c, 'Authorization: BASIC YWRtaW5pc3RyYXRvcjoxMjM=');
d := utl_tcp.write_line(c, 'Content-Length: ' || content_length);
dbms_output.put_line('Content-Length: ' || content_length);
-- send TWO CR's per HTTP Protocol (Note: One from above and the other sent by
write_line)
-- (Note: If testing with telnet count cr as 2 characters)
d := utl_tcp.write_line(c, '' );
-- send DBPrism xml Invalidation message
d := utl_tcp.write_line(c, '<?xml version=' || DQUOTE || '1.0' || DQUOTE || '?>');
d := utl_tcp.write_line(c, '<invalidation>');
d := utl_tcp.write_line(c, ' <url exp=' || DQUOTE || '/cms/CMSj.content' || DQUOTE
|| ' prefix=' || DQUOTE || 'no' || DQUOTE || '>');
d := utl_tcp.write_line(c, ' <validity level=' || DQUOTE || '0' || DQUOTE ||
'/>');
d := utl_tcp.write_line(c, ' <sitemap-argument name=' || DQUOTE || 'source' ||
DQUOTE || ' value=' || DQUOTE || uri || DQUOTE || '/>');
d := utl_tcp.write_line(c, ' <sitemap-argument name=' || DQUOTE || 'printable'
|| DQUOTE || ' value=' || DQUOTE || 'no' || DQUOTE || '/>');
d := utl_tcp.write_line(c, ' </url>');
d := utl_tcp.write_line(c, ' <url exp=' || DQUOTE || '/cms/CMSj.header' || DQUOTE
|| ' prefix=' || DQUOTE || 'no' || DQUOTE || '>');
d := utl_tcp.write_line(c, ' <validity level=' || DQUOTE || '0' || DQUOTE ||
'/>');
d := utl_tcp.write_line(c, ' <sitemap-argument name=' || DQUOTE || 'source' ||
DQUOTE || ' value=' || DQUOTE || uri || DQUOTE || '/>');
d := utl_tcp.write_line(c, ' <sitemap-argument name=' || DQUOTE || 'printable'
|| DQUOTE || ' value=' || DQUOTE || 'no' || DQUOTE || '/>');
d := utl_tcp.write_line(c, ' </url>');
d := utl_tcp.write_line(c, '</invalidation>');
utl_tcp.close_connection(c);
END;
/
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, email: [EMAIL PROTECTED]