001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.source.impl;
018:
019: import java.io.IOException;
020: import java.util.ArrayList;
021: import java.util.List;
022:
023: import org.apache.excalibur.source.Source;
024: import org.apache.excalibur.source.SourceResolver;
025: import org.apache.excalibur.source.SourceValidity;
026: import org.apache.excalibur.source.impl.validity.AbstractAggregatedValidity;
027:
028: /**
029: * <p>An aggregated {@link SourceValidity} for multiple sources.</p>
030: *
031: * @author <a href="http://www.apache.org/~sylvain">Sylvain Wallez</a>
032: * @version $Id: MultiSourceValidity.java 433543 2006-08-22 06:22:54Z crossley $
033: */
034: public class MultiSourceValidity extends AbstractAggregatedValidity {
035:
036: /** <p>When validity expiration is performed.</p> */
037: private long expiry;
038:
039: /** <p>The delay from <b>now</b> used to calculate the expiration time.</p> */
040: private long delay;
041:
042: /** <p>An ordered list of URIs which should be checked.</p> */
043: private List uris = new ArrayList();
044:
045: /** <p>Is this instance is closed (accepts modifications or is validable)? */
046: private boolean isClosed = false;
047:
048: /** <p>The {@link SourceResolver} to use (transient not to be serialized). */
049: private transient SourceResolver resolver;
050:
051: /**
052: * <p>The delay value indicating to check always.</p>
053: */
054: public static final int CHECK_ALWAYS = -1;
055:
056: /**
057: * <p>Create a new {@link MultiSourceValidity} instance.</p>
058: *
059: * <p>If the number of milliseconds is less than <b>zero</b>, or it's sum with
060: * the number of <b>now</b> milliseconds is greater than the biggest long
061: * representable, the expiration date will be set to {@link Long#MAX_VALUE}
062: * milliseconds from the epoch.</p>
063: *
064: * @param resolver the {@link SourceResolver} used to access the sources.
065: * @param delay the number of milliseconds from <b>now</b> defining for how long
066: * this instance will be valid.
067: */
068: public MultiSourceValidity(SourceResolver resolver, long delay) {
069: /* Calculate the initial expiration time and calculate the delay */
070: this .resolver = resolver;
071: this .expiry = System.currentTimeMillis() + delay;
072: this .delay = delay;
073: }
074:
075: /**
076: * <p>Add a {@link Source} to the list of {@link Source}s monitored by this
077: * instance.</p>
078: *
079: * @param src a <b>non-null</b> {@link Source}.
080: */
081: public void addSource(Source src) {
082: if (this .uris != null) {
083: SourceValidity validity = src.getValidity();
084: if (validity == null) {
085: /* The source has no validity: this will be always be invalid. */
086: this .uris = null;
087: } else {
088: /* Add the validity and URI to the list */
089: super .add(validity);
090: this .uris.add(src.getURI());
091: }
092: }
093: }
094:
095: /**
096: * <p>Close this instance, or in other words declare that no other sources will
097: * be added to this {@link MultiSourceValidity} and that checkings can be now
098: * performed.</p>
099: */
100: public void close() {
101: this .isClosed = true;
102: this .resolver = null;
103: }
104:
105: /**
106: * <p>Check the validity of this {@link SourceValidity} instance.</p>
107: *
108: * @see SourceValidity#isValid()
109: */
110: public int isValid() {
111: if (System.currentTimeMillis() <= expiry) {
112: /* Validity not expired, so, don't even check */
113: return SourceValidity.VALID;
114: }
115:
116: /* Re-calculate the expiry time based on the current time */
117: expiry = System.currentTimeMillis() + delay;
118:
119: if (uris == null || !isClosed) {
120: /* We have not been closed (yet) or we were forced to be invalid */
121: return SourceValidity.INVALID;
122: } else {
123: /* Compute the status of all the sources listed in this instance */
124: return computeStatus(null);
125: }
126: }
127:
128: /**
129: * <p>Check the validity of this instance comparing it with a (recently acquired)
130: * new {@link SourceValidity} object.</p>
131: *
132: * @see SourceValidity#isValid(SourceValidity)
133: */
134: public int isValid(SourceValidity newValidity) {
135: if (uris == null || !isClosed) {
136: /* We have not been closed (yet) or we were forced to be invalid */
137: return SourceValidity.INVALID;
138: }
139:
140: /* Perform a simple class check and compute the validity of the sources */
141: if (newValidity instanceof MultiSourceValidity) {
142: return computeStatus(((MultiSourceValidity) newValidity).resolver);
143: } else {
144: /* The supplied validity is not an instance of ourselves, forget it */
145: return SourceValidity.INVALID;
146: }
147: }
148:
149: /**
150: * <p>Compute the status of this instance by checking every source.</p>
151: *
152: * @param resolver The {@link SourceResolver} to use to access sources.
153: * @return {@link SourceValidity.VALID}, {@link SourceValidity.INVALID} or
154: * {@link SourceValidity.UNKNOWN} depending on the status.
155: */
156: private int computeStatus(SourceResolver resolver) {
157: /* Get the validities and analyse them one by one */
158: List validities = super .getValidities();
159: for (int i = 0; i < validities.size(); i++) {
160:
161: /* Check the validity status */
162: SourceValidity validity = (SourceValidity) validities
163: .get(i);
164: switch (validity.isValid()) {
165:
166: /* The current source is valid: just continue to next source */
167: case SourceValidity.VALID:
168: break;
169:
170: /* The current source is invalid: stop examining */
171: case SourceValidity.INVALID:
172: return SourceValidity.INVALID;
173:
174: /* The source validity is not known: check with the new source */
175: case SourceValidity.UNKNOWN:
176: /* We have no resolver: definitely don't know */
177: if (resolver == null) {
178: return SourceValidity.UNKNOWN;
179: }
180:
181: /* Check the new source by asking to the resolver */
182: Source newSrc = null;
183: int newValidity = SourceValidity.INVALID;
184: try {
185: newSrc = resolver.resolveURI((String) this .uris
186: .get(i));
187: newValidity = validity
188: .isValid(newSrc.getValidity());
189: } catch (IOException ioe) {
190: /* Swallow the IOException, but set the new validity */
191: newValidity = SourceValidity.INVALID;
192: } finally {
193: /* Make sure that the source is released */
194: if (newSrc != null) {
195: resolver.release(newSrc);
196: }
197: }
198:
199: /* If the source is still valid, go to the next one */
200: if (newValidity == SourceValidity.VALID) {
201: break;
202: }
203:
204: /* The source is not valid (or unknown), we invalidate the lot */
205: return SourceValidity.INVALID;
206:
207: /* We got something _really_ odd out tof the validity, dunno. */
208: default:
209: return SourceValidity.INVALID;
210: }
211: }
212:
213: /* All items checked successfully */
214: return SourceValidity.VALID;
215: }
216: }
|