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: package org.apache.lenya.cms.cocoon.source;
019:
020: import java.io.IOException;
021: import java.net.MalformedURLException;
022: import java.util.Map;
023: import java.util.StringTokenizer;
024:
025: import org.apache.avalon.framework.context.ContextException;
026: import org.apache.avalon.framework.context.Contextualizable;
027: import org.apache.avalon.framework.logger.AbstractLogEnabled;
028: import org.apache.avalon.framework.service.ServiceException;
029: import org.apache.avalon.framework.service.ServiceManager;
030: import org.apache.avalon.framework.service.Serviceable;
031: import org.apache.cocoon.components.ContextHelper;
032: import org.apache.cocoon.environment.Request;
033: import org.apache.excalibur.source.Source;
034: import org.apache.excalibur.source.SourceFactory;
035: import org.apache.excalibur.source.SourceResolver;
036: import org.apache.excalibur.source.SourceUtil;
037: import org.apache.excalibur.source.URIAbsolutizer;
038: import org.apache.lenya.cms.module.ModuleManager;
039: import org.apache.lenya.cms.publication.DocumentFactory;
040: import org.apache.lenya.cms.publication.DocumentUtil;
041: import org.apache.lenya.cms.publication.Publication;
042: import org.apache.lenya.cms.publication.URLInformation;
043: import org.apache.lenya.cms.publication.templating.ExistingSourceResolver;
044: import org.apache.lenya.cms.publication.templating.PublicationTemplateManager;
045: import org.apache.lenya.cms.publication.templating.VisitingSourceResolver;
046:
047: /**
048: * <p>
049: * Source factory following the fallback principle.
050: * </p>
051: * <p>
052: * The ID of the current publication can be passed in the URL (<code>fallback:pub://path</code),
053: * this is necessary as a workaround for bug 40564.
054: * </p>
055: *
056: * @version $Id: FallbackSourceFactory.java 590743 2007-10-31 16:48:56Z andreas $
057: */
058: public class FallbackSourceFactory extends AbstractLogEnabled implements
059: SourceFactory, Serviceable, Contextualizable, URIAbsolutizer {
060:
061: /**
062: * Ctor.
063: */
064: public FallbackSourceFactory() {
065: super ();
066: }
067:
068: /**
069: * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, java.util.Map)
070: */
071: public Source getSource(final String location, Map parameters)
072: throws IOException, MalformedURLException {
073:
074: // Remove the protocol and the first '//'
075: int pos = location.indexOf("://");
076:
077: if (pos == -1) {
078: throw new RuntimeException("The location [" + location
079: + "] does not contain the string '://'");
080: }
081:
082: String path = location.substring(pos + 3);
083:
084: String publicationId = null;
085:
086: // extract publication ID
087: String prefix = location.substring(0, pos);
088: StringTokenizer tokens = new StringTokenizer(prefix, ":");
089: if (tokens.countTokens() > 1) {
090: tokens.nextToken();
091: publicationId = tokens.nextToken();
092: }
093:
094: // remove query string
095: int questionMarkIndex = path.indexOf("?");
096: if (questionMarkIndex > -1) {
097: path = path.substring(0, questionMarkIndex);
098: }
099:
100: if (path.length() == 0) {
101: throw new RuntimeException(
102: "The path after the protocol must not be empty!");
103: }
104:
105: if (getLogger().isDebugEnabled()) {
106: getLogger().debug("Location: [" + location + "]");
107: getLogger().debug("Path: [" + path + "]");
108: }
109:
110: PublicationTemplateManager templateManager = null;
111: SourceResolver sourceResolver = null;
112: Source source = null;
113: try {
114: sourceResolver = (SourceResolver) this .manager
115: .lookup(SourceResolver.ROLE);
116:
117: templateManager = (PublicationTemplateManager) this .manager
118: .lookup(PublicationTemplateManager.ROLE);
119:
120: Request request = ContextHelper.getRequest(this .context);
121:
122: if (publicationId == null) {
123: String webappUrl = request.getRequestURI().substring(
124: request.getContextPath().length());
125:
126: URLInformation info = new URLInformation(webappUrl);
127: publicationId = info.getPublicationId();
128: }
129:
130: DocumentFactory factory = DocumentUtil.getDocumentFactory(
131: this .manager, request);
132: if (factory.existsPublication(publicationId)) {
133: Publication pub = factory.getPublication(publicationId);
134: VisitingSourceResolver resolver = getSourceVisitor();
135: templateManager.visit(pub, path, resolver);
136: source = resolver.getSource();
137: }
138:
139: if (source == null) {
140: if (path.startsWith("lenya/modules/")) {
141: ModuleManager moduleMgr = null;
142: try {
143: moduleMgr = (ModuleManager) this .manager
144: .lookup(ModuleManager.ROLE);
145: final String moduleShortcut = path.split("/")[2];
146: String baseUri = moduleMgr
147: .getBaseURI(moduleShortcut);
148: final String modulePath = path
149: .substring(("lenya/modules/" + moduleShortcut)
150: .length());
151: source = sourceResolver.resolveURI(baseUri
152: + modulePath);
153: } finally {
154: if (moduleMgr != null) {
155: this .manager.release(moduleMgr);
156: }
157: }
158: } else {
159: String contextUri = "context://" + path;
160: source = sourceResolver.resolveURI(contextUri);
161: }
162: }
163:
164: if (getLogger().isDebugEnabled()) {
165: getLogger().debug(
166: "Resolved source: [" + source.getURI() + "]");
167: }
168:
169: } catch (Exception e) {
170: throw new RuntimeException("Resolving path [" + location
171: + "] failed: ", e);
172: } finally {
173: if (templateManager != null) {
174: this .manager.release(templateManager);
175: }
176: if (sourceResolver != null) {
177: this .manager.release(sourceResolver);
178: }
179: }
180:
181: return source;
182: }
183:
184: protected VisitingSourceResolver getSourceVisitor() {
185: return new ExistingSourceResolver();
186: }
187:
188: private org.apache.avalon.framework.context.Context context;
189:
190: /** The ServiceManager */
191: private ServiceManager manager;
192:
193: /**
194: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
195: */
196: public void service(ServiceManager _manager)
197: throws ServiceException {
198: this .manager = _manager;
199: }
200:
201: /**
202: * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
203: */
204: public void contextualize(
205: org.apache.avalon.framework.context.Context _context)
206: throws ContextException {
207: this .context = _context;
208: }
209:
210: /**
211: * @see org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source)
212: */
213: public void release(Source source) {
214: // In fact, this method should never be called as this factory
215: // returns a source object from a different factory. So that
216: // factory should release the source
217: if (null != source) {
218: if (this .getLogger().isDebugEnabled()) {
219: this .getLogger().debug(
220: "Releasing source " + source.getURI());
221: }
222: SourceResolver resolver = null;
223: try {
224: resolver = (SourceResolver) this .manager
225: .lookup(SourceResolver.ROLE);
226: resolver.release(source);
227: } catch (ServiceException ignore) {
228: // ignore the exception
229: } finally {
230: this .manager.release(resolver);
231: }
232: }
233: }
234:
235: /**
236: * @see org.apache.excalibur.source.URIAbsolutizer#absolutize(java.lang.String,
237: * java.lang.String)
238: */
239: public String absolutize(String baseURI, String location) {
240: return SourceUtil.absolutize(baseURI, location, true);
241: }
242: }
|