|
Tomcat example source code file (Parameters.java)
The Tomcat Parameters.java source code/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.tomcat.util.http; import java.io.IOException; import java.util.Enumeration; import java.util.Hashtable; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.buf.CharChunk; import org.apache.tomcat.util.buf.MessageBytes; import org.apache.tomcat.util.buf.UDecoder; import org.apache.tomcat.util.collections.MultiMap; /** * * @author Costin Manolache */ public final class Parameters extends MultiMap { private static org.apache.juli.logging.Log log= org.apache.juli.logging.LogFactory.getLog(Parameters.class ); // Transition: we'll use the same Hashtable( String->String[] ) // for the beginning. When we are sure all accesses happen through // this class - we can switch to MultiMap private Hashtable<String,String[]> paramHashStringArray = new Hashtable<String,String[]>(); private boolean didQueryParameters=false; private boolean didMerge=false; MessageBytes queryMB; MimeHeaders headers; UDecoder urlDec; MessageBytes decodedQuery=MessageBytes.newInstance(); public static final int INITIAL_SIZE=4; // Garbage-less parameter merging. // In a sub-request with parameters, the new parameters // will be stored in child. When a getParameter happens, // the 2 are merged togheter. The child will be altered // to contain the merged values - the parent is allways the // original request. private Parameters child=null; private Parameters parent=null; private Parameters currentChild=null; String encoding=null; String queryStringEncoding=null; /** * */ public Parameters() { super( INITIAL_SIZE ); } public void setQuery( MessageBytes queryMB ) { this.queryMB=queryMB; } public void setHeaders( MimeHeaders headers ) { this.headers=headers; } public void setEncoding( String s ) { encoding=s; if(debug>0) log( "Set encoding to " + s ); } public void setQueryStringEncoding( String s ) { queryStringEncoding=s; if(debug>0) log( "Set query string encoding to " + s ); } public void recycle() { super.recycle(); paramHashStringArray.clear(); didQueryParameters=false; currentChild=null; didMerge=false; encoding=null; decodedQuery.recycle(); } // -------------------- Sub-request support -------------------- public Parameters getCurrentSet() { if( currentChild==null ) return this; return currentChild; } /** Create ( or reuse ) a child that will be used during a sub-request. All future changes ( setting query string, adding parameters ) will affect the child ( the parent request is never changed ). Both setters and getters will return the data from the deepest child, merged with data from parents. */ public void push() { // We maintain a linked list, that will grow to the size of the // longest include chain. // The list has 2 points of interest: // - request.parameters() is the original request and head, // - request.parameters().currentChild() is the current set. // The ->child and parent<- links are preserved ( currentChild is not // the last in the list ) // create a new element in the linked list // note that we reuse the child, if any - pop will not // set child to null ! if( currentChild==null ) { currentChild=new Parameters(); currentChild.setURLDecoder( urlDec ); currentChild.parent=this; return; } if( currentChild.child==null ) { currentChild.child=new Parameters(); currentChild.setURLDecoder( urlDec ); currentChild.child.parent=currentChild; } // it is not null if this object already had a child // i.e. a deeper include() ( we keep it ) // the head will be the new element. currentChild=currentChild.child; currentChild.setEncoding( encoding ); } /** Discard the last child. This happens when we return from a sub-request and the parameters are locally modified. */ public void pop() { if( currentChild==null ) { throw new RuntimeException( "Attempt to pop without a push" ); } currentChild.recycle(); currentChild=currentChild.parent; // don't remove the top. } // -------------------- Data access -------------------- // Access to the current name/values, no side effect ( processing ). // You must explicitely call handleQueryParameters and the post methods. // This is the original data representation ( hash of String->String[]) public void addParameterValues( String key, String[] newValues) { if ( key==null ) return; String values[]; if (paramHashStringArray.containsKey(key)) { String oldValues[] = (String[])paramHashStringArray.get(key); values = new String[oldValues.length + newValues.length]; for (int i = 0; i < oldValues.length; i++) { values[i] = oldValues[i]; } for (int i = 0; i < newValues.length; i++) { values[i+ oldValues.length] = newValues[i]; } } else { values = newValues; } paramHashStringArray.put(key, values); } public String[] getParameterValues(String name) { handleQueryParameters(); // sub-request if( currentChild!=null ) { currentChild.merge(); return (String[])currentChild.paramHashStringArray.get(name); } // no "facade" String values[]=(String[])paramHashStringArray.get(name); return values; } public Enumeration getParameterNames() { handleQueryParameters(); // Slow - the original code if( currentChild!=null ) { currentChild.merge(); return currentChild.paramHashStringArray.keys(); } // merge in child return paramHashStringArray.keys(); } /** Combine the parameters from parent with our local ones */ private void merge() { // recursive if( debug > 0 ) { log("Before merging " + this + " " + parent + " " + didMerge ); log( paramsAsString()); } // Local parameters first - they take precedence as in spec. handleQueryParameters(); // we already merged with the parent if( didMerge ) return; // we are the top level if( parent==null ) return; // Add the parent props to the child ( lower precedence ) parent.merge(); Hashtable<String,String[]> parentProps=parent.paramHashStringArray; merge2( paramHashStringArray , parentProps); didMerge=true; if(debug > 0 ) log("After " + paramsAsString()); } // Shortcut. public String getParameter(String name ) { String[] values = getParameterValues(name); if (values != null) { if( values.length==0 ) return ""; return values[0]; } else { return null; } } // -------------------- Processing -------------------- /** Process the query string into parameters */ public void handleQueryParameters() { if( didQueryParameters ) return; didQueryParameters=true; if( queryMB==null || queryMB.isNull() ) return; if( debug > 0 ) log( "Decoding query " + decodedQuery + " " + queryStringEncoding); try { decodedQuery.duplicate( queryMB ); } catch (IOException e) { // Can't happen, as decodedQuery can't overflow e.printStackTrace(); } processParameters( decodedQuery, queryStringEncoding ); } // -------------------- /** Combine 2 hashtables into a new one. * ( two will be added to one ). * Used to combine child parameters ( RequestDispatcher's query ) * with parent parameters ( original query or parent dispatcher ) */ private static void merge2(Hashtable<String,String[]> one, Hashtable<String,String[]> two ) { Enumeration e = two.keys(); while (e.hasMoreElements()) { String name = (String) e.nextElement(); String[] oneValue = one.get(name); String[] twoValue = two.get(name); String[] combinedValue; if (twoValue == null) { continue; } else { if( oneValue==null ) { combinedValue = new String[twoValue.length]; System.arraycopy(twoValue, 0, combinedValue, 0, twoValue.length); } else { combinedValue = new String[oneValue.length + twoValue.length]; System.arraycopy(oneValue, 0, combinedValue, 0, oneValue.length); System.arraycopy(twoValue, 0, combinedValue, oneValue.length, twoValue.length); } one.put(name, combinedValue); } } } // incredibly inefficient data representation for parameters, // until we test the new one private void addParam( String key, String value ) { if( key==null ) return; String values[]; if (paramHashStringArray.containsKey(key)) { String oldValues[] = (String[])paramHashStringArray. get(key); values = new String[oldValues.length + 1]; for (int i = 0; i < oldValues.length; i++) { values[i] = oldValues[i]; } values[oldValues.length] = value; } else { values = new String[1]; values[0] = value; } paramHashStringArray.put(key, values); } public void setURLDecoder( UDecoder u ) { urlDec=u; } // -------------------- Parameter parsing -------------------- // This code is not used right now - it's the optimized version // of the above. // we are called from a single thread - we can do it the hard way // if needed ByteChunk tmpName=new ByteChunk(); ByteChunk tmpValue=new ByteChunk(); CharChunk tmpNameC=new CharChunk(1024); CharChunk tmpValueC=new CharChunk(1024); public void processParameters( byte bytes[], int start, int len ) { processParameters(bytes, start, len, encoding); } public void processParameters( byte bytes[], int start, int len, String enc ) { int end=start+len; int pos=start; if( debug>0 ) log( "Bytes: " + new String( bytes, start, len )); do { boolean noEq=false; int valStart=-1; int valEnd=-1; int nameStart=pos; int nameEnd=ByteChunk.indexOf(bytes, nameStart, end, '=' ); // Workaround for a&b&c encoding int nameEnd2=ByteChunk.indexOf(bytes, nameStart, end, '&' ); if( (nameEnd2!=-1 ) && ( nameEnd==-1 || nameEnd > nameEnd2) ) { nameEnd=nameEnd2; noEq=true; valStart=nameEnd; valEnd=nameEnd; if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + new String(bytes, nameStart, nameEnd-nameStart) ); } if( nameEnd== -1 ) nameEnd=end; if( ! noEq ) { valStart= (nameEnd < end) ? nameEnd+1 : end; valEnd=ByteChunk.indexOf(bytes, valStart, end, '&'); if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart; } pos=valEnd+1; if( nameEnd<=nameStart ) { log.warn("Parameters: Invalid chunk ignored."); continue; // invalid chunk - it's better to ignore } tmpName.setBytes( bytes, nameStart, nameEnd-nameStart ); tmpValue.setBytes( bytes, valStart, valEnd-valStart ); try { addParam( urlDecode(tmpName, enc), urlDecode(tmpValue, enc) ); } catch (IOException e) { // Exception during character decoding: skip parameter log.warn("Parameters: Character decoding failed. " + "Parameter skipped.", e); } tmpName.recycle(); tmpValue.recycle(); } while( pos<end ); } private String urlDecode(ByteChunk bc, String enc) throws IOException { if( urlDec==null ) { urlDec=new UDecoder(); } urlDec.convert(bc); String result = null; if (enc != null) { bc.setEncoding(enc); result = bc.toString(); } else { CharChunk cc = tmpNameC; int length = bc.getLength(); cc.allocate(length, -1); // Default encoding: fast conversion byte[] bbuf = bc.getBuffer(); char[] cbuf = cc.getBuffer(); int start = bc.getStart(); for (int i = 0; i < length; i++) { cbuf[i] = (char) (bbuf[i + start] & 0xff); } cc.setChars(cbuf, 0, length); result = cc.toString(); cc.recycle(); } return result; } public void processParameters( char chars[], int start, int len ) { int end=start+len; int pos=start; if( debug>0 ) log( "Chars: " + new String( chars, start, len )); do { boolean noEq=false; int nameStart=pos; int valStart=-1; int valEnd=-1; int nameEnd=CharChunk.indexOf(chars, nameStart, end, '=' ); int nameEnd2=CharChunk.indexOf(chars, nameStart, end, '&' ); if( (nameEnd2!=-1 ) && ( nameEnd==-1 || nameEnd > nameEnd2) ) { nameEnd=nameEnd2; noEq=true; valStart=nameEnd; valEnd=nameEnd; if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + new String(chars, nameStart, nameEnd-nameStart) ); } if( nameEnd== -1 ) nameEnd=end; if( ! noEq ) { valStart= (nameEnd < end) ? nameEnd+1 : end; valEnd=CharChunk.indexOf(chars, valStart, end, '&'); if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart; } pos=valEnd+1; if( nameEnd<=nameStart ) { continue; // invalid chunk - no name, it's better to ignore // XXX log it ? } try { tmpNameC.append( chars, nameStart, nameEnd-nameStart ); tmpValueC.append( chars, valStart, valEnd-valStart ); if( debug > 0 ) log( tmpNameC + "= " + tmpValueC); if( urlDec==null ) { urlDec=new UDecoder(); } urlDec.convert( tmpNameC ); urlDec.convert( tmpValueC ); if( debug > 0 ) log( tmpNameC + "= " + tmpValueC); addParam( tmpNameC.toString(), tmpValueC.toString() ); } catch( IOException ex ) { ex.printStackTrace(); } tmpNameC.recycle(); tmpValueC.recycle(); } while( pos<end ); } public void processParameters( MessageBytes data ) { processParameters(data, encoding); } public void processParameters( MessageBytes data, String encoding ) { if( data==null || data.isNull() || data.getLength() <= 0 ) return; if( data.getType() == MessageBytes.T_BYTES ) { ByteChunk bc=data.getByteChunk(); processParameters( bc.getBytes(), bc.getOffset(), bc.getLength(), encoding); } else { if (data.getType()!= MessageBytes.T_CHARS ) data.toChars(); CharChunk cc=data.getCharChunk(); processParameters( cc.getChars(), cc.getOffset(), cc.getLength()); } } /** Debug purpose */ public String paramsAsString() { StringBuffer sb=new StringBuffer(); Enumeration en= paramHashStringArray.keys(); while( en.hasMoreElements() ) { String k=(String)en.nextElement(); sb.append( k ).append("="); String v[]=(String[])paramHashStringArray.get( k ); for( int i=0; i<v.length; i++ ) sb.append( v[i] ).append(","); sb.append("\n"); } return sb.toString(); } private static int debug=0; private void log(String s ) { if (log.isDebugEnabled()) log.debug("Parameters: " + s ); } // -------------------- Old code, needs rewrite -------------------- /** Used by RequestDispatcher */ public void processParameters( String str ) { int end=str.length(); int pos=0; if( debug > 0) log("String: " + str ); do { boolean noEq=false; int valStart=-1; int valEnd=-1; int nameStart=pos; int nameEnd=str.indexOf('=', nameStart ); int nameEnd2=str.indexOf('&', nameStart ); if( nameEnd2== -1 ) nameEnd2=end; if( (nameEnd2!=-1 ) && ( nameEnd==-1 || nameEnd > nameEnd2) ) { nameEnd=nameEnd2; noEq=true; valStart=nameEnd; valEnd=nameEnd; if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + str.substring(nameStart, nameEnd) ); } if( nameEnd== -1 ) nameEnd=end; if( ! noEq ) { valStart=nameEnd+1; valEnd=str.indexOf('&', valStart); if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart; } pos=valEnd+1; if( nameEnd<=nameStart ) { continue; } if( debug>0) log( "XXX " + nameStart + " " + nameEnd + " " + valStart + " " + valEnd ); try { tmpNameC.append(str, nameStart, nameEnd-nameStart ); tmpValueC.append(str, valStart, valEnd-valStart ); if( debug > 0 ) log( tmpNameC + "= " + tmpValueC); if( urlDec==null ) { urlDec=new UDecoder(); } urlDec.convert( tmpNameC ); urlDec.convert( tmpValueC ); if( debug > 0 ) log( tmpNameC + "= " + tmpValueC); addParam( tmpNameC.toString(), tmpValueC.toString() ); } catch( IOException ex ) { ex.printStackTrace(); } tmpNameC.recycle(); tmpValueC.recycle(); } while( pos<end ); } } Other Tomcat examples (source code examples)Here is a short list of links related to this Tomcat Parameters.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.