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.modules.input;
018:
019: import org.apache.avalon.framework.component.ComponentException;
020: import org.apache.avalon.framework.component.ComponentManager;
021: import org.apache.avalon.framework.component.Composable;
022: import org.apache.avalon.framework.configuration.Configuration;
023: import org.apache.avalon.framework.configuration.ConfigurationException;
024: import org.apache.avalon.framework.thread.ThreadSafe;
025:
026: import org.apache.excalibur.source.Source;
027: import org.apache.excalibur.source.SourceResolver;
028:
029: import java.util.Collection;
030: import java.util.Iterator;
031: import java.util.Map;
032: import java.util.TreeSet;
033: import java.util.Vector;
034:
035: /**
036: * Locate a resource in a resource tree. Any attribute name is interpreted as a
037: * URI with the last part being the resource name unless it ends with a slash.
038: * The URI is checked if the resource exists and the URI is returned. If the
039: * resource does not exist, the URI is shortened until the resource name is found
040: * and the new URI is returned. If no resource with the given name exists, null
041: * is returned.
042: *
043: * <p>A use case is to locate the closest menu file or when moving a site from
044: * a filesystem path == URL system from a httpd to Cocoon and provide similar
045: * functions to .htaccess files.</p>
046: *
047: * <p>Example: for context:/some/path/to/a/file.xml the following URIs
048: * are tested: context:/some/path/to/a/file.xml, context:/some/path/to/file.xml,
049: * context:/some/path/file.xml, context:/some/file.xml, and context:/file.xml.
050: * For the attribute name context:/some/path/foo/ tests context:/some/path/foo/,
051: * context:/some/path/, context:/some/, and context:/ are tested.</p>
052: *
053: * <p>The getAttribute() method will return the URI for the first match while
054: * getAttributeValues() will return an array of all existing paths.
055: * getAttributeNames() will return an Iterator to an empty collection.</p>
056: *
057: * @author <a href="mailto:haul@apache.org">Christian Haul</a>
058: * @version $Id: LocateResource.java 433543 2006-08-22 06:22:54Z crossley $
059: */
060: public class LocateResource extends AbstractInputModule implements
061: Composable, ThreadSafe {
062:
063: protected static final Collection col = new TreeSet();
064:
065: protected ComponentManager manager = null;
066:
067: /**
068: * Calculate the minimal length of the URL, that is the position
069: * of the first ":" if a protocol is provided or otherwise 0.
070: * @param name
071: * @return minimal length
072: */
073: protected int calculateMinLen(String name) {
074:
075: int minLen = name.indexOf(':');
076: minLen = (minLen == -1 ? 0 : minLen);
077:
078: return minLen;
079: }
080:
081: /**
082: * Remove one path element from the URL unless minimum length has
083: * been reached.
084: *
085: * @param urlstring
086: * @param minLen
087: * @return shortened URI
088: */
089: protected String shortenURI(String urlstring, int minLen) {
090:
091: int idx = urlstring.lastIndexOf('/');
092: idx = (idx <= minLen + 1) ? minLen : idx;
093: urlstring = urlstring.substring(0, idx);
094:
095: return urlstring;
096: }
097:
098: /**
099: * if the url does not end with a "/", keep the last part in
100: * order to add it later again after traversing up
101: */
102: protected String extractFilename(String urlstring) {
103:
104: String filename = "";
105: if (!urlstring.endsWith("/")) {
106: int idx = urlstring.lastIndexOf('/');
107: filename = urlstring.substring(idx);
108: }
109:
110: return filename;
111: }
112:
113: /**
114: * Locate a resource with the given URL consisting of urlstring + filename.
115: * The filename is appended each time the path is shortened. Returns the first
116: * existing occurance.
117: *
118: * @param urlstring
119: * @param filename
120: * @param minLen
121: * @return urlstring if resource was found, <code>null</code> otherwise
122: */
123: protected String locateResource(String urlstring, String filename,
124: int minLen) {
125: String sourcename = null;
126: Source src = null;
127: SourceResolver resolver = null;
128: boolean found = false;
129: try {
130: resolver = (SourceResolver) this .manager
131: .lookup(org.apache.excalibur.source.SourceResolver.ROLE);
132: while (!found && urlstring.length() > minLen) {
133: sourcename = urlstring + filename;
134: try {
135: src = resolver.resolveURI(sourcename);
136: if (src.exists())
137: found = true;
138: } catch (Exception e) {
139: if (this .getLogger().isWarnEnabled())
140: this .getLogger()
141: .warn(
142: "Exception resolving URL "
143: + sourcename, e);
144: } finally {
145: resolver.release(src);
146: }
147: if (!found) {
148: urlstring = shortenURI(urlstring, minLen);
149: }
150: }
151: } catch (ComponentException e1) {
152: if (this .getLogger().isErrorEnabled())
153: this .getLogger().error(
154: "Exception obtaining source resolver ", e1);
155: } finally {
156: if (resolver != null) {
157: this .manager.release(resolver);
158: }
159: }
160: return (found ? urlstring : null);
161: }
162:
163: /**
164: * Set the current <code>ComponentManager</code> instance used by this
165: * <code>Composable</code>.
166: */
167: public void compose(ComponentManager manager)
168: throws ComponentException {
169: this .manager = manager;
170: }
171:
172: /* (non-Javadoc)
173: * @see org.apache.cocoon.components.modules.input.InputModule#getAttribute(java.lang.String, org.apache.avalon.framework.configuration.Configuration, java.util.Map)
174: */
175: public Object getAttribute(String name, Configuration modeConf,
176: Map objectModel) throws ConfigurationException {
177:
178: String urlstring = name;
179: String filename = extractFilename(urlstring);
180: int minLen = calculateMinLen(name);
181: if (filename.length() > 0) {
182: urlstring = shortenURI(urlstring, minLen);
183: }
184:
185: String result = locateResource(urlstring, filename, minLen);
186: if (result != null) {
187: result += filename;
188: }
189: if (this .getLogger().isDebugEnabled())
190: this .getLogger().debug("located " + name + " @ " + result);
191: return result;
192: }
193:
194: /* (non-Javadoc)
195: * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeNames(org.apache.avalon.framework.configuration.Configuration, java.util.Map)
196: */
197: public Iterator getAttributeNames(Configuration modeConf,
198: Map objectModel) throws ConfigurationException {
199: // return an iterator to an empty collection
200: return LocateResource.col.iterator();
201: }
202:
203: /* (non-Javadoc)
204: * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeValues(java.lang.String, org.apache.avalon.framework.configuration.Configuration, java.util.Map)
205: */
206: public Object[] getAttributeValues(String name,
207: Configuration modeConf, Map objectModel)
208: throws ConfigurationException {
209:
210: Vector uris = null;
211: String urlstring = name;
212: String filename = extractFilename(urlstring);
213: int minLen = calculateMinLen(name);
214: if (filename.length() > 0) {
215: urlstring = shortenURI(urlstring, minLen);
216: }
217:
218: while (urlstring != null && urlstring.length() > minLen) {
219: urlstring = this
220: .locateResource(urlstring, filename, minLen);
221: if (urlstring != null) {
222: if (uris == null)
223: uris = new Vector();
224: if (this .getLogger().isDebugEnabled())
225: this .getLogger().debug(
226: "-> located " + name + " @ " + urlstring
227: + filename);
228: uris.add(urlstring + filename);
229: urlstring = shortenURI(urlstring, minLen);
230: }
231: }
232: return (uris == null ? null : uris.toArray());
233: }
234: }
|