ProcessFilter.java
Author: Benoit Mahe

    // ProcessFilter.java
    // $Id: ProcessFilter.html,v 1.2 1999/10/27 22:10:36 ylafon Exp $
    // (c) COPYRIGHT MIT and INRIA, 1996.
    // Please first read the full copyright statement in file COPYRIGHT.html

    package org.w3c.jigsaw.filters ;

    import java.io.* ;
    import java.util.*;

    import org.w3c.tools.resources.*;
    import org.w3c.www.http.*;
    import org.w3c.jigsaw.http.* ;
    import org.w3c.jigsaw.resources.*;

    class ProcessFeeder extends Thread {
        Process      proc    = null ;
        OutputStream out     = null ;
        InputStream  in      = null ;
        int          count = -1 ;

        public void run () {
    	try {
    	    byte buffer[] = new byte[4096] ;
    	    int  got      = -1 ;
    	    int emitted   = 0;

    	    // Send the data to the target process:
    	    if ( count >= 0 ) {
    		while ( (count > 0) && ((got = in.read(buffer)) > 0) ) {
    		    out.write (buffer, 0, got) ;
    		    count   -= got ;
    		    emitted += got;
    		}
    	    } else {
    		while ( (got = in.read(buffer)) > 0 ) {
    		    out.write (buffer, 0, got) ;
    		    emitted += got;
    		}
    	    }
    	    // Clean up the process:
    	    out.flush() ;
    	    out.close() ;
    	    proc.waitFor() ;
    	} catch (Exception e) {
    	    System.out.println ("ProcessFeeder: caught exception !") ;
    	    e.printStackTrace() ;
    	}
        }

        ProcessFeeder (Process proc, InputStream in) {
    	this (proc, in, -1) ;
        }

        ProcessFeeder (Process proc, InputStream in, int count) {
    	this.proc   = proc ;
    	this.out    = proc.getOutputStream() ;
    	this.in     = in ;
    	this.count  = count ;
        }
    }

    /**
     * This filter process a normal entity body through any process.
     * <p>Although, you would probably include the filtered resource inside a
     * NegotiatedResource to make sure the target browser accepts this
     * content-encoding.
     */

    public class ProcessFilter extends ResourceFilter {
        /**
         * Attribute index - The command we should pass the stream through.
         */
        protected static int ATTR_COMMAND = -1 ;

        static {
    	Attribute a   = null ;
    	Class     cls = null ;
    	try {
    	    cls = Class.forName("org.w3c.jigsaw.filters.ProcessFilter") ;
    	} catch (Exception ex) {
    	    ex.printStackTrace() ;
    	    System.exit(1) ;
    	}
    	// The command attribute
    	a = new StringArrayAttribute("command"
    				     , null
    				     , Attribute.EDITABLE|Attribute.MANDATORY);
    	ATTR_COMMAND = AttributeRegistry.registerAttribute(cls, a) ;
        }

        /**
         * A pointer to our runtime object.
         */
        protected Runtime runtime  = null ;

        /**
         * Get the command we should process the reply stream through.
         */

        public String[] getCommand() {
    	return (String[]) getValue(ATTR_COMMAND, null) ;
        }

        /**
         * Process the request output through the provided process filter.
         */

        public ReplyInterface outgoingFilter (RequestInterface req,
    					  ReplyInterface rep)
    	throws ProtocolException
        {
    	Request request = (Request) req;
    	Reply   reply   = (Reply) rep;
    	Process       process   = null ;
    	ProcessFeeder feeder    = null ;
    	String        command[] = getCommand() ;

    	// Some sanity checks:
    	if (reply.getStatus() != HTTP.OK)
    	    return null;
    	InputStream in = reply.openStream() ;
    	if ( in == null )
    	    return null;
    	if ( command == null ) {
    	    Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;
    	    error.setContent("The process filter of this resource is not "
    			     + " configured properly (it has no command).");
    	    throw new HTTPException(error);
    	}
    	// Okay, run the reply stream through the process:
    	try {
    	    process = runtime.exec (command) ;
    	} catch (IOException e) {
    	    Reply error = request.makeReply(HTTP.NOT_FOUND) ;
    	    error.setContent("The filter's process command "+
    			     command[0]+
    			     " wasn't found: "+e.getMessage()) ;
    	    throw new HTTPException (error);
    	}
    	if ( reply.hasContentLength() ) {
    	    feeder = new ProcessFeeder (process
    					, in
    					, reply.getContentLength()) ;
    	} else {
    	    feeder = new ProcessFeeder (process, in) ;
    	}
    	feeder.start() ;
    	reply.setContentLength(-1) ;
    	reply.setStream(process.getInputStream());
    	return reply ;
        }

        /**
         * Initialize a process filter.
         * Just get a pointer to the runtime object.
         * @param values The default attribute values.
         */

        public void initialize(Object values[]) {
    	super.initialize(values);
    	this.runtime = Runtime.getRuntime();
        }

    }