View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package net.jini.core.constraint;
20  
21  import java.io.IOException;
22  import java.io.InvalidObjectException;
23  import java.io.ObjectInputStream;
24  import java.io.ObjectStreamField;
25  import java.io.Serializable;
26  import java.util.Collection;
27  import java.util.Set;
28  import org.apache.river.api.io.AtomicSerial;
29  import org.apache.river.api.io.AtomicSerial.GetArg;
30  
31  /**
32   * An immutable aggregation of constraints into a set of requirements and a
33   * set of preferences. A requirement is a mandatory constraint that must be
34   * satisfied for the invocation. A preference is a desired constraint, to be
35   * satisfied if possible, but it will not be satisfied if it conflicts with a
36   * requirement. If two preferences conflict, it is arbitrary as to which
37   * one will be satisfied. If a constraint is not understood, due to lack of
38   * knowledge of the type of the constraint or the contents of the constraint,
39   * then the constraint cannot be satisfied.
40   * <p>
41   * Note that it is possible for an instance of this class to contain both
42   * requirements that conflict with each other, and preferences that conflict
43   * with each other and with requirements.
44   *
45   * @author Sun Microsystems, Inc.
46   * @since 2.0
47   */
48  @AtomicSerial
49  public final class InvocationConstraints implements Serializable {
50      private static final long serialVersionUID = -3363161199079334224L;
51  
52      /**
53       * @serialField reqs InvocationConstraint[] The requirements.
54       * @serialField prefs InvocationConstraint[] The preferences.
55       */
56      private static final ObjectStreamField[] serialPersistentFields = {
57          new ObjectStreamField("reqs", InvocationConstraint[].class, true),
58          new ObjectStreamField("prefs", InvocationConstraint[].class, true)
59      };
60  
61      /** An empty array */
62      private static final InvocationConstraint[] empty =
63  						  new InvocationConstraint[0];
64  
65      /**
66       * Bit set in the rel field if there are relative requirements.
67       */
68      private static final int REL_REQS = 1;
69      /**
70       * Bit set in the rel field if there are relative preferences.
71       */
72      private static final int REL_PREFS = 2;
73  
74      /**
75       * An empty instance, one that has no requirements and no preferences.
76       */
77      public static final InvocationConstraints EMPTY =
78  		  new InvocationConstraints((InvocationConstraint) null, null);
79  
80      /**
81       * The requirements.
82       */
83      private InvocationConstraint[] reqs;
84      /**
85       * The preferences.
86       */
87      private InvocationConstraint[] prefs;
88  
89      /**
90       * Flags indicating whether any requirements and/or preferences are
91       * based on relative time.
92       */
93      private transient int rel = 0;
94      
95      /**
96       * AtomicSerial constructor.
97       * 
98       * @param arg atomic deserialization parameter 
99       * @throws IOException if there are I/O errors while reading from GetArg's
100      *         underlying <code>InputStream</code> 
101      */
102     public InvocationConstraints(GetArg arg) throws IOException{
103 	this(arg.get("reqs", null, InvocationConstraint[].class),
104 	     arg.get("prefs", null, InvocationConstraint[].class),
105 	     true
106 	);
107     }
108     
109     /**
110      * AtomicSerial private constructor.
111      * @param reqs
112      * @param prefs
113      * @param serial
114      * @throws InvalidObjectException 
115      */
116     private InvocationConstraints(InvocationConstraint[] reqs, 
117 				  InvocationConstraint[] prefs,
118 				  boolean serial) throws InvalidObjectException
119     {
120 	this(check(reqs, prefs),
121              verify(reqs),
122              verify(prefs)
123         );
124     }
125     
126     private static boolean check(InvocationConstraint[] reqs, 
127 				 InvocationConstraint[] prefs) throws InvalidObjectException
128     {
129         for (int i = prefs.length; --i >= 0; ) {
130 	    if (Constraint.contains(reqs, reqs.length, prefs[i])) {
131 		throw new InvalidObjectException(
132 			  "cannot create constraint with redundant elements");
133 	    }
134 	}
135         return true;
136     }
137     
138     /**
139      * AtomicSerial private constructor.
140      * @param serial
141      * @param reqs
142      * @param prefs 
143      */
144     private InvocationConstraints(boolean serial,
145 				  InvocationConstraint[] reqs, 
146 				  InvocationConstraint[] prefs)
147     {
148 	this.reqs = reqs;
149 	this.prefs = prefs;
150 	setRelative(reqs, REL_REQS);
151 	setRelative(prefs, REL_REQS);
152     }
153 
154     /**
155      * Creates an instance that has the first constraint, <code>req</code>,
156      * added as a requirement if it is a non-<code>null</code> value, and has
157      * the second constraint, <code>pref</code>, added as a preference if it
158      * is a non-<code>null</code> value and is not a duplicate of the
159      * requirement.
160      *
161      * @param req a requirement, or <code>null</code>
162      * @param pref a preference, or <code>null</code>
163      */
164     public InvocationConstraints(InvocationConstraint req,
165 				 InvocationConstraint pref)
166     {
167 	if (req != null) {
168 	    reqs = new InvocationConstraint[]{req};
169 	}
170 	if (pref != null) {
171 	    prefs = new InvocationConstraint[]{pref};
172 	}
173 	reduce();
174     }
175 
176     /**
177      * Creates an instance that has all of the constraints from the first
178      * array, <code>reqs</code>, added as requirements if the array is a
179      * non-<code>null</code> value, and has all of the constraints from
180      * the second array, <code>prefs</code>, added as preferences if the
181      * array is a non-<code>null</code> value. Duplicate requirements,
182      * duplicate preferences, and preferences that are duplicates of
183      * requirements are all removed. The arguments passed to the constructor
184      * are neither modified nor retained; subsequent changes to those
185      * arguments have no effect on the instance created.
186      *
187      * @param reqs requirements, or <code>null</code>
188      * @param prefs preferences, or <code>null</code>
189      * @throws NullPointerException if any element of an argument is
190      * <code>null</code>
191      */
192     public InvocationConstraints(InvocationConstraint[] reqs,
193 				 InvocationConstraint[] prefs)
194     {
195 	if (reqs != null) {
196 	    this.reqs = (InvocationConstraint[]) reqs.clone();
197 	}
198 	if (prefs != null) {
199 	    this.prefs = (InvocationConstraint[]) prefs.clone();
200 	}
201 	reduce();
202     }
203 
204     /**
205      * Creates an instance that has all of the constraints from the first
206      * collection, <code>reqs</code>, added as requirements if the collection
207      * is a non-<code>null</code> value, and has all of the constraints from
208      * the second collection, <code>prefs</code>, added as preferences if the
209      * collection is a non-<code>null</code> value. Duplicate requirements,
210      * duplicate preferences, and preferences that are duplicates of
211      * requirements are all removed. The arguments passed to the constructor
212      * are neither modified nor retained; subsequent changes to those
213      * arguments have no effect on the instance created.
214      *
215      * @param reqs requirements, or <code>null</code>
216      * @param prefs preferences, or <code>null</code>
217      * @throws NullPointerException if any element of an argument is
218      * <code>null</code>
219      * @throws IllegalArgumentException if any element of an argument is not
220      * an instance of <code>InvocationConstraint</code>
221      */
222     public InvocationConstraints(Collection<InvocationConstraint> reqs, Collection<InvocationConstraint> prefs) {
223 	try {
224 	    if (reqs != null) {
225 		this.reqs = reqs.toArray(new InvocationConstraint[reqs.size()]);
226 	    }
227 	    if (prefs != null) {
228 		this.prefs = prefs.toArray(
229 				       new InvocationConstraint[prefs.size()]);
230 	    }
231 	} catch (ArrayStoreException e) {
232 	    throw new IllegalArgumentException(
233 		       "element of collection is not an InvocationConstraint");
234 	}
235 	reduce();
236     }
237 
238     /**
239      * Creates an instance containing the specified requirements and
240      * preferences. Reqidx and prefidx indicate how many of the initial
241      * elements in each array are known to have been reduced (meaning they
242      * came from an existing instance of this class), but prefidx must be
243      * zero if reqidx is non-zero but reqidx is less than the total number
244      * of requirements. Rel contains the flags for which constraints are
245      * relative.
246      */
247     private InvocationConstraints(InvocationConstraint[] reqs,
248 				  int reqidx,
249 				  InvocationConstraint[] prefs,
250 				  int prefidx,
251 				  int rel)
252     {
253 	this.reqs = reqs;
254 	this.prefs = prefs;
255 	reduce(reqidx, prefidx);
256 	this.rel = rel;
257     }
258 
259     /**
260      * Replaces null fields with empty arrays, eliminates duplicates, and
261      * sets flags indicating which constraints are relative.
262      */
263     private void reduce() {
264 	if (reqs == null) {
265 	    reqs = empty;
266 	}
267 	if (prefs == null) {
268 	    prefs = empty;
269 	}
270 	reduce(0, 0);
271 	setRelative(reqs, REL_REQS);
272 	setRelative(prefs, REL_PREFS);
273     }
274 
275     /**
276      * Checks for nulls and eliminates duplicates. Reqidx and prefidx
277      * indicate how many of the initial elements in each array are known to
278      * have been reduced (meaning they came from an existing instance of this
279      * class), but prefidx must be zero if reqidx is non-zero but reqidx is
280      * less than the total number of requirements.
281      */
282     private void reduce(int reqidx, int prefidx) {
283 	for (int i = reqidx; i < reqs.length; i++) {
284 	    InvocationConstraint req = reqs[i];
285 	    if (req == null) {
286 		throw new NullPointerException("elements cannot be null");
287 	    } else if (!Constraint.contains(reqs, reqidx, req)) {
288 		reqs[reqidx++] = req;
289 	    }
290 	}
291 	reqs = (InvocationConstraint[]) Constraint.trim(reqs, reqidx);
292 	for (int i = prefidx; i < prefs.length; i++) {
293 	    InvocationConstraint pref = prefs[i];
294 	    if (pref == null) {
295 		throw new NullPointerException("elements cannot be null");
296 	    } else if (!Constraint.contains(prefs, prefidx, pref) &&
297 		       !Constraint.contains(reqs, reqs.length, pref))
298 	    {
299 		prefs[prefidx++] = pref;
300 	    }
301 	}
302 	prefs = (InvocationConstraint[]) Constraint.trim(prefs, prefidx);
303     }
304 
305     /**
306      * Returns true if the specified constraint either implements
307      * RelativeTimeConstraint or is an instance of ConstraintAlternatives with
308      * elements that implement RelativeTimeConstraint, and false otherwise.
309      */
310     private static boolean relative(InvocationConstraint c) {
311 	return (c instanceof RelativeTimeConstraint &&
312 		(!(c instanceof ConstraintAlternatives) ||
313 		 ((ConstraintAlternatives) c).relative()));
314     }
315 
316     /**
317      * Sets the given flag in the rel field if any if the specified
318      * constraints are relative.
319      */
320     private void setRelative(InvocationConstraint[] constraints, int flag) {
321 	for (int i = constraints.length; --i >= 0; ) {
322 	    if (relative(constraints[i])) {
323 		rel |= flag;
324 		return;
325 	    }
326 	}
327     }
328 
329     /**
330      * Returns an instance of this class that has all of the requirements from
331      * each non-<code>null</code> argument added as requirements and has all
332      * of the preferences from each non-<code>null</code> argument added as
333      * preferences. Duplicate requirements, duplicate preferences, and
334      * preferences that are duplicates of requirements are all removed.
335      *
336      * @param constraints1 constraints, or <code>null</code>
337      * @param constraints2 constraints, or <code>null</code>
338      * @return an instance of this class that has all of the requirements from
339      * each non-<code>null</code> argument added as requirements and has all
340      * of the preferences from each non-<code>null</code> argument added as
341      * preferences
342      */
343     public static InvocationConstraints combine(
344 					   InvocationConstraints constraints1,
345 					   InvocationConstraints constraints2)
346     {
347 	if (constraints1 == null || constraints1.isEmpty()) {
348 	    return constraints2 == null ? EMPTY : constraints2;
349 	} else if (constraints2 == null || constraints2.isEmpty()) {
350 	    return constraints1;
351 	} else if (constraints2.reqs.length > constraints1.reqs.length) {
352 	    InvocationConstraints tmp = constraints1;
353 	    constraints1 = constraints2;
354 	    constraints2 = tmp;
355 	}
356 	int prefidx;
357 	InvocationConstraint[] reqs;
358 	if (constraints2.reqs.length > 0) {
359 	    reqs = concat(constraints1.reqs, constraints2.reqs);
360 	    prefidx = 0;
361 	} else {
362 	    reqs = constraints1.reqs;
363 	    prefidx = constraints1.prefs.length;
364 	}
365 	InvocationConstraint[] prefs;
366 	if (constraints1.prefs.length > 0 || constraints2.prefs.length > 0) {
367 	    prefs = concat(constraints1.prefs, constraints2.prefs);
368 	} else {
369 	    prefs = empty;
370 	}
371 	return new InvocationConstraints(reqs, constraints1.reqs.length,
372 					 prefs, prefidx,
373 					 constraints1.rel | constraints2.rel);
374     }
375 
376     /**
377      * Returns a new array containing the elements of both arguments.
378      */
379     private static InvocationConstraint[] concat(InvocationConstraint[] arr1,
380 						 InvocationConstraint[] arr2)
381     {
382 	InvocationConstraint[] res =
383 	    new InvocationConstraint[arr1.length + arr2.length];
384 	System.arraycopy(arr1, 0, res, 0, arr1.length);
385 	System.arraycopy(arr2, 0, res, arr1.length, arr2.length);
386 	return res;
387     }
388 
389     /**
390      * Converts any relative constraints to absolute time.
391      */
392     private static InvocationConstraint[] makeAbsolute(
393 						   InvocationConstraint[] arr,
394 						   long baseTime)
395     {
396 	InvocationConstraint[] narr = new InvocationConstraint[arr.length];
397 	for (int i = arr.length; --i >= 0; ) {
398 	    InvocationConstraint c = arr[i];
399 	    if (c instanceof RelativeTimeConstraint) {
400 		c = ((RelativeTimeConstraint) c).makeAbsolute(baseTime);
401 	    }
402 	    narr[i] = c;
403 	}
404 	return narr;
405     }
406 
407     /**
408      * Returns an instance of this class equal to the result of taking the
409      * requirements and preferences in this instance, replacing each
410      * constraint that is an instance of {@link RelativeTimeConstraint} with
411      * the result of invoking that constraint's <code>makeAbsolute</code>
412      * method with the specified base time, and creating a new instance of
413      * this class with duplicate requirements, duplicate preferences, and
414      * preferences that are duplicates of requirements all removed.
415      *
416      * @param baseTime an absolute time, specified in milliseconds from
417      * midnight, January 1, 1970 UTC
418      * @return an instance of this class equal to the result of taking the
419      * requirements and preferences in this instance, replacing each
420      * constraint that is an instance of <code>RelativeTimeConstraint</code>
421      * with the result of invoking that constraint's <code>makeAbsolute</code>
422      * method with the specified base time, and creating a new instance of
423      * this class with duplicate requirements, duplicate preferences, and
424      * preferences that are duplicates of requirements all removed
425      */
426     public InvocationConstraints makeAbsolute(long baseTime) {
427 	if (rel == 0) {
428 	    return this;
429 	}
430 	InvocationConstraint[] nreqs;
431 	int reqidx;
432 	if ((rel & REL_REQS) != 0) {
433 	    nreqs = makeAbsolute(reqs, baseTime);
434 	    reqidx = 0;
435 	} else {
436 	    nreqs = reqs;
437 	    reqidx = reqs.length;
438 	}
439 	InvocationConstraint[] nprefs;
440 	if ((rel & REL_PREFS) != 0) {
441 	    nprefs = makeAbsolute(prefs, baseTime);
442 	} else {
443 	    nprefs = (InvocationConstraint[]) prefs.clone();
444 	}
445 	return new InvocationConstraints(nreqs, reqidx, nprefs, 0, 0);
446     }
447 
448     /**
449      * Returns an instance of this class constructed from all of the same
450      * requirements and preferences as this instance, but with every
451      * constraint that is an instance of {@link RelativeTimeConstraint}
452      * replaced by the result of invoking the constraint's
453      * <code>makeAbsolute</code> method with the current time (as given by
454      * {@link System#currentTimeMillis System.currentTimeMillis}). Duplicate
455      * requirements, duplicate preferences, and preferences that are
456      * duplicates of requirements are all removed.
457      *
458      * @return an instance of this class constructed from all of the same
459      * requirements and preferences as this instance, but with every
460      * constraint that is an instance of <code>RelativeTimeConstraint</code>
461      * replaced by the result of invoking the constraint's
462      * <code>makeAbsolute</code> method with the current time
463      */
464     public InvocationConstraints makeAbsolute() {
465 	if (rel == 0) {
466 	    return this;
467 	}
468 	return makeAbsolute(System.currentTimeMillis());
469     }
470 
471     /**
472      * Returns an immutable set of all of the requirements. Any attempt to
473      * modify this set results in an {@link UnsupportedOperationException}
474      * being thrown.
475      *
476      * @return an immutable set of all of the requirements
477      */
478     public Set<InvocationConstraint> requirements() {
479 	return new ArraySet<InvocationConstraint>(reqs);
480     }
481 
482     /**
483      * Returns an immutable set of all of the preferences. Any attempt to
484      * modify this set results in an {@link UnsupportedOperationException}
485      * being thrown.
486      *
487      * @return an immutable set of all of the preferences
488      */
489     public Set<InvocationConstraint> preferences() {
490 	return new ArraySet<InvocationConstraint>(prefs);
491     }
492 
493     /**
494      * Returns <code>true</code> if the instance has no requirements and no
495      * preferences; returns <code>false</code> otherwise.
496      *
497      * @return <code>true</code> if the instance has no requirements and no
498      * preferences; <code>false</code> otherwise
499      */
500     public boolean isEmpty() {
501 	return reqs.length == 0 && prefs.length == 0;
502     }
503 
504     /**
505      * Returns a hash code value for this object.
506      */
507     public int hashCode() {
508 	return Constraint.hash(reqs) + Constraint.hash(prefs);
509     }
510 
511     /**
512      * Two instances of this class are equal if they have the same requirements
513      * and the same preferences. This method is a sufficient substitute for
514      * {@link net.jini.security.proxytrust.TrustEquivalence#checkTrustEquivalence
515      * TrustEquivalence.checkTrustEquivalence}.
516      */
517     public boolean equals(Object obj) {
518 	if (this == obj) {
519 	    return true;
520 	} else if (!(obj instanceof InvocationConstraints)) {
521 	    return false;
522 	}
523 	InvocationConstraints sc = (InvocationConstraints)obj;
524 	return (Constraint.equal(reqs, sc.reqs) &&
525 		Constraint.equal(prefs, sc.prefs));
526     }
527 
528     /**
529      * Returns a string representation of this object.
530      */
531     public String toString() {
532 	return ("InvocationConstraints[reqs: " + Constraint.toString(reqs) +
533 		", prefs: " + Constraint.toString(prefs) + "]");
534     }
535     
536     /**
537      * Verifies that there are no <code>null</code> elements and no duplicates.
538      *
539      * @throws InvalidObjectException if the requirements or preferences
540      * arrays are <code>null</code>, or any element is <code>null</code>,
541      * or if there are duplicate requirements, duplicate preferences, or
542      * preferences that are duplicates of requirements
543      * @param s ObjectInputStream
544      * @throws ClassNotFoundException if class not found.
545      * @throws IOException if a problem occurs during de-serialization.
546      */
547     /* Also sets the rel field */
548     private void readObject(ObjectInputStream s)
549 	throws IOException, ClassNotFoundException
550     {
551 	s.defaultReadObject();
552 	verify(reqs);
553 	verify(prefs);
554 	for (int i = prefs.length; --i >= 0; ) {
555 	    if (Constraint.contains(reqs, reqs.length, prefs[i])) {
556 		throw new InvalidObjectException(
557 			  "cannot create constraint with redundant elements");
558 	    }
559 	}
560 	setRelative(reqs, REL_REQS);
561 	setRelative(prefs, REL_REQS);
562     }
563 
564     /**
565      * Verifies that the array is non-null, the elements are all non-null,
566      * and there are no duplicates.
567      */
568     private static InvocationConstraint[] verify(InvocationConstraint[] constraints)
569 	throws InvalidObjectException
570     {
571 	if (constraints == null) {
572 	    throw new InvalidObjectException("array cannot be null");
573 	}
574 	for (int i = constraints.length; --i >= 0; ) {
575 	    InvocationConstraint c = constraints[i];
576 	    if (c == null) {
577 		throw new InvalidObjectException("elements cannot be null");
578 	    } else if (Constraint.contains(constraints, i, c)) {
579 		throw new InvalidObjectException(
580 			  "cannot create constraint with redundant elements");
581 	    }
582 	}
583         return constraints;
584     }
585 }