/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package javax.security.jacc; import java.util.HashMap; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; /** * This class is used ... * <P> * * @author Ron Monzillo * @author Gary Ellison */ final class HttpMethodSpec { private static final String comma = ","; private static final String emptyString = ""; private static final String exclaimationPoint = "!"; private static final char exclaimationPointChar = '!'; private static Object methodKeys[] = { "DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT", "TRACE" }; private static int mapSize = methodKeys.length; private static HashMap methodHash = new HashMap(); static { int b = 1; for (int i=0; i<mapSize; i++) { methodHash.put(methodKeys[i], Integer.valueOf(b)); b = b << 1; } }; private static int allSet; static { allSet = 0; for (int i=0; i<mapSize; i++) { allSet = allSet << 1; allSet += 1; } } private static HttpMethodSpec specArray[] = new HttpMethodSpec[allSet + 1]; static { for (int i=0; i<allSet+1; i++) { specArray[i] = new HttpMethodSpec(false,i); } } private static HttpMethodSpec exceptionSpecArray[] = new HttpMethodSpec[allSet + 1]; static { for (int i=0; i<allSet+1; i++) { exceptionSpecArray[i] = new HttpMethodSpec(true,i); } } private static HttpMethodSpec allSpec = new HttpMethodSpec(false,0); private static ArrayList extensionMethods = new ArrayList(); HttpMethodSpec standardSpec; boolean exceptionList; int standardMap; BitSet extensionSet; String actions; static HttpMethodSpec getSpec (String actions) { HttpMethodSpec rvalue; if (actions == null || actions.equals(emptyString)) { rvalue = allSpec; } else { BitSet set = new BitSet(); rvalue = getStandardSpec(actions,set); if (!set.isEmpty()) { rvalue = new HttpMethodSpec(rvalue,set); } } return rvalue; } static HttpMethodSpec getSpec (String[] methods) { HttpMethodSpec rvalue; if (methods == null || methods.length == 0) { rvalue = allSpec; } else { int map = 0; BitSet set = new BitSet(); for (int i=0; i<methods.length; i++) { Integer bit = (Integer) methodHash.get(methods[i]); if (bit != null) { map |= bit.intValue(); } else { setExtensionBit(methods[i],set); } } if (set.isEmpty()) { rvalue = specArray[map]; } else { rvalue = new HttpMethodSpec(specArray[map],set); } } return rvalue; } public String toString() { return getActions(); } String getActions() { if (standardMap == 0 && extensionSet == null) { return null; } synchronized(this) { if (actions != null) { return actions; } if (standardSpec != null) { actions = getExtensionActions(standardSpec.getActions(),standardMap, extensionSet); } else { actions = getStandardActions(exceptionList,standardMap); } } return actions; } public int hashCode() { return (this.exceptionList ? 1 : 0) + (this.standardMap << 1) + ((this.extensionSet == null ? 0 : this.extensionSet.hashCode()) << mapSize +1); } public boolean equals (Object that) { boolean rvalue = false; if (that != null && that instanceof HttpMethodSpec) { if (that == this) { rvalue = true; } else { rvalue = this.hashCode() == ((HttpMethodSpec) that).hashCode(); } } return rvalue; } boolean implies (HttpMethodSpec that) { boolean rvalue; // null actions implies everything if (this.standardMap == 0 && this.extensionSet == null) { rvalue = true; } // only the null actions can implie the null actions else if (that.standardMap == 0 && that.extensionSet == null) { rvalue = false; } // both are an HttpMethodExceptionList else if (this.exceptionList && that.exceptionList) { rvalue = (this.standardMap & that.standardMap) == this.standardMap; if (rvalue) { if (this.extensionSet != null) { if (that.extensionSet == null) { rvalue = false; } else { BitSet clone = (BitSet) that.extensionSet.clone(); clone.and(this.extensionSet); rvalue = clone.equals(this.extensionSet) ? true : false; } } } } // neither is an HttpMethodExceptionList else if (this.exceptionList == that.exceptionList) { rvalue = (this.standardMap & that.standardMap) == that.standardMap; if (rvalue) { if (that.extensionSet != null) { if (this.extensionSet == null) { rvalue = false; } else { BitSet clone = (BitSet) that.extensionSet.clone(); clone.and(this.extensionSet); rvalue = clone.equals(that.extensionSet); } } } } // one or the other is an HttpMethodExceptionList else if (this.exceptionList) { rvalue = (this.standardMap & that.standardMap) == 0; if (rvalue) { if (that.extensionSet != null) { if (this.extensionSet == null) { rvalue = true; } else { rvalue = this.extensionSet.intersects (that.extensionSet) ? false : true; } } } } // an explicit list can never imply an exception list else { rvalue = false; } return rvalue; } // beginning of private methods private HttpMethodSpec (boolean isExceptionList, int map) { standardSpec = null; exceptionList = isExceptionList; standardMap = map; extensionSet = null; actions = null; } private HttpMethodSpec(HttpMethodSpec spec, BitSet set) { standardSpec = spec; exceptionList = spec.exceptionList; standardMap = spec.standardMap; extensionSet = set.isEmpty() ? null : set; actions = null; } private static void setExtensionBit(String method, BitSet set) { int bitPos; synchronized (extensionMethods) { bitPos = extensionMethods.indexOf(method); if (bitPos < 0) { bitPos = extensionMethods.size(); // *** should ensure method is syntactically legal extensionMethods.add(method); } } set.set(bitPos); } private static String getExtensionMethod(int bitPos) { synchronized (extensionMethods) { if (bitPos >= 0 && bitPos < extensionMethods.size()) { return (String) extensionMethods.get(bitPos); } else { throw new RuntimeException ("invalid (extensionMethods) bit position: '" + bitPos + "' size: '" + extensionMethods.size() + " '"); } } } private static HttpMethodSpec getStandardSpec(String actions, BitSet set) { boolean isExceptionList = false; if (actions.charAt(0) == exclaimationPointChar) { isExceptionList = true; if (actions.length() < 2) { throw new IllegalArgumentException ("illegal HTTP method Spec actions: '" + actions + "'"); } actions = actions.substring(1); } int map = makeMethodSet(actions, set); HttpMethodSpec rvalue; if (isExceptionList) { rvalue = exceptionSpecArray[map]; } else { rvalue = specArray[map]; } return rvalue; } private static int makeMethodSet (String actions, BitSet set) { int i = 0; int mSet = 0; int commaPos = 0; while (commaPos >= 0 && i < actions.length()) { commaPos = actions.indexOf(comma,i); if (commaPos != 0) { String method; if (commaPos < 0) { method = actions.substring(i); } else { method = actions.substring(i,commaPos); } Integer bit = (Integer) methodHash.get(method); if (bit != null) { mSet |= bit.intValue(); } else { setExtensionBit(method,set); } i = commaPos + 1; } else { throw new IllegalArgumentException ("illegal HTTP method Spec actions: '" + actions + "'"); } } return mSet; } private String getExtensionActions(String standardActions, int map, BitSet set) { ArrayList methods = null; for(int i=set.nextSetBit(0); i>=0; i=set.nextSetBit(i+1)) { if (methods == null) { methods = new ArrayList(); } methods.add(getExtensionMethod(i)); } String rvalue; if (methods == null) { rvalue = standardActions; } else { Collections.sort(methods); StringBuffer actBuf = new StringBuffer (standardActions == null ? (exceptionList ? exclaimationPoint : emptyString) : standardActions); for (int i = 0; i < methods.size(); i++) { if (i > 0 || map > 0) { actBuf.append(comma); } actBuf.append(methods.get(i)); } rvalue = actBuf.toString(); } return rvalue; } private String getStandardActions(boolean isExceptionList, int map) { int bitValue = 1; StringBuffer actBuf = null; for (int i=0; i<mapSize; i++) { if ((map & bitValue) == bitValue) { if (actBuf == null) { actBuf = new StringBuffer (isExceptionList ? exclaimationPoint : emptyString); } else { actBuf.append(comma); } actBuf.append((String) methodKeys[i]); } bitValue = bitValue * 2; } if (actBuf == null) { return isExceptionList ? exclaimationPoint : emptyString; } else { return actBuf.toString(); } } }