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 }