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