You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@locus.apache.org on 2000/01/14 20:48:24 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/util InvocationHelper.java XmlHelper.java

costin      00/01/14 11:48:24

  Modified:    src/share/org/apache/tomcat/core Context.java
                        ContextManager.java RequestImpl.java
                        ServletWrapper.java
               src/share/org/apache/tomcat/request MapperInterceptor.java
               src/share/org/apache/tomcat/service/http HttpAdapter.java
               src/share/org/apache/tomcat/servlets DefaultServlet.java
               src/share/org/apache/tomcat/util InvocationHelper.java
                        XmlHelper.java
  Log:
  - Started reverse eng. MapperInterceptor,
  - started to remove dependencies on Startup and HttpServer
  - added few log messages to be able to trace ( can be removed for a "stable" release )
  
  - added few more "special cases" to XMLHelper. ( i.e. in addition to the original ant
  features - i.e. create and configure objects based on tag/attribute, it can deal
  with a hierarchy of objects created based on the xml document.)
  
  Revision  Changes    Path
  1.32      +31 -7     jakarta-tomcat/src/share/org/apache/tomcat/core/Context.java
  
  Index: Context.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/Context.java,v
  retrieving revision 1.31
  retrieving revision 1.32
  diff -u -r1.31 -r1.32
  --- Context.java	2000/01/14 04:08:10	1.31
  +++ Context.java	2000/01/14 19:48:22	1.32
  @@ -293,22 +293,28 @@
   
       /** Implements getResource() - use a sub-request to let interceptors do the job.
        */
  -    public URL getResource(String path)	throws MalformedURLException {
  +    public URL getResource(String rpath)	throws MalformedURLException {
           URL url = null;
   
  -	if ("".equals(path)) 
  +	if ("".equals(rpath)) 
   	    return getDocumentBase();
   	
   	// deal with exceptional cases
  -        if (path == null) 
  +        if (rpath == null) 
               throw new MalformedURLException(sm.getString("scfacade.getresource.npe"));
  -        else if ( ! path.startsWith("/")) {
  -	    throw new MalformedURLException(sm.getString("scfacade.getresource.iae", path));
  +        else if ( ! rpath.startsWith("/")) {
  +	    // XXX fix - it shouldn't be a special case, MapperInterceptor
  +	    // should deal with this ( workaround for bug in MapperInterceptor)
  +	    //	    System.out.println("rpath=" + rpath + " " + path);
  +	    if( "/".equals(path) ) // default context
  +		rpath="/" + rpath;
  +	    else
  +		throw new MalformedURLException(sm.getString("scfacade.getresource.iae", rpath));
   	}
   
   	// Create a Sub-Request, do the request processing stage
   	// that will take care of aliasing and set the paths
  -	Request lr=contextM.createRequest( this, path );
  +	Request lr=contextM.createRequest( this, rpath );
   	getContextManager().processRequest(lr);
   
   	return getResourceURL( lr );
  @@ -525,7 +531,7 @@
   	new WorkDirInterceptor().handleContextInit( this );
   
   	// XXX who uses servletBase ???
  -	URL servletBase = this.documentBase;
  +	URL servletBase = getDocumentBase();
           this.setServletBase(servletBase);
   
   	// expand WAR
  @@ -621,10 +627,19 @@
       }
       
       public URL getDocumentBase() {
  +	if( documentBase == null ) {
  +	    if( docBase != null)
  +		try {
  +		    documentBase=URLUtil.resolve( docBase );
  +		} catch( MalformedURLException ex ) {
  +		    ex.printStackTrace();
  +		}
  +	}
           return documentBase;
       }
   
       public void setDocumentBase(URL s) {
  +	// Used only by startup, will be removed
           this.documentBase=s;
       }
   
  @@ -851,6 +866,11 @@
       }
   
       public ServletWrapper getDefaultServlet() {
  +	if( defaultServlet==null)
  +	    defaultServlet=getServletByName(Constants.DEFAULT_SERVLET_NAME );
  +	// XXX works only if we do load default web.xml first - we should
  +	// be able to work without that trick ( i.e. define a "default" )
  +	
   	return defaultServlet;
       }
       
  @@ -1069,5 +1089,9 @@
           }
   
           return cp;
  +    }
  +
  +    public String toString() {
  +	return "Context( " + path + " , " + getDocumentBase() + " ) ";
       }
   }
  
  
  
  1.18      +110 -15   jakarta-tomcat/src/share/org/apache/tomcat/core/ContextManager.java
  
  Index: ContextManager.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/ContextManager.java,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -r1.17 -r1.18
  --- ContextManager.java	2000/01/13 23:28:27	1.17
  +++ ContextManager.java	2000/01/14 19:48:22	1.18
  @@ -86,13 +86,11 @@
        */
       private StringManager sm =StringManager.getManager("org.apache.tomcat.core");
   
  +    int debug=0;
  +    
       private Vector requestInterceptors = new Vector();
       private Vector contextLifecycleInterceptors = new Vector();
       
  -    ContextMapperInterceptor contextInterceptor=new ContextMapperInterceptor( this );
  -    SessionInterceptor sessionInterceptor=new SessionInterceptor();
  -    MapperInterceptor mapperInterceptor=new MapperInterceptor();
  -    
       /**
        * The set of Contexts associated with this ContextManager,
        * keyed by context paths.
  @@ -116,7 +114,10 @@
       int port;
   
       String workDir;
  +    
  +    Vector connectors=new Vector();
   
  +    
       /**
        * Construct a new ContextManager instance with default values.
        */
  @@ -131,6 +132,58 @@
           return contexts.keys();
       }
   
  +    public void start() throws Exception {
  +
  +	// set a default connector ( http ) if none defined yet
  +	if(connectors.size()==0) {
  +	    // Make the default customizable!
  +	    addServerConnector(  new org.apache.tomcat.service.http.HttpAdapter() );
  +	}
  +	
  +	for( int i=0; i<connectors.size(); i++ ) {
  +	    ((ServerConnector)connectors.elementAt(i)).setContextManager( this );
  +	    ((ServerConnector)connectors.elementAt(i)).start();
  +	}
  +	
  +	// check for default context 
  +	Context defaultContext=getContext("/");
  +	if (defaultContext == null ||
  +	    defaultContext.getDocumentBase() == null) {
  +	    // XXX find a better exception 
  +	    throw new IllegalArgumentException("No default context " + defaultContext);
  +	}
  +
  +	if( requestInterceptors.size() == 0 ) {
  +	    // nothing set up by starter, add default ones
  +	    addRequestInterceptor(new ContextMapperInterceptor( this ));
  +	    addRequestInterceptor(new SessionInterceptor());
  +	    addRequestInterceptor(new MapperInterceptor());
  +	}
  +	
  +	// init contexts
  +	Enumeration enum = getContextNames();
  +	while (enum.hasMoreElements()) {
  +            Context context = getContext((String)enum.nextElement());
  +            context.init();
  +	}
  +    }
  +
  +    public void stop() throws Exception {
  +	for (int i=0; i<connectors.size(); i++) {
  +	    ((ServerConnector)connectors.elementAt(i)).stop();
  +	}
  +
  +	Enumeration enum = getContextNames();
  +	while (enum.hasMoreElements()) {
  +	    Context context =
  +	        getContext((String)enum.nextElement());
  +	    
  +	    System.out.println("Taking down context: " +
  +			       context.getPath());
  +	    
  +	    context.shutdown();
  +	}
  +    }
   
       /**
        * Gets a context by it's name, or <code>null</code> if there is
  @@ -154,7 +207,9 @@
   
   	// it will replace existing context - it's better than 
   	// IllegalStateException.
  -	contexts.put( ctx.getPath(), ctx );
  +	String path=ctx.getPath();
  +	// Log	System.out.println(this + " adding " + ctx + " " + ctx.getPath() + " " +  ctx.getDocBase());
  +	contexts.put( path, ctx );
       }
       
       /**
  @@ -175,6 +230,22 @@
   	}
       }
   
  +
  +    // -------------------- Connectors and Interceptors --------------------
  +
  +    /**
  +     * Add the specified server connector to the those attached to this server.
  +     *
  +     * @param con The new server connector
  +     */
  +    public synchronized void addServerConnector( ServerConnector con ) {
  +	connectors.addElement( con );
  +    }
  +
  +    public void addRequestInterceptor( RequestInterceptor ri ) {
  +	requestInterceptors.addElement( ri );
  +    }
  +    
       // -------------------- Defaults for all contexts --------------------
       
       /**
  @@ -254,9 +325,15 @@
   
   	    processRequest( rrequest );
   
  -	    // do it
  -	    rrequest.getWrapper().handleRequest(rrequest.getFacade(),
  -					       rresponse.getFacade());
  +	    if( rrequest.getWrapper() == null ) {
  +		System.out.println("ERROR: mapper returned no wrapper ");
  +		System.out.println(rrequest );
  +		// XXX send an error - it shouldn't happen, mapper is broken
  +	    } else {
  +		// do it
  +		rrequest.getWrapper().handleRequest(rrequest.getFacade(),
  +						    rresponse.getFacade());
  +	    }
   	    
   	    // finish and clean up
   	    rresponse.finish();
  @@ -279,16 +356,26 @@
        *  is already known.
        */
       int processRequest( Request req ) {
  -	// will set the Context
  -	contextInterceptor.handleRequest( req );
  -	// will set Session 
  -	sessionInterceptor.handleRequest( req );
  -	return mapperInterceptor.handleRequest( req );
  +
  +	if(debug>0) log( "ProcessRequest: ");
  +	if(debug>0) log( req.toString() );
  +	if(debug>0) log("");
  +
  +	for( int i=0; i< requestInterceptors.size(); i++ ) {
  +	    ((RequestInterceptor)requestInterceptors.elementAt(i)).handleRequest( req );
  +	}
  +
  +	if(debug>0) log("After processing: ");
  +	if(debug>0) log( req.toString() );
  +	if(debug>0) log("");
  +	return 0;
       }
   
  +    // XXX XXX hack - we need to create a new request !
  +    ContextMapperInterceptor contextInterceptor=new ContextMapperInterceptor( this );
       public Context getContextByPath(String path ) {
   	// XXX XXX XXX need to create a sub-request !!!!
  -	// 
  +	//
   	return contextInterceptor.getContextByPath( path );      
       }
   
  @@ -366,6 +453,14 @@
   	
       }
   
  -    
  +    // Debug ( to be replaced with the real thing )
  +    public void setDebug( int level ) {
  +	debug=level;
  +    }
  +
  +    void log( String msg ) {
  +	System.out.println("CM: " + msg );
  +    }
  +
       
   }
  
  
  
  1.6       +14 -3     jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java
  
  Index: RequestImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- RequestImpl.java	2000/01/13 18:20:32	1.5
  +++ RequestImpl.java	2000/01/14 19:48:22	1.6
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java,v 1.5 2000/01/13 18:20:32 costin Exp $
  - * $Revision: 1.5 $
  - * $Date: 2000/01/13 18:20:32 $
  + * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java,v 1.6 2000/01/14 19:48:22 costin Exp $
  + * $Revision: 1.6 $
  + * $Date: 2000/01/14 19:48:22 $
    *
    * ====================================================================
    *
  @@ -716,5 +716,16 @@
   	didCookies = false;
   	if( reqA!=null) reqA.recycle();// XXX avoid double recycle
   	//	moreRequests = false;
  +    }
  +
  +    public String toString() {
  +	StringBuffer sb=new StringBuffer();
  +	sb.append( "Request( " + context );
  +	sb.append( ",URI:" + getRequestURI()  ).append("\n");
  +	sb.append( "    SP:" + getServletPath() );
  +	sb.append( ",PI:" + getPathInfo() );
  +	sb.append( ",LP:" + getLookupPath() );
  +	sb.append( "," + getWrapper() +") ");
  +	return sb.toString();
       }
   }
  
  
  
  1.14      +9 -4      jakarta-tomcat/src/share/org/apache/tomcat/core/ServletWrapper.java
  
  Index: ServletWrapper.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/ServletWrapper.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- ServletWrapper.java	2000/01/14 04:08:11	1.13
  +++ ServletWrapper.java	2000/01/14 19:48:22	1.14
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/ServletWrapper.java,v 1.13 2000/01/14 04:08:11 costin Exp $
  - * $Revision: 1.13 $
  - * $Date: 2000/01/14 04:08:11 $
  + * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/ServletWrapper.java,v 1.14 2000/01/14 19:48:22 costin Exp $
  + * $Revision: 1.14 $
  + * $Date: 2000/01/14 19:48:22 $
    *
    * ====================================================================
    *
  @@ -429,6 +429,11 @@
   	    } catch (InterruptedException e) { }
   	}
       }
  -    
  +
  +    public String toString() {
  +	String toS="Wrapper( " + servletClassName + ",";
  +	if( servlet!=null ) toS=toS+ servlet.getClass().getName();
  +	return toS + ")";
  +    }
   
   }
  
  
  
  1.7       +32 -15    jakarta-tomcat/src/share/org/apache/tomcat/request/MapperInterceptor.java
  
  Index: MapperInterceptor.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/request/MapperInterceptor.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- MapperInterceptor.java	2000/01/14 04:08:11	1.6
  +++ MapperInterceptor.java	2000/01/14 19:48:23	1.7
  @@ -77,11 +77,18 @@
    *
    */
   public class MapperInterceptor  implements  RequestInterceptor {
  -
  +    int debug=0;
  +    
       public MapperInterceptor() {
       }
  +
  +    public void setDebug( int level ) {
  +	debug=level;
  +    }
   
  -    // no configuration 
  +    void log( String msg ) {
  +	System.out.println("Mapper: " + msg );
  +    }
       
       public int handleRequest(Request req) {
   	Context context=req.getContext();
  @@ -92,31 +99,32 @@
   
   	if (wrapper == null) {
   	    wrapper = context.getDefaultServlet();
  -	    if (wrapper == null) {
  -	        wrapper = context.getServletByName(Constants.DEFAULT_SERVLET_NAME );
  -	    }
   
   	    req.setWrapper( wrapper );
   	    req.setServletPath( "" );
   	    req.setPathInfo( path);
  +	    if(debug>0) log("Found wrapper using getMatch " + req);
   	} else {
   	    getMapPath(wrapper, req);
   	    String resolvedServlet = getResolvedServlet(context, req.getMappedPath());
   	    
   	    req.setWrapper( wrapper );
   	    req.setResolvedServlet( resolvedServlet );
  +	    if(debug>0) log("Found wrapper using getMapPath " + req);
   	}
  -	
  -	if (req.getResolvedServlet() != null) {
  -	    req.setAttribute(Constants.Attribute.RESOLVED_SERVLET,
  -				 req.getResolvedServlet());
  -	} else if (req.getMappedPath() != null) {
  -	    req.setAttribute(Constants.Attribute.RESOLVED_SERVLET,
  -				 req.getMappedPath());
  -	} else {
  -	    req.removeAttribute(Constants.Attribute.RESOLVED_SERVLET);
  -	}
   
  +	// XXX Nobody seems to use it, remove if nothing comes out,
  +	// Document why it's here if we find a user 
  +	// 	if (req.getResolvedServlet() != null) {
  +	// 	    req.setAttribute(Constants.Attribute.RESOLVED_SERVLET,
  +	// 				 req.getResolvedServlet());
  +	// 	} else if (req.getMappedPath() != null) {
  +	// 	    req.setAttribute(Constants.Attribute.RESOLVED_SERVLET,
  +	// 				 req.getMappedPath());
  +	// 	} else {
  +	// 	    req.removeAttribute(Constants.Attribute.RESOLVED_SERVLET);
  +	// 	}
  +
   	return OK;
       }
   
  @@ -276,6 +284,15 @@
           return wrapper;
       }
   
  +    /** Called when a wrapper is found.
  +	It will change "mappedPath" ( ?? ):
  +
  +	- if servletPath==/servlet -> mapPath=everything after first /
  +	   component of pathInfo ( if pathInfo!=null) 
  +	- else mapPath = resourceName (??) ( if !=null)
  +	- else unchanged
  +	
  +     */
       private void getMapPath(ServletWrapper wrapper, Request req) {
           String mapPath = req.getMappedPath();
   
  
  
  
  1.3       +8 -4      jakarta-tomcat/src/share/org/apache/tomcat/service/http/HttpAdapter.java
  
  Index: HttpAdapter.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/service/http/HttpAdapter.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- HttpAdapter.java	1999/11/12 23:24:24	1.2
  +++ HttpAdapter.java	2000/01/14 19:48:23	1.3
  @@ -1,9 +1,9 @@
   
   
   /*
  - * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/service/http/HttpAdapter.java,v 1.2 1999/11/12 23:24:24 costin Exp $
  - * $Revision: 1.2 $
  - * $Date: 1999/11/12 23:24:24 $
  + * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/service/http/HttpAdapter.java,v 1.3 2000/01/14 19:48:23 costin Exp $
  + * $Revision: 1.3 $
  + * $Date: 2000/01/14 19:48:23 $
    *
    * ====================================================================
    *
  @@ -118,7 +118,11 @@
   	this.cm=ctx;
   	con.setContextManager( ctx );
       }
  -    
  +
  +    public void setPort( String s ) {
  +	vport=string2Int( s );
  +    }
  +
       public void setProperty( String prop, String value) {
   	if(HttpServer.VHOST_PORT.equals(prop) ) {
   	    //	    System.out.println("XXX");
  
  
  
  1.3       +1 -0      jakarta-tomcat/src/share/org/apache/tomcat/servlets/DefaultServlet.java
  
  Index: DefaultServlet.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/servlets/DefaultServlet.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- DefaultServlet.java	2000/01/12 06:35:20	1.2
  +++ DefaultServlet.java	2000/01/14 19:48:24	1.3
  @@ -113,6 +113,7 @@
   	// It should use getMappedPath instead !!!
           URL url = getServletContext().getResource(pathInfo);
   
  +	// 	System.out.println("Resource: " + url + " PI: " + pathInfo);
   	if (url != null) {
   	    if (url.getProtocol().equals("war") &&
   	        context.isWARExpanded()) {
  
  
  
  1.2       +20 -0     jakarta-tomcat/src/share/org/apache/tomcat/util/InvocationHelper.java
  
  Index: InvocationHelper.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/InvocationHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- InvocationHelper.java	2000/01/14 05:17:36	1.1
  +++ InvocationHelper.java	2000/01/14 19:48:24	1.2
  @@ -133,6 +133,7 @@
        */
       public static void addAttribute( Object o, String name, Object v ) {
   	try {
  +	    // Find addXXX method
   	    Method setMethod = getMethod(o, "add" + capitalize( name ));
   	    //	    System.out.println("ADD: " + name + " " + o.getClass() + " " + v.getClass());
   	    if( setMethod!= null ) {
  @@ -144,6 +145,25 @@
   		    return;
   		}
   	    }
  +
  +	    // Try to find addYYY - where YYY is an interface supported by v
  +	    Class interF[]=v.getClass().getInterfaces();
  +	    for( int i=0; i<interF.length; i++ ) {
  +		String iName=interF[i].getName();
  +		// XXX deal with default package
  +		String lastComp = iName.substring( iName.lastIndexOf(".") );
  +		setMethod = getMethod( o, "add" + lastComp );
  +		if( setMethod != null ) {
  +		    System.out.println("Found add" + lastComp + " for " +
  +				       o.getClass().getName() + " " + v.getClass().getName());
  +		    Class[] ma =setMethod.getParameterTypes();
  +		    if ( (ma.length == 1) && (! ma[0].getName().equals("java.lang.String"))) {
  +			setMethod.invoke(o, new Object[] {v});
  +			return;
  +		    }
  +		}
  +	    }
  +	    
   	    
   	    // fallback to setAttribute
   	    setAttribute( o, name, v);
  
  
  
  1.2       +75 -46    jakarta-tomcat/src/share/org/apache/tomcat/util/XmlHelper.java
  
  Index: XmlHelper.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/XmlHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- XmlHelper.java	2000/01/14 05:20:21	1.1
  +++ XmlHelper.java	2000/01/14 19:48:24	1.2
  @@ -90,7 +90,8 @@
       String tagStack[];
       int sp;
       Properties props;
  -
  +    int debug=0;
  +    
       Hashtable tagMapper;
       
       public XmlHelperHandler(Hashtable mapper, Properties props) {
  @@ -133,57 +134,76 @@
   	throws SAXException
       {
   	try {
  -	    //	System.out.println("Start " + tag + " " + attributes + " " + sp);
  +	    if( debug>0) log("Start " + tag + " " + attributes + " " + sp);
   
  -	// Special case: property
  -	if( "property".equals( tag ) ) {
  -	    String n=attributes.getValue("name");
  -	    String v=attributes.getValue("value");
  -	    if(n==null || v==null)
  -		System.out.println("Error: property with null name/value");
  -	    Object elem=elemStack[sp-1];
  -	    //  System.out.println("Setting " + elem.getClass()+ " "  + n + "=" + v );
  -	    InvocationHelper.setProperty( elem, n, processValue(v) );
  -	    return;
  -	}
  -	
  -	Object elem=tagMapper.get( tag );
  +	    // Special case: <foo><property name="a" value="b" /></foo>
  +	    // Will call foo.setA( "b" ) ( or foo.setProperty("a", "b") if setA not found )
  +	    if( "property".equals( tag ) ) {
  +		String n=attributes.getValue("name");
  +		String v=attributes.getValue("value");
  +		if(n==null || v==null)
  +		    System.out.println("Error: property with null name/value");
  +		Object elem=elemStack[sp-1];
  +		if(debug>1) log("Setting " + elem.getClass()+ " "  + n + "=" + v );
  +		
  +		InvocationHelper.setProperty( elem, n, processValue(v) );
  +		return;
  +	    }
  +
  +	    // Normal tag <foo> - mapped to org.bar.Foo, will construct a new Foo.
  +	    Object elem=tagMapper.get( tag );
   
  -	if(sp==0) {
  -	    // set all "default" properties
  -	    Enumeration e = props.keys();
  -	    while (e.hasMoreElements()) {
  -		String arg = (String)e.nextElement();
  -		String value = (String)props.get(arg);
  -		InvocationHelper.setProperty(elem, arg, value);
  +	    // For the root element, add the properties passed to XmlHelper
  +	    // ( from the command line )
  +	    if(sp==0) {
  +		// set all "default" properties
  +		Enumeration e = props.keys();
  +		while (e.hasMoreElements()) {
  +		    String arg = (String)e.nextElement();
  +		    String value = (String)props.get(arg);
  +		    InvocationHelper.setProperty(elem, arg, value);
  +		}
   	    }
  -	    
  -	}
  -	
  -	if( sp > 0 ) {
  -	    // we're not the root
  -	    InvocationHelper.setAttribute( elem, tagStack[sp-1], elemStack[sp-1] );
  -	}
  -	
  -	if(attributes!=null) {
  -	    for (int i = 0; i < attributes.getLength (); i++) {
  -		String type = attributes.getType (i);
  -		String name=attributes.getName(i);
  -		String value=attributes.getValue(i);
  -		//System.out.println("Attribute " + i + " type= " + type + " name=" + name + " value=" + value);
   
  -		InvocationHelper.setProperty( elem, name, processValue(value) );
  +	    /* Set parent in child:
  +	       For <foo><bar/></foo>,
  +	       will try: bar.setFoo( Foo )
  +	       then: foo.setAttribute( "foo", Foo);
  +	    */
  +	    if( sp > 0 ) {
  +		// we're not the root
  +		// tell our parent about us
  +		InvocationHelper.setAttribute( elem, tagStack[sp-1], elemStack[sp-1] );
   	    }
  -	}
   
  -	if( sp > 0 ) {
  -	    InvocationHelper.addAttribute( elemStack[sp-1], tag, elem );
  -	}
  +	    /* <foo a="b" />
  +	       Normal attribute setting, with setA( "b" ), or setProperty("a", "b") if not
  +	       setA found
  +	     */
  +	    if(attributes!=null) {
  +		for (int i = 0; i < attributes.getLength (); i++) {
  +		    String type = attributes.getType (i);
  +		    String name=attributes.getName(i);
  +		    String value=attributes.getValue(i);
  +		    //System.out.println("Attribute " + i + " type= " + type + " name=" + name + " value=" + value);
  +		    InvocationHelper.setProperty( elem, name, processValue(value) );
  +		}
  +	    }
  +
  +	    /*
  +	      Set parent in sun, will try:
  +	      foo.setBar( Bar )
  +	      foo.addBar( Bar )
  +	      foo.setAttribute( "bar", Bar)
  +	    */
  +	    if( sp > 0 ) {
  +		InvocationHelper.addAttribute( elemStack[sp-1], tag, elem );
  +	    }
   	
  -	elemStack[sp]=elem;
  -	tagStack[sp]=tag;
  -	sp++;
  -	//	System.out.println("Start " + tag + " " + attributes + " " + sp);
  +	    elemStack[sp]=elem;
  +	    tagStack[sp]=tag;
  +	    sp++;
  +	    //	System.out.println("Start " + tag + " " + attributes + " " + sp);
   	} catch (Exception ex) {
   	    ex.printStackTrace();
   	}
  @@ -224,6 +244,15 @@
       public Object getRootElement() {
   	return elemStack[0];
       }
  -	
  +
  +    // Debug ( to be replaced with the real thing )
  +    public void setDebug( int level ) {
  +	debug=level;
  +    }
  +
  +    void log( String msg ) {
  +	System.out.println("XMLH: " + msg );
  +    }
  +
   }