blob: ae6c941a3732c36e3c18b94984359a1cd145fa84 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.lenya.cms.cocoon.source;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.cocoon.components.ContextHelper;
import org.apache.cocoon.environment.Request;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceFactory;
import org.apache.excalibur.source.SourceResolver;
import org.apache.excalibur.source.SourceUtil;
import org.apache.excalibur.source.URIAbsolutizer;
import org.apache.lenya.cms.module.ModuleManager;
import org.apache.lenya.cms.publication.DocumentFactory;
import org.apache.lenya.cms.publication.DocumentUtil;
import org.apache.lenya.cms.publication.Publication;
import org.apache.lenya.cms.publication.URLInformation;
import org.apache.lenya.cms.publication.templating.ExistingSourceResolver;
import org.apache.lenya.cms.publication.templating.PublicationTemplateManager;
import org.apache.lenya.cms.publication.templating.VisitingSourceResolver;
/**
* <p>
* Source factory following the fallback principle.
* </p>
* <p>
* The ID of the current publication can be passed in the URL (<code>fallback:pub://path</code),
* this is necessary as a workaround for bug 40564.
* </p>
*
* @version $Id$
*/
public class FallbackSourceFactory extends AbstractLogEnabled implements SourceFactory,
Serviceable, Contextualizable, URIAbsolutizer {
/**
* Ctor.
*/
public FallbackSourceFactory() {
super();
}
/**
* @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, java.util.Map)
*/
public Source getSource(final String location, Map parameters) throws IOException,
MalformedURLException {
// Remove the protocol and the first '//'
int pos = location.indexOf("://");
if (pos == -1) {
throw new RuntimeException("The location [" + location
+ "] does not contain the string '://'");
}
String path = location.substring(pos + 3);
String publicationId = null;
// extract publication ID
String prefix = location.substring(0, pos);
StringTokenizer tokens = new StringTokenizer(prefix, ":");
if (tokens.countTokens() > 1) {
tokens.nextToken();
publicationId = tokens.nextToken();
}
// remove query string
int questionMarkIndex = path.indexOf("?");
if (questionMarkIndex > -1) {
path = path.substring(0, questionMarkIndex);
}
if (path.length() == 0) {
throw new RuntimeException("The path after the protocol must not be empty!");
}
if (getLogger().isDebugEnabled()) {
getLogger().debug("Location: [" + location + "]");
getLogger().debug("Path: [" + path + "]");
}
PublicationTemplateManager templateManager = null;
SourceResolver sourceResolver = null;
Source source = null;
try {
sourceResolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE);
templateManager = (PublicationTemplateManager) this.manager.lookup(PublicationTemplateManager.ROLE);
Request request = ContextHelper.getRequest(this.context);
if (publicationId == null) {
String webappUrl = request.getRequestURI().substring(request.getContextPath().length());
URLInformation info = new URLInformation(webappUrl);
publicationId = info.getPublicationId();
}
DocumentFactory factory = DocumentUtil.getDocumentFactory(this.manager, request);
if (factory.existsPublication(publicationId)) {
Publication pub = factory.getPublication(publicationId);
VisitingSourceResolver resolver = getSourceVisitor();
templateManager.visit(pub, path, resolver);
source = resolver.getSource();
}
if (source == null) {
if (path.startsWith("lenya/modules/")) {
ModuleManager moduleMgr = null;
try {
moduleMgr = (ModuleManager) this.manager.lookup(ModuleManager.ROLE);
final String moduleShortcut = path.split("/")[2];
String baseUri = moduleMgr.getBaseURI(moduleShortcut);
final String modulePath = path.substring(("lenya/modules/" + moduleShortcut).length());
source = sourceResolver.resolveURI(baseUri + modulePath);
} finally {
if (moduleMgr != null) {
this.manager.release(moduleMgr);
}
}
} else {
String contextUri = "context://" + path;
source = sourceResolver.resolveURI(contextUri);
}
}
if (getLogger().isDebugEnabled()) {
getLogger().debug("Resolved source: [" + source.getURI() + "]");
}
} catch (Exception e) {
throw new RuntimeException("Resolving path [" + location + "] failed: ", e);
} finally {
if (templateManager != null) {
this.manager.release(templateManager);
}
if (sourceResolver != null) {
this.manager.release(sourceResolver);
}
}
return source;
}
protected VisitingSourceResolver getSourceVisitor() {
return new ExistingSourceResolver();
}
private org.apache.avalon.framework.context.Context context;
/** The ServiceManager */
private ServiceManager manager;
/**
* @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
*/
public void service(ServiceManager _manager) throws ServiceException {
this.manager = _manager;
}
/**
* @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
*/
public void contextualize(org.apache.avalon.framework.context.Context _context)
throws ContextException {
this.context = _context;
}
/**
* @see org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source)
*/
public void release(Source source) {
// In fact, this method should never be called as this factory
// returns a source object from a different factory. So that
// factory should release the source
if (null != source) {
if (this.getLogger().isDebugEnabled()) {
this.getLogger().debug("Releasing source " + source.getURI());
}
SourceResolver resolver = null;
try {
resolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE);
resolver.release(source);
} catch (ServiceException ignore) {
// ignore the exception
} finally {
this.manager.release(resolver);
}
}
}
/**
* @see org.apache.excalibur.source.URIAbsolutizer#absolutize(java.lang.String,
* java.lang.String)
*/
public String absolutize(String baseURI, String location) {
return SourceUtil.absolutize(baseURI, location, true);
}
}