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: */
018:
019: package org.apache.lenya.ac.cache;
020:
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.net.MalformedURLException;
024:
025: import org.apache.avalon.framework.activity.Disposable;
026: import org.apache.avalon.framework.logger.AbstractLogEnabled;
027: import org.apache.avalon.framework.service.ServiceException;
028: import org.apache.avalon.framework.service.ServiceManager;
029: import org.apache.avalon.framework.service.Serviceable;
030: import org.apache.avalon.framework.thread.ThreadSafe;
031: import org.apache.excalibur.source.Source;
032: import org.apache.excalibur.source.SourceNotFoundException;
033: import org.apache.excalibur.source.SourceResolver;
034: import org.apache.excalibur.source.SourceValidity;
035: import org.apache.lenya.util.CacheMap;
036:
037: /**
038: * Basic implementation of a source cache.
039: * @version $Id: SourceCacheImpl.java 566251 2007-08-15 16:35:07Z andreas $
040: */
041: public class SourceCacheImpl extends AbstractLogEnabled implements
042: SourceCache, Serviceable, Disposable, ThreadSafe {
043:
044: /**
045: * Returns the service manager.
046: * @return A service manager.
047: */
048: public ServiceManager getManager() {
049: return this .manager;
050: }
051:
052: /**
053: * Returns the source resolver.
054: * @return A source resolver.
055: */
056: public SourceResolver getResolver() {
057: return this .resolver;
058: }
059:
060: /**
061: * Ctor.
062: */
063: public SourceCacheImpl() {
064: }
065:
066: protected static final int CAPACITY = 1000;
067: private CacheMap cache;
068:
069: /**
070: * Returns the cache.
071: * @return A cache object.
072: */
073: protected CacheMap getCache() {
074: if (this .cache == null) {
075: this .cache = new CacheMap(CAPACITY, getLogger());
076: }
077: return this .cache;
078: }
079:
080: /**
081: * @see org.apache.lenya.ac.cache.SourceCache#get(java.lang.String, org.apache.lenya.ac.cache.InputStreamBuilder)
082: */
083: public synchronized Object get(String sourceUri,
084: InputStreamBuilder builder) throws CachingException {
085:
086: String key = sourceUri;
087: Object value = null;
088:
089: CachedObject cachedObject = (CachedObject) getCache().get(key);
090: boolean usedCache = false;
091: SourceValidity sourceValidity = null;
092:
093: try {
094: if (cachedObject != null) {
095: if (getLogger().isDebugEnabled()) {
096: getLogger().debug(
097: "Found cached object [" + cachedObject
098: + "]");
099: }
100: SourceValidity cachedValidity = cachedObject
101: .getValidityObject();
102:
103: int result = cachedValidity.isValid();
104: boolean valid = false;
105: if (result == 0) {
106:
107: // get source validity and compare
108:
109: sourceValidity = getSourceValidity(sourceUri);
110:
111: if (sourceValidity != null) {
112: result = cachedValidity.isValid(sourceValidity);
113: if (result == 0) {
114: sourceValidity = null;
115: } else {
116: valid = (result == 1);
117: }
118: }
119: } else {
120: valid = (result > 0);
121: }
122:
123: if (valid) {
124: if (this .getLogger().isDebugEnabled()) {
125: this .getLogger().debug(
126: "Using valid cached source for '"
127: + sourceUri + "'.");
128: }
129: usedCache = true;
130: value = cachedObject.getValue();
131: } else {
132: if (this .getLogger().isDebugEnabled()) {
133: this .getLogger().debug(
134: "Cached content is invalid for '"
135: + sourceUri + "'.");
136: }
137: // remove invalid cached object
138: getCache().remove(key);
139: }
140:
141: } else {
142: getLogger().debug("Did not find cached object.");
143: }
144:
145: if (!usedCache) {
146: getLogger().debug("Did not use cache.");
147: if (key != null) {
148: if (sourceValidity == null) {
149: sourceValidity = getSourceValidity(sourceUri);
150: }
151: if (sourceValidity != null) {
152: if (getLogger().isDebugEnabled()) {
153: getLogger().debug(
154: "Source validity is not null.");
155: }
156: } else {
157: if (getLogger().isDebugEnabled()) {
158: getLogger()
159: .debug(
160: "Source validity is null - not caching.");
161: }
162: key = null;
163: }
164: }
165:
166: value = buildObject(sourceUri, builder);
167:
168: // store the response
169: if (key != null) {
170: if (this .getLogger().isDebugEnabled()) {
171: this .getLogger().debug(
172: "Caching object [" + value
173: + "] for further requests of ["
174: + sourceUri + "].");
175: }
176: getCache().put(key,
177: new CachedObject(sourceValidity, value));
178: }
179: }
180: } catch (final SourceNotFoundException e1) {
181: throw new CachingException(e1);
182: } catch (final MalformedURLException e1) {
183: throw new CachingException(e1);
184: } catch (final IOException e1) {
185: throw new CachingException(e1);
186: } catch (final BuildException e1) {
187: throw new CachingException(e1);
188: }
189:
190: return value;
191: }
192:
193: /**
194: * Returns the input stream to read a source from.
195: * @param sourceUri The URI of the source.
196: * @param builder The input stream builder that should be used.
197: * @return An object.
198: * @throws MalformedURLException when an error occurs.
199: * @throws IOException when an error occurs.
200: * @throws SourceNotFoundException when an error occurs.
201: * @throws BuildException if an error occurs.
202: */
203: protected synchronized Object buildObject(String sourceUri,
204: InputStreamBuilder builder) throws MalformedURLException,
205: IOException, SourceNotFoundException, BuildException {
206: Object value = null;
207: Source source = null;
208: try {
209: source = getResolver().resolveURI(sourceUri);
210: if (source.exists()) {
211: InputStream stream = source.getInputStream();
212: value = builder.build(stream);
213: }
214: } finally {
215: if (source != null) {
216: getResolver().release(source);
217: }
218: }
219: return value;
220: }
221:
222: /**
223: * Returns the validity of a source.
224: * @param sourceUri The URI of the source.
225: * @return A source validity object.
226: * @throws MalformedURLException when an error occurs.
227: * @throws IOException when an error occurs.
228: */
229: protected synchronized SourceValidity getSourceValidity(
230: String sourceUri) throws MalformedURLException, IOException {
231: SourceValidity sourceValidity;
232: Source source = null;
233: try {
234: source = getResolver().resolveURI(sourceUri);
235: sourceValidity = source.getValidity();
236: } finally {
237: if (source != null) {
238: getResolver().release(source);
239: }
240: }
241: return sourceValidity;
242: }
243:
244: private ServiceManager manager;
245: private SourceResolver resolver;
246:
247: /**
248: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
249: */
250: public void service(ServiceManager _manager)
251: throws ServiceException {
252: this .manager = _manager;
253: this .resolver = (SourceResolver) _manager
254: .lookup(SourceResolver.ROLE);
255: }
256:
257: /**
258: * @see org.apache.avalon.framework.activity.Disposable#dispose()
259: */
260: public void dispose() {
261: if (getResolver() != null) {
262: getManager().release(getResolver());
263: }
264: }
265:
266: }
|