/* * 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 net.jini.jeri.http; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.HashMap; import java.util.Map; import net.jini.core.constraint.ClientAuthentication; import net.jini.core.constraint.ClientMaxPrincipal; import net.jini.core.constraint.ClientMaxPrincipalType; import net.jini.core.constraint.ClientMinPrincipal; import net.jini.core.constraint.ClientMinPrincipalType; import net.jini.core.constraint.Confidentiality; import net.jini.core.constraint.ConnectionAbsoluteTime; import net.jini.core.constraint.ConnectionRelativeTime; import net.jini.core.constraint.ConstraintAlternatives; import net.jini.core.constraint.Delegation; import net.jini.core.constraint.DelegationAbsoluteTime; import net.jini.core.constraint.DelegationRelativeTime; import net.jini.core.constraint.Integrity; import net.jini.core.constraint.InvocationConstraint; import net.jini.core.constraint.InvocationConstraints; import net.jini.core.constraint.RelativeTimeConstraint; import net.jini.core.constraint.ServerAuthentication; import net.jini.core.constraint.ServerMinPrincipal; import net.jini.io.UnsupportedConstraintException; /** * Constraint support for this transport provider. * * This code makes some significant simplifying assumptions: * * - The transport layer aspects of all constraints supported by this * provider are always satisfied by all open connections and * requests. * * - No combination of individual constraints supported by this * provider can contain conflicting constraints. * * @author Sun Microsystems, Inc. **/ class Constraints { /** * indicates that this provider does not support implementing (or * does not understand how to implement) the transport layer * aspects of satisfying a given constraint **/ private static final int NO_SUPPORT = 0; /** * indicates that this provider supports implementing all aspects * of satisfying a given constraint **/ private static final int FULL_SUPPORT = 1; /** * indicates that this provider supports implementing the * transport layer aspects of satisfying a given constraint, but * at least partial implementation by higher layers is also needed * in order to fully satisfy the constraint **/ private static final int PARTIAL_SUPPORT = 2; /** * maps constraint values that are supported to Boolean indicating * whether or not they must be at least partially implemented by * higher layers to be fully satisfied **/ private static final Map supportedValues = new HashMap(); static { supportedValues.put(Integrity.NO, Boolean.TRUE); supportedValues.put(Confidentiality.NO, Boolean.FALSE); supportedValues.put(ClientAuthentication.NO, Boolean.FALSE); supportedValues.put(ServerAuthentication.NO, Boolean.FALSE); supportedValues.put(Delegation.NO, Boolean.FALSE); } /** * maps constraint classes that are supported to Boolean * indicating whether or not such constraints must be at least * partially implemented by higher layers to be fully satisfied **/ private static final Map supportedClasses = new HashMap(); static { // ConstraintAlternatives is supported but handled specially in code supportedClasses.put(ConnectionAbsoluteTime.class, Boolean.FALSE); supportedClasses.put(ConnectionRelativeTime.class, Boolean.FALSE); /* * The following classes are (trivially) supported just * because ClientAuthentication.YES, ServerAuthentication.YES, * and Delegation.YES are not supported. */ supportedClasses.put(ClientMaxPrincipal.class, Boolean.FALSE); supportedClasses.put(ClientMaxPrincipalType.class, Boolean.FALSE); supportedClasses.put(ClientMinPrincipal.class, Boolean.FALSE); supportedClasses.put(ClientMinPrincipalType.class, Boolean.FALSE); supportedClasses.put(ServerMinPrincipal.class, Boolean.FALSE); supportedClasses.put(DelegationAbsoluteTime.class, Boolean.FALSE); supportedClasses.put(DelegationRelativeTime.class, Boolean.FALSE); } /** * Returns this provider's general support for the given * constraint. **/ private static int getSupport(InvocationConstraint c) { Boolean support = (Boolean) supportedValues.get(c); if (support == null) { support = (Boolean) supportedClasses.get(c.getClass()); } return support == null ? NO_SUPPORT : support.booleanValue() ? PARTIAL_SUPPORT : FULL_SUPPORT; } /** * Checks that we support at least the transport layer aspects of * the given requirements (and throws an * UnsupportedConstraintException if not), and returns the * requirements that must be at least partially implemented by * higher layers and the supported preferences that must be at * least partially implemented by higher layers. * * [If this provider supported constraints whose transport layer * aspects were not always satisfied by open connections or * requests, then we would need a variant of this method that * checks the given constraints against an open connection or * request. If this provider supported constraints that could * conflict with each other then (when not checking against an * open connection or request) we would need to check for possible * conflicts.] **/ static InvocationConstraints check(InvocationConstraints constraints, boolean relativeOK) throws UnsupportedConstraintException { return distill(constraints, relativeOK).getUnfulfilledConstraints(); } /** * Distills the given constraints to a form more directly usable * by this provider. Throws an UnsupportedConstraintException if * we do not support at least the transport layer aspects of the * requirements. **/ static Distilled distill(InvocationConstraints constraints, boolean relativeOK) throws UnsupportedConstraintException { return new Distilled(constraints, relativeOK); } private Constraints() { throw new AssertionError(); } /** * A distillation of constraints to a form more directly usable by * this provider. **/ static class Distilled { /** * true if relative time constraints are allowed (in other * words, not for client-side use) */ private final boolean relativeOK; private Collection unfulfilledRequirements = null; // lazily created private Collection unfulfilledPreferences = null; // lazily created private boolean hasConnectDeadline = false; private long connectDeadline; Distilled(InvocationConstraints constraints, boolean relativeOK) throws UnsupportedConstraintException { this.relativeOK = relativeOK; for (Iterator i = constraints.requirements().iterator(); i.hasNext();) { addConstraint((InvocationConstraint) i.next(), true); } for (Iterator i = constraints.preferences().iterator(); i.hasNext();) { addConstraint((InvocationConstraint) i.next(), false); } } /** * Returns the requirements and supported preferences that * must be at least partially implemented by higher layers. **/ InvocationConstraints getUnfulfilledConstraints() { if (unfulfilledRequirements == null && unfulfilledPreferences == null) { return InvocationConstraints.EMPTY; } else { return new InvocationConstraints(unfulfilledRequirements, unfulfilledPreferences); } } /** * Returns true if a there is a socket connect deadline. **/ boolean hasConnectDeadline() { return hasConnectDeadline; } /** * Returns the absolute time of the socket connect deadline. **/ long getConnectDeadline() { assert hasConnectDeadline; return connectDeadline; } /** * If "isRequirement" is true, throws an * UnsupportedConstraintException if we do not support at * least the transport layer aspects of the given constraint. * * If we do support at least the transport layer aspects of * the given constraint, then if appropriate, adds it to the * collection of requirements or preferences that must be at * least partially implemented by higher layers. **/ private void addConstraint(InvocationConstraint constraint, boolean isRequirement) throws UnsupportedConstraintException { if (!(constraint instanceof ConstraintAlternatives)) { int support = getSupport(constraint); if (support == NO_SUPPORT || (!relativeOK && constraint instanceof RelativeTimeConstraint)) { if (isRequirement) { throw new UnsupportedConstraintException( "cannot satisfy constraint: " + constraint); } else { return; } } if (support == PARTIAL_SUPPORT) { if (isRequirement) { if (unfulfilledRequirements == null) { unfulfilledRequirements = new ArrayList(); } unfulfilledRequirements.add(constraint); } else { if (unfulfilledPreferences == null) { unfulfilledPreferences = new ArrayList(); } unfulfilledPreferences.add(constraint); } } if (constraint instanceof ConnectionAbsoluteTime) { // REMIND: only bother with this on client side? addConnectDeadline( ((ConnectionAbsoluteTime) constraint).getTime()); } } else { addAlternatives((ConstraintAlternatives) constraint, isRequirement); } } /** * If "isRequirement" is true, throws an * UnsupportedConstraintException if we do not support at * least the transport layer aspects of at least one of the * constraints in the given alternatives. * * If we do support at least the transport layer aspects of at * least one of the constraints in the given alternatives, * then if appropriate, adds a ConstraintAlternatives of the * supported alternatives to the collection of requirements or * preferences that must be at least partially implemented by * higher layers. * * If all of the supported alternatives need at least partial * implementation by higher layers, then adds a * ConstraintAlternatives with all of the supported * alternatives to the unfulfilled collection or preferences, * because higher layers must support at least one of them. * But if at least one of the supported alternatives can be * fully satisfied by the transport layer, then add nothing to * the unfulfilled collection, because it is possible that * higher layers need not support any of them (and there is no * way to express no constraint). * * The weakest connect deadline (with no deadline being the * the weakest possibility) is chosen among alternatives. **/ private void addAlternatives(ConstraintAlternatives constraint, boolean isRequirement) throws UnsupportedConstraintException { Collection alts = constraint.elements(); boolean supported = false; long maxConnectDeadline = Long.MIN_VALUE; Collection unfulfilledAlts = null; // lazily created boolean forgetUnfulfilled = false; for (Iterator i = alts.iterator(); i.hasNext();) { InvocationConstraint c = (InvocationConstraint) i.next(); // nested ConstraintAlternatives not allowed int support = getSupport(c); if (support == NO_SUPPORT || (!relativeOK && c instanceof RelativeTimeConstraint)) { continue; } supported = true; // we support at least one if (!forgetUnfulfilled) { if (support == PARTIAL_SUPPORT) { if (unfulfilledAlts == null) { unfulfilledAlts = new ArrayList(); } unfulfilledAlts.add(c); } else { assert support == FULL_SUPPORT; unfulfilledAlts = null; forgetUnfulfilled = true; } } if (c instanceof ConnectionAbsoluteTime) { assert support == FULL_SUPPORT; // else more care required maxConnectDeadline = Math.max(maxConnectDeadline, ((ConnectionAbsoluteTime) c).getTime()); } else { maxConnectDeadline = Long.MAX_VALUE; } } if (!supported) { if (isRequirement) { throw new UnsupportedConstraintException( "cannot satisfy constraint: " + constraint); } else { return; // maxConnectDeadline is bogus in this case } } if (!forgetUnfulfilled && unfulfilledAlts != null) { if (isRequirement) { if (unfulfilledRequirements == null) { unfulfilledRequirements = new ArrayList(); } unfulfilledRequirements.add( ConstraintAlternatives.create(unfulfilledAlts)); } else { if (unfulfilledPreferences == null) { unfulfilledPreferences = new ArrayList(); } unfulfilledPreferences.add( ConstraintAlternatives.create(unfulfilledAlts)); } } if (maxConnectDeadline < Long.MAX_VALUE) { assert maxConnectDeadline != Long.MIN_VALUE; addConnectDeadline(maxConnectDeadline); } } /** * Adds the given connect deadline to this object's state. * The earliest connect deadline is what gets remembered. **/ private void addConnectDeadline(long deadline) { if (!hasConnectDeadline) { hasConnectDeadline = true; connectDeadline = deadline; } else { connectDeadline = Math.min(connectDeadline, deadline); } } } }