- add moved class to core-cocoon git-svn-id: https://svn.apache.org/repos/asf/lenya/trunk@1034327 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.lenya.core.cocoon/pom.xml b/org.apache.lenya.core.cocoon/pom.xml index 844ce91..0b3fb15 100644 --- a/org.apache.lenya.core.cocoon/pom.xml +++ b/org.apache.lenya.core.cocoon/pom.xml
@@ -16,7 +16,14 @@ <description>Contains core cocoon components defined for Lenya</description> <dependencies> - + <dependency> + <groupId>org.apache.lenya</groupId> + <artifactId>lenya-core-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.lenya</groupId> + <artifactId>lenya-core-impl</artifactId> + </dependency> <!-- TODO : see if all theses dependencies are required --> <dependency> <groupId>org.apache.cocoon</groupId>
diff --git a/org.apache.lenya.core.cocoon/rcl.properties b/org.apache.lenya.core.cocoon/rcl.properties index 711c321..8fe3e22 100644 --- a/org.apache.lenya.core.cocoon/rcl.properties +++ b/org.apache.lenya.core.cocoon/rcl.properties
@@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -org.apache.lenya.lenya-core-cocoon-components.service%classes-dir=./target/classes \ No newline at end of file +org.apache.lenya.core.cocoon.block%classes-dir=./target/classes \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/DiscoverCheckoutAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/DiscoverCheckoutAction.java new file mode 100644 index 0000000..5996053 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/DiscoverCheckoutAction.java
@@ -0,0 +1,53 @@ +/* + * 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.acting; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.avalon.framework.parameters.Parameters; +import org.apache.cocoon.environment.Redirector; +import org.apache.cocoon.environment.SourceResolver; +import org.apache.lenya.cms.publication.Document; + + +/** + * An action that tests if a document is already checked out by a given user. + * If it isn't, a check out will be tried. + */ +public class DiscoverCheckoutAction extends RevisionControllerAction { + + /** + * @see org.apache.cocoon.acting.Action#act(org.apache.cocoon.environment.Redirector, org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters) + */ + public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String src, + Parameters parameters) throws Exception { + super.act(redirector, resolver, objectModel, src, parameters); + + HashMap actionMap = new HashMap(); + + Document document = getDocument(); + if (document.isCheckedOut()) { + actionMap.put("filename", document.getSourceURI()); + actionMap.put("user", document.getCheckoutUserId()); + return actionMap; + } + return null; + } +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/LanguageExistsAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/LanguageExistsAction.java new file mode 100644 index 0000000..b927992 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/LanguageExistsAction.java
@@ -0,0 +1,72 @@ +/* + * 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. + * + */ + +/* $Id$ */ + +package org.apache.lenya.cms.cocoon.acting; + +import java.util.Collections; +import java.util.Map; + +import org.apache.avalon.framework.parameters.Parameters; +import org.apache.cocoon.acting.AbstractAction; +import org.apache.cocoon.environment.ObjectModelHelper; +import org.apache.cocoon.environment.Redirector; +import org.apache.cocoon.environment.Request; +import org.apache.cocoon.environment.SourceResolver; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.Session; +import org.apache.lenya.util.ServletHelper; + +/** + * Action that checks if the current URL represents an existing document. + */ +public class LanguageExistsAction extends AbstractAction { + + private Repository repository; + + /** + * Check if the current URL represents an existing document. + * @return an empty <code>Map</code> if there is a version of this document for the current + * language, <code>null</code> otherwise. + * @throws Exception if an error occurs + */ + public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, + Parameters parameters) throws Exception { + + Request request = ObjectModelHelper.getRequest(objectModel); + Session session = this.repository.getSession(request); + + String url = ServletHelper.getWebappURI(request); + if (session.getUriHandler().isDocument(url)) { + return Collections.unmodifiableMap(Collections.EMPTY_MAP); + } + else { + return null; + } + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public Repository getRepository() { + return repository; + } + +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckinAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckinAction.java new file mode 100644 index 0000000..d776919 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckinAction.java
@@ -0,0 +1,87 @@ +/* + * 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. + * + */ + +/* $Id$ */ + +package org.apache.lenya.cms.cocoon.acting; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.avalon.framework.parameters.Parameters; +import org.apache.cocoon.environment.ObjectModelHelper; +import org.apache.cocoon.environment.Redirector; +import org.apache.cocoon.environment.Request; +import org.apache.cocoon.environment.SourceResolver; +import org.apache.lenya.ac.Identity; +import org.apache.lenya.cms.publication.Document; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.Session; +import org.apache.lenya.util.ServletHelper; + +/** + * Checkin document + */ +public class ReservedCheckinAction extends RevisionControllerAction { + + protected Repository repository; + + /** + * Checkin document + * @return HashMap with checkin parameters + * @see org.apache.cocoon.acting.Action#act(org.apache.cocoon.environment.Redirector, + * org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String, + * org.apache.avalon.framework.parameters.Parameters) + */ + public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String src, + Parameters parameters) throws Exception { + super.act(redirector, resolver, objectModel, src, parameters); + + try { + Request request = ObjectModelHelper.getRequest(objectModel); + Identity identity = (Identity) request.getSession().getAttribute(Identity.class.getName()); + Session session = this.repository.startSession(identity, true); + + String url = ServletHelper.getWebappURI(request); + if (session.getUriHandler().isDocument(url)) { + Document document = session.getUriHandler().getDocument(url); + if (document.isCheckedOutBySession(session.getId(), session.getIdentity().getUser().getId())) { + document.checkin(); + } + } + else { + throw new RuntimeException("The URL [" + url + "] doesn't represent a document."); + } + + } catch (final Exception e) { + getLogger().error("Could not check in node: ", e); + Map actionMap = new HashMap(); + actionMap.put("exception", "genericException"); + actionMap.put("filename", getDocument().getSourceURI()); + actionMap.put("message", e.getMessage()); + return actionMap; + } + + return null; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckoutAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckoutAction.java new file mode 100644 index 0000000..b252a48 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckoutAction.java
@@ -0,0 +1,71 @@ +/* + * 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. + * + */ + +/* $Id$ */ + +package org.apache.lenya.cms.cocoon.acting; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.avalon.framework.parameters.Parameters; +import org.apache.cocoon.environment.Redirector; +import org.apache.cocoon.environment.SourceResolver; +import org.apache.lenya.cms.publication.Document; +import org.apache.lenya.cms.publication.RepositoryException; +import org.apache.lenya.cms.publication.Session; + +/** + * Action doing reserved checkout + */ +public class ReservedCheckoutAction extends RevisionControllerAction { + + /** + * @see org.apache.cocoon.acting.Action#act(org.apache.cocoon.environment.Redirector, org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters) + */ + public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String src, + Parameters parameters) throws Exception { + super.act(redirector, resolver, objectModel, src, parameters); + + HashMap actionMap = new HashMap(); + Document doc = getDocument(); + + //check out + try { + + String username = getUsername(); + + assert doc != null; + assert username != null; + + Session session = doc.getSession(); + if (!doc.isCheckedOutBySession(session.getId(), session.getIdentity().getUser().getId())) { + doc.checkout(); + } + } catch (RepositoryException e) { + actionMap.put("exception", "genericException"); + actionMap.put("filename", doc.getSourceURI()); + actionMap.put("message", "" + e.getMessage()); + getLogger().error("The document " + doc.getSourceURI() + " couldn't be checked out: ", e); + + return actionMap; + } + + return null; + } +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckoutTestAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckoutTestAction.java new file mode 100644 index 0000000..11e0f55 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ReservedCheckoutTestAction.java
@@ -0,0 +1,74 @@ +/* + * 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. + * + */ + +/* $Id$ */ + +package org.apache.lenya.cms.cocoon.acting; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.avalon.framework.parameters.Parameters; +import org.apache.cocoon.environment.Redirector; +import org.apache.cocoon.environment.SourceResolver; +import org.apache.lenya.cms.publication.Document; +import org.apache.lenya.cms.publication.RepositoryException; + +/** + * An action that tests if a document is already checked out by a given user. If it isn't, a check + * out will be tried. + */ + +public class ReservedCheckoutTestAction extends RevisionControllerAction { + + /** + * @see org.apache.cocoon.acting.Action#act(org.apache.cocoon.environment.Redirector, + * org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String, + * org.apache.avalon.framework.parameters.Parameters) + */ + public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String src, + Parameters parameters) throws Exception { + super.act(redirector, resolver, objectModel, src, parameters); + + HashMap actionMap = new HashMap(); + + Document doc = getDocument(); + try { + + if (!doc.isCheckedOut() || !doc.getCheckoutUserId().equals(getUsername())) { + // check out + doc.checkout(); + } + } catch (RepositoryException e) { + actionMap.put("exception", "RepositoryException"); + actionMap.put("filename", doc.getSourceURI()); + + return actionMap; + } catch (Exception e) { + actionMap.put("exception", "genericException"); + actionMap.put("filename", doc.getSourceURI()); + actionMap.put("message", e.getMessage()); + getLogger().error( + ".act(): The document " + doc.getSourceURI() + " couldn't be checked out: ", + e); + + return actionMap; + } + return null; + } +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ResourceExistsAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ResourceExistsAction.java new file mode 100644 index 0000000..24fe72d --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/ResourceExistsAction.java
@@ -0,0 +1,89 @@ +/* + * 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. + * + */ + +/* $Id$ */ + +package org.apache.lenya.cms.cocoon.acting; + +import java.util.Collections; +import java.util.Map; + +import org.apache.avalon.framework.parameters.Parameters; +import org.apache.avalon.framework.thread.ThreadSafe; +import org.apache.cocoon.acting.AbstractAction; +import org.apache.cocoon.environment.Redirector; +import org.apache.cocoon.environment.SourceResolver; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.TraversableSource; + + +/** + * This action simply checks to see if a given resource exists. It checks + * whether the specified in the src attribute source exists or not. + * The action returns empty <code>Map</code> if it exists, null otherwise. + * <p>Instead of src attribute, source can be specified using + * parameter named 'url' (this is old syntax). + * <p>In order to differentiate between files and directories, the type can be specified + * using the parameter 'type' (<map:parameter name="type" value="file"/> or + * <map:parameter name="type" value="directory"/>). The parameter 'type' is optional. + * <p> + * <strong>Note:</strong> {@link org.apache.cocoon.selection.ResourceExistsSelector} + * should be preferred to this component, as the semantics of a Selector better + * match the supplied functionality. + */ +public class ResourceExistsAction extends AbstractAction implements ThreadSafe { + /** + * @see org.apache.cocoon.acting.Action#act(org.apache.cocoon.environment.Redirector, org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters) + */ + public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, + Parameters parameters) throws Exception { + String url = parameters.getParameter("url", source); + String type = parameters.getParameter("type", "resource"); + Source src = null; + + try { + src = resolver.resolveURI(url); + + if (src.exists()) { + + boolean isCollection = false; + if (src instanceof TraversableSource) { + TraversableSource traversableSource = (TraversableSource) src; + isCollection = traversableSource.isCollection(); + } + + boolean exists = type.equals("resource") + || type.equals("file") && !isCollection + || type.equals("directory") && isCollection; + + if (exists) { + getLogger().debug(type + " exists: " + src.getURI()); + return Collections.EMPTY_MAP; + } + } + getLogger().debug(".act(): Resource " + source + " as type \"" + type + + "\" does not exist"); + } finally { + if (src != null) { + resolver.release(src); + } + } + + return null; + } +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/RevisionControllerAction.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/RevisionControllerAction.java new file mode 100644 index 0000000..4528325 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/acting/RevisionControllerAction.java
@@ -0,0 +1,160 @@ +/* + * 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.acting; + +import java.util.Map; + +import javax.servlet.http.HttpSession; + +import org.apache.avalon.framework.parameters.Parameters; +import org.apache.cocoon.acting.ServiceableAction; +import org.apache.cocoon.environment.ObjectModelHelper; +import org.apache.cocoon.environment.Redirector; +import org.apache.cocoon.environment.Request; +import org.apache.cocoon.environment.SourceResolver; +import org.apache.lenya.ac.Identity; +import org.apache.lenya.ac.User; +import org.apache.lenya.cms.publication.Document; +import org.apache.lenya.cms.publication.PageEnvelope; +import org.apache.lenya.cms.publication.PageEnvelopeFactory; +import org.apache.lenya.cms.publication.Publication; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.Session; +import org.apache.lenya.cms.publication.URLInformation; +import org.apache.lenya.util.ServletHelper; + +/** + * Revision controller action. + * + * @version $Id$ + */ +public class RevisionControllerAction extends ServiceableAction { + + private String username = null; + private Repository repository; + private Document document; + + /** + * @see org.apache.cocoon.acting.Action#act(org.apache.cocoon.environment.Redirector, + * org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String, + * org.apache.avalon.framework.parameters.Parameters) + */ + public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String src, + Parameters parameters) throws Exception { + // Get request object + Request request = ObjectModelHelper.getRequest(objectModel); + + if (request == null) { + getLogger().error(".act(): No request object"); + + return null; + } + + Session repoSession = this.repository.getSession(request); + + PageEnvelope envelope = null; + String id = new URLInformation(ServletHelper.getWebappURI(request)).getPublicationId(); + Publication publication = repoSession.getPublication(id); + + Document doc = null; + + try { + envelope = PageEnvelopeFactory.getInstance().getPageEnvelope(objectModel, publication); + doc = envelope.getDocument(); + } catch (Exception e) { + getLogger().error("Resolving page envelope failed: ", e); + throw e; + } + + // Get session + HttpSession session = request.getSession(false); + + if (session == null) { + getLogger().error(".act(): No session object"); + + return null; + } + + Identity identity = (Identity) session.getAttribute(Identity.class.getName()); + getLogger().debug(".act(): Identity: " + identity); + + // FIXME: hack because of the uri for the editor bitflux. The filename + // cannot be get from + // the page-envelope + + String path = doc.getPath(); + int bx = path.lastIndexOf("-bxe"); + + if (bx > 0) { + String language = doc.getLanguage(); + + int l = path.length(); + int bxLength = "-bxe".length(); + int lang = path.lastIndexOf("_", bx); + int langLength = bx - lang; + + if (bx > 0 && bx + bxLength <= l) { + path = path.substring(0, bx) + path.substring(bx + bxLength, l); + + if (lang > 0 && langLength + lang < l) { + language = path.substring(lang + 1, lang + langLength); + path = path.substring(0, lang) + + path.substring(lang + langLength, l - bxLength); + } + } + + this.document = doc.area().getSite().getNode(path).getLink(language).getDocument(); + + } else { + this.document = doc; + } + + this.username = null; + + if (identity != null) { + User user = identity.getUser(); + if (user != null) { + this.username = user.getId(); + } + } else { + getLogger().error(".act(): No identity yet"); + } + + getLogger().debug(".act(): Username: " + this.username); + + return null; + } + + /** + * Get the node. + * @return the node + */ + protected Document getDocument() { + return this.document; + } + + /** + * Get the user name. + * @return the user name + */ + protected String getUsername() { + return this.username; + } + +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/context/ContextUtility.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/context/ContextUtility.java new file mode 100644 index 0000000..8dcb0cb --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/context/ContextUtility.java
@@ -0,0 +1,83 @@ +/* + * 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.components.context; + +import java.util.Map; + +import org.apache.avalon.framework.component.Component; +import org.apache.avalon.framework.context.Context; +import org.apache.avalon.framework.context.ContextException; +import org.apache.avalon.framework.context.Contextualizable; +import org.apache.cocoon.components.ContextHelper; +import org.apache.cocoon.environment.Request; +import org.apache.cocoon.environment.Response; +import org.apache.cocoon.util.AbstractLogEnabled; + +/** + * Utility class for getting the context, request, response and + * object model of the current request. + * @deprecated + */ +public class ContextUtility extends AbstractLogEnabled implements + Component, Contextualizable { + /** + * The component's role. + */ + public static final String ROLE = ContextUtility.class.getName(); + + protected Context context; + + + /** + * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context) + */ + public void contextualize(Context context) throws ContextException { + this.context = context; + } + + /** + * Get the context object of the current request. + * @return The context object of the current request. + */ + public Context getContext() { + return context; + } + + /** + * Get the request object of the current request. + * @return The request object of the current request. + */ + public Request getRequest() { + return ContextHelper.getRequest(context); + } + + /** + * Get the response object of the current request. + * @return The response object of the current request. + */ + public Response getResponse() { + return ContextHelper.getResponse(context); + } + + /** + * Get the object model of the current request. + * @return The object model of the current request. + */ + public Map getObjectModel() { + return ContextHelper.getObjectModel(context); + } +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/AbstractPageEnvelopeModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/AbstractPageEnvelopeModule.java new file mode 100644 index 0000000..9a6c6a0 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/AbstractPageEnvelopeModule.java
@@ -0,0 +1,98 @@ +/* + * 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. + * + */ + +/* $Id$ */ + +package org.apache.lenya.cms.cocoon.components.modules.input; + +import java.util.Map; + +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.cocoon.environment.ObjectModelHelper; +import org.apache.cocoon.environment.Request; +import org.apache.lenya.cms.publication.PageEnvelope; +import org.apache.lenya.cms.publication.PageEnvelopeFactory; +import org.apache.lenya.cms.publication.Publication; +import org.apache.lenya.cms.publication.Session; +import org.apache.lenya.util.ServletHelper; +import org.apache.lenya.utils.URLInformation; +/** + * Abstract superclass for classes which need access to the page envelope. + * + * The web application URL can be provided in the attribute name, separated by a colon (":"). + */ +public abstract class AbstractPageEnvelopeModule extends OperationModule { + + /** + * Get the the page envelope for the given objectModel. + * @param objectModel the objectModel for which the page enevelope is requested. + * @param name The attribute name. + * @return a <code>PageEnvelope</code> + * @throws ConfigurationException if the page envelope could not be instantiated. + */ + protected PageEnvelope getEnvelope(Map objectModel, String name) throws ConfigurationException { + + String webappUrl = null; + Request request = ObjectModelHelper.getRequest(objectModel); + URLInformation urlInfo = new URLInformation(); + + PageEnvelope envelope = (PageEnvelope) request.getAttribute(PageEnvelope.class.getName()); + if (envelope == null) { + + String[] snippets = name.split(":"); + if (snippets.length > 1) { + webappUrl = snippets[1]; + } else { + webappUrl = urlInfo.getWebappUrl(); + } + + if (getLogger().isDebugEnabled()) { + getLogger().debug("Resolving page envelope for URL [" + webappUrl + "]"); + } + + String contextPath = request.getContextPath(); + + try { + Session session = getSession(); + Publication pub = null; + String pubId = urlInfo.getPublicationId(); + if (pubId != null && session.existsPublication(pubId)) { + pub = session.getPublication(pubId); + } + envelope = PageEnvelopeFactory.getInstance().getPageEnvelope( + contextPath, + webappUrl, + pub); + } catch (Exception e) { + throw new ConfigurationException("Resolving page envelope failed: ", e); + } + request.setAttribute(PageEnvelope.class.getName(), envelope); + } + return envelope; + } + + /** + * @param name The original attribute name. + * @return The attribute name without URL attachment. + */ + protected String getAttributeName(String name) { + final String[] snippets = name.split(":"); + return snippets[0]; + } + +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DateConverterModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DateConverterModule.java new file mode 100644 index 0000000..583cf0c --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DateConverterModule.java
@@ -0,0 +1,64 @@ +/* + * 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.components.modules.input; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.avalon.framework.thread.ThreadSafe; +import org.apache.cocoon.components.modules.input.AbstractInputModule; + +/** + * The DateConverterModule converts a date string from one format into + * another format. + * The conversion is defined by the nested elements <src-pattern/> and + * <pattern/> of the module declaration. + * + */ +public class DateConverterModule extends AbstractInputModule implements ThreadSafe { + + public Object getAttribute(String name, Configuration modeConf, Map objectModel) throws ConfigurationException { + + String srcPattern = (String) this.settings.get("src-pattern"); + String pattern = (String) this.settings.get("pattern"); + + if (modeConf != null) { + srcPattern = modeConf.getChild("src-pattern").getValue(srcPattern); + pattern = modeConf.getChild("pattern").getValue(pattern); + } + + if (srcPattern==null) { + throw new ConfigurationException("Source date pattern not specified."); + } + if (pattern==null) { + throw new ConfigurationException("Date pattern not specified."); + } + + try { + SimpleDateFormat srcFormat = new SimpleDateFormat(srcPattern); + SimpleDateFormat format = new SimpleDateFormat(pattern); + Date date = srcFormat.parse(name); + return format.format(date); + } catch (Exception e) { + throw new ConfigurationException("Could not convert date: "+name, e); + } + } +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DocumentInfoModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DocumentInfoModule.java new file mode 100644 index 0000000..04c5444 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DocumentInfoModule.java
@@ -0,0 +1,207 @@ +/* + * 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. + * + */ + +/* $Id:$ */ + +package org.apache.lenya.cms.cocoon.components.modules.input; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.cocoon.components.modules.input.AbstractInputModule; +import org.apache.cocoon.environment.ObjectModelHelper; +import org.apache.cocoon.environment.Request; +import org.apache.lenya.cms.publication.Document; +import org.apache.lenya.cms.publication.DocumentException; +import org.apache.lenya.cms.publication.Publication; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.Session; + +/** + * Input module to get document information. + * {doc-info:{publication-id}:{area}:{uuid}:{document-language}:{property}} where {property} may be: + * <ul> + * <li><strong><code>contentLength</code></strong> - the content length (number of bytes).</li> + * <li><strong><code>expires</code></strong> - the expiration date in ISO 8601 format.</li> + * <li><strong><code>lastModified</code></strong> - the last modification date in ISO 8601 + * format.</li> + * <li><strong><code>mimeType</code></strong> - the MIME type</li> + * <li><strong><code>nodeName</code></strong> - the name of the node in the site structure</li> + * <li><strong><code>path</code></strong> - the path in the site structure (starting with a + * slash) or an empty string if the document is not referenced in the site structure.</li> + * <li><strong><code>resourceType</code></strong> - the name of the resource type</li> + * <li><strong><code>sourceExtension</code></strong> - the source extension</li> + * <li><strong><code>visibleInNav</code></strong> - <code>true</code> if the document's node + * is visible in the navigation, <code>false</code> otherwise.</li> + * <li><strong><code>webappUrl</code></strong> - the web application URL of the document or + * an empty string if the document is not referenced in the site structure.</li> + * </ul> + */ +public class DocumentInfoModule extends AbstractInputModule { + + // Input module parameters: + protected final static String PARAM_PUBLICATION_ID = "publication-id"; + protected final static String PARAM_AREA = "area"; + protected final static String PARAM_UUID = "uuid"; + protected final static String PARAM_DOCUMENT_LANGUAGE = "document-language"; + protected final static String PARAM_PROPERTY = "property"; + protected final static String PARAM_REVISION = "revision"; + protected final static int MIN_MANDATORY_PARAMS = 5; + + protected final static String UUID = "uuid"; + protected final static String LANGUAGE = "language"; + protected final static String PATH = "path"; + protected final static String NODE_NAME = "nodeName"; + protected final static String WEBAPP_URL = "webappUrl"; + protected final static String DOCUMENT_URL = "documentUrl"; + protected final static String RESOURCE_TYPE = "resourceType"; + protected final static String LAST_MODIFIED = "lastModified"; + protected final static String MIME_TYPE = "mimeType"; + protected final static String CONTENT_LENGTH = "contentLength"; + protected final static String SOURCE_EXTENSION = "sourceExtension"; + protected final static String EXPIRES = "expires"; + protected final static String VISIBLE_IN_NAVIGATION = "visibleInNav"; + + protected final static String[] PARAMS = { PARAM_PUBLICATION_ID, PARAM_AREA, PARAM_UUID, + PARAM_DOCUMENT_LANGUAGE, PARAM_PROPERTY, PARAM_REVISION }; + + protected final static String META_RESOURCE_TYPE = "resourceType"; + protected final static String META_EXPIRES = "expires"; + + protected SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + + private Repository repository; + + /** + * Parse the parameters and return a document. + * @param publicationId The publication ID. + * @param area The area. + * @param uuid The document UUID. + * @param language The document language. + * @param revision The revision. + * @param objectModel The object model. + * @return The document object created. + * @throws ConfigurationException + */ + protected Document getDocument(String publicationId, String area, String uuid, String language, + int revision, Map objectModel) throws ConfigurationException { + Document document = null; + + Request request = ObjectModelHelper.getRequest(objectModel); + + try { + Session session = this.repository.getSession(request); + Publication pub = session.getPublication(publicationId); + document = pub.getArea(area).getDocument(uuid, language, revision); + } catch (Exception e) { + throw new ConfigurationException("Error getting document [" + publicationId + ":" + + area + ":" + uuid + ":" + language + "]: " + e.getMessage(), e); + } + return document; + } + + /** + * @see org.apache.cocoon.components.modules.input.InputModule#getAttribute(java.lang.String, + * org.apache.avalon.framework.configuration.Configuration, java.util.Map) + */ + public Object getAttribute(String name, Configuration modeConf, Map objectModel) + throws ConfigurationException { + Object value = null; + + InputModuleParameters params = new InputModuleParameters(name, PARAMS, MIN_MANDATORY_PARAMS); + + try { + int rev = -1; + if (params.isParameter(PARAM_REVISION)) { + String revision = params.getParameter(PARAM_REVISION); + if (!revision.equals("")) { + rev = Integer.valueOf(revision).intValue(); + } + } + + Document document = getDocument(params.getParameter(PARAM_PUBLICATION_ID), params + .getParameter(PARAM_AREA), params.getParameter(PARAM_UUID), params + .getParameter(PARAM_DOCUMENT_LANGUAGE), rev, objectModel); + + String attribute = params.getParameter(PARAM_PROPERTY); + + if (attribute.equals(RESOURCE_TYPE)) { + value = document.getResourceType().getName(); + } else if (attribute.equals(LAST_MODIFIED)) { + value = this.dateFormat.format(new Date(document.getLastModified())); + } else if (attribute.equals(MIME_TYPE)) { + value = document.getMimeType(); + } else if (attribute.equals(CONTENT_LENGTH)) { + value = Long.toString(document.getContentLength()); + } else if (attribute.equals(SOURCE_EXTENSION)) { + value = document.getSourceExtension(); + } else if (attribute.equals(LANGUAGE)) { + value = document.getLanguage(); + } else if (attribute.equals(PATH)) { + value = document.getPath(); + } else if (attribute.equals(NODE_NAME)) { + value = document.getName(); + } else if (attribute.equals(UUID)) { + value = document.getUUID(); + } else if (attribute.equals(WEBAPP_URL)) { + value = document.getCanonicalWebappURL(); + } else if (attribute.equals(DOCUMENT_URL)) { + value = document.getCanonicalDocumentURL(); + } else if (attribute.equals(EXPIRES)) { + try { + Date expires = document.getExpires(); + value = this.dateFormat.format(expires); + } catch (DocumentException e) { + throw new ConfigurationException("Error getting expires date from document.", e); + } + } else if (attribute.equals(VISIBLE_IN_NAVIGATION)) { + value = Boolean.toString(isVisibleInNavigation(document)); + } else { + throw new ConfigurationException("Attribute '" + attribute + "' not supported [" + + name + "]"); + } + } catch (ConfigurationException e) { + throw e; + } catch (Exception e) { + throw new ConfigurationException("Error getting input module parameters.", e); + } + + return value; + } + + protected boolean isVisibleInNavigation(Document document) throws ConfigurationException { + try { + return document.getLink().getNode().isVisible(); + } catch (DocumentException e) { + throw new ConfigurationException("Obtaining navigation visibility failed [" + document + + "]: " + e.getMessage(), e); + } + + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public Repository getRepository() { + return repository; + } +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DocumentURLModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DocumentURLModule.java new file mode 100644 index 0000000..610e170 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/DocumentURLModule.java
@@ -0,0 +1,91 @@ +/* + * 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.components.modules.input; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.lenya.cms.publication.util.DocumentHelper; + +/** + * <p> + * This module constructs the document url taking into account difference in the language .version + * being created and used. + * </p> + * <p> + * Example: + * <code>{document-url:{page-envelope:area}:{page-envelope:document-uuid}:{page-envelope:document-language}}</code> + * </p> + * @version $Id$ + */ +public class DocumentURLModule extends AbstractPageEnvelopeModule { + + /** + * @see org.apache.cocoon.components.modules.input.InputModule#getAttribute(java.lang.String, + * org.apache.avalon.framework.configuration.Configuration, java.util.Map) + */ + public Object getAttribute(String name, Configuration modeConf, Map objectModel) + throws ConfigurationException { + + String url; + + final String[] attributes = name.split(":"); + + if (attributes.length < 3) { + throw new ConfigurationException("Invalid number of parameters: " + attributes.length + + ". Expected 3 (area, document-uuid, language)"); + } + + final String area = attributes[0]; + final String uuid = attributes[1]; + final String language = attributes[2]; + + try { + DocumentHelper helper = new DocumentHelper(objectModel); + url = helper.getDocumentUrl(uuid, area, language); + } catch (Exception e) { + throw new ConfigurationException("Resolving attribute [" + name + "] failed: ", e); + } + + return url; + } + + /** + * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeNames(org.apache.avalon.framework.configuration.Configuration, + * java.util.Map) + */ + public Iterator getAttributeNames(Configuration modeConf, Map objectModel) + throws ConfigurationException { + return Collections.EMPTY_SET.iterator(); + } + + /** + * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeValues(java.lang.String, + * org.apache.avalon.framework.configuration.Configuration, java.util.Map) + */ + public Object[] getAttributeValues(String name, Configuration modeConf, Map objectModel) + throws ConfigurationException { + Object[] objects = { getAttribute(name, modeConf, objectModel) }; + return objects; + } + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/FallbackModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/FallbackModule.java new file mode 100644 index 0000000..d09133f --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/FallbackModule.java
@@ -0,0 +1,145 @@ +/* + * 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.components.modules.input; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.cocoon.components.modules.input.AbstractInputModule; +import org.apache.cocoon.environment.ObjectModelHelper; +import org.apache.cocoon.environment.Request; +import org.apache.commons.lang.Validate; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.SourceResolver; +import org.apache.excalibur.store.impl.MRUMemoryStore; +import org.apache.lenya.cms.cocoon.source.FallbackSourceFactory; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.Session; +import org.apache.lenya.cms.publication.URLInformation; +import org.apache.lenya.util.ServletHelper; + +/** + * <p> + * This module returns the actual source URI of a fallback:// source. The protocol (fallback, + * template-fallback, ...) is configurable via the <em>protocol</em> parameter. + * </p> + */ +public class FallbackModule extends AbstractInputModule { + + private String protocol; + protected MRUMemoryStore store; + private Repository repository; + private SourceResolver resolver; + private static Boolean useCache = null; + + public void setRepository(Repository repo) { + this.repository = repo; + } + + protected boolean useCache() { + return this.store != null; + } + + public void setStore(MRUMemoryStore store) { + Validate.notNull(store); + this.store = store; + } + + protected MRUMemoryStore getStore() { + return this.store; + } + + protected String getPublicationId(Map objectModel) { + Request request = ObjectModelHelper.getRequest(objectModel); + String webappUri = ServletHelper.getWebappURI(request); + URLInformation info = new URLInformation(webappUri); + String pubId = null; + try { + Session session = this.repository.getSession(request); + String pubIdCandidate = info.getPublicationId(); + if (pubIdCandidate != null && session.existsPublication(pubIdCandidate)) { + pubId = pubIdCandidate; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return pubId; + } + + public Object getAttribute(String name, Configuration modeConf, Map objectModel) + throws ConfigurationException { + String uri; + String fallbackUri = getFallbackUri(name); + if (useCache()) { + final String pubId = getPublicationId(objectModel); + String cacheKey = FallbackSourceFactory.getCacheKey(pubId, fallbackUri); + MRUMemoryStore store = getStore(); + if (store.containsKey(cacheKey)) { + uri = (String) store.get(cacheKey); + } else { + uri = resolveSourceUri(name); + } + } else { + uri = resolveSourceUri(name); + } + return uri; + } + + protected String resolveSourceUri(String name) throws ConfigurationException { + Source source = null; + try { + source = this.resolver.resolveURI(getFallbackUri(name)); + return source.getURI(); + } catch (Exception e) { + throw new ConfigurationException("Resolving fallback source [" + name + "] failed: ", e); + } finally { + if (source != null) { + this.resolver.release(source); + } + } + } + + protected String getFallbackUri(String name) { + return this.protocol + "://" + name; + } + + public Iterator getAttributeNames(Configuration modeConf, Map objectModel) + throws ConfigurationException { + return Collections.EMPTY_SET.iterator(); + } + + public Object[] getAttributeValues(String name, Configuration modeConf, Map objectModel) + throws ConfigurationException { + Object[] objects = { getAttribute(name, modeConf, objectModel) }; + return objects; + } + + public void setProtocol(String protocol) { + Validate.notNull(protocol); + this.protocol = protocol; + } + + public void setSourceResolver(SourceResolver resolver) { + this.resolver = resolver; + } + +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/InputModuleParameters.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/InputModuleParameters.java new file mode 100644 index 0000000..2ab073d --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/InputModuleParameters.java
@@ -0,0 +1,116 @@ +/* + * 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. + * + */ + +/* $Id:$ */ + +package org.apache.lenya.cms.cocoon.components.modules.input; + +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.avalon.framework.parameters.ParameterException; +import org.apache.avalon.framework.parameters.Parameters; + +/** + * Input module parameter class. + * Parses input module parameters and offers accessors for the parameters. + * The parameters are expected to be separated by ":". + */ +public class InputModuleParameters { + + protected final static char PARAM_SEPARATOR = ':'; + + protected Parameters params; + protected final String[] paramList; + + /** + * Parse input module parameters. + * @param parameters Parameter to be parsed. + * @param paramList List of expected parameters. + * @param minParams Minimum number of parameters expected. + * @throws ConfigurationException if there is an error parsing the parameters. + */ + public InputModuleParameters(String parameters, final String[] paramList, int minParams) + throws ConfigurationException + { + params = new Parameters(); + this.paramList = (String[])paramList.clone(); + parseParameters(parameters, minParams); + } + + /** + * Parse parameters according to the parameter list passed. + * @param parameters + * @param minParams Minimum number of parameters. + * @return Parameters object initialized with parsed parameters. + * @throws ConfigurationException + */ + protected Parameters parseParameters(String parameters, int minParams) + throws ConfigurationException + { + // Parse parameters + int start = 0; + int end = parameters.indexOf(PARAM_SEPARATOR); + for (int i=0; i<paramList.length; i++) { + if (end != -1) { + String paramToken = parameters.substring(start, end); + params.setParameter(paramList[i], paramToken); + start = end+1; + end = parameters.indexOf(PARAM_SEPARATOR, start+1); + } else { + if ((i+1) < minParams) { + // A mandatory parameter is missing. + throw new ConfigurationException("Error parsing parameters: mandatory parameter '" + + paramList[i] + "' not found [" + parameters + "]"); + } else if (i == 0) { + // Zero or one parameter passed. + if (parameters.length() != 0) { + params.setParameter(paramList[i], parameters); + } + break; + } else { + // All parameters parsed except the last one. + String paramToken = parameters.substring(start); + if (paramToken.length() != 0) { + params.setParameter(paramList[i], paramToken); + } + break; + } + } + } + return params; + } + + /** + * Get a parameter. + * @param param Name of requested parameter. + * @return Requested parameter. + * @throws ParameterException if the specified parameter cannot be found + */ + public String getParameter(String param) throws ParameterException + { + return params.getParameter(param); + } + + /** + * Does a parameter with given name exists? + * @param param Parameter name. + * @return True if parameters exists, otherwise false. + */ + public boolean isParameter(String param) { + return params.isParameter(param); + } +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/OperationModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/OperationModule.java new file mode 100644 index 0000000..b90d45a --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/OperationModule.java
@@ -0,0 +1,56 @@ +/* + * 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.components.modules.input; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.cocoon.components.modules.input.AbstractInputModule; +import org.apache.cocoon.processing.ProcessInfoProvider; +import org.apache.cocoon.spring.configurator.WebAppContextUtils; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.Session; + +/** + * Super class for operation-based input modules. + * + * @version $Id$ + */ +public class OperationModule extends AbstractInputModule { + + private Repository repository; + private org.apache.lenya.cms.publication.Session session; + + protected Session getSession() { + if (this.session == null) { + ProcessInfoProvider processInfo = (ProcessInfoProvider) WebAppContextUtils + .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE); + HttpServletRequest request = processInfo.getRequest(); + this.session = this.getRepository().getSession(request); + } + return this.session; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public Repository getRepository() { + return repository; + } + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/PageEnvelopeModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/PageEnvelopeModule.java new file mode 100644 index 0000000..407a1b6 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/PageEnvelopeModule.java
@@ -0,0 +1,196 @@ +/* + * 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. + * + */ + +/* $Id$ */ + +package org.apache.lenya.cms.cocoon.components.modules.input; + +import java.net.MalformedURLException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.Iterator; +import java.util.Map; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.commons.lang.StringUtils; +import org.apache.lenya.cms.publication.Document; +import org.apache.lenya.cms.publication.DocumentLocator; +import org.apache.lenya.cms.publication.PageEnvelope; +import org.apache.lenya.cms.publication.Publication; +import org.apache.lenya.cms.publication.ResourceType; +import org.apache.lenya.cms.site.SiteException; +import org.apache.lenya.utils.URLInformation; + +/** + * Input module wrapping the page envelope. This module provides publication + * related information such as document-uuid, area, publication-id. + * + * @see org.apache.lenya.cms.publication.PageEnvelope + * @deprecated use DocumentInfoModule instead. + */ +public class PageEnvelopeModule extends AbstractPageEnvelopeModule { + + protected static final String URI_PARAMETER_DOCTYPE = "doctype"; + + /** + * @see org.apache.cocoon.components.modules.input.InputModule#getAttribute(java.lang.String, + * org.apache.avalon.framework.configuration.Configuration, + * java.util.Map) + */ + public Object getAttribute(final String attributeName, Configuration modeConf, Map objectModel) + throws ConfigurationException { + + final String name = getAttributeName(attributeName); + + if (!Arrays.asList(PageEnvelope.PARAMETER_NAMES).contains(name)) { + throw new ConfigurationException("The attribute [" + name + "] is not supported!"); + } + + PageEnvelope envelope = getEnvelope(objectModel, attributeName); + Object value = null; + + try { + if (name.equals(PageEnvelope.AREA)) { + value = envelope.getArea(); + } else if (name.equals(PageEnvelope.CONTEXT)) { + value = envelope.getContext(); + } else if (name.equals(PageEnvelope.IS_PUBLICATION)) { + value = Boolean.toString(envelope.getPublication() != null); + } else if (name.equals(PageEnvelope.PUBLICATION_ID)) { + Publication pub = envelope.getPublication(); + value = pub != null ? pub.getId() : ""; + } else if (name.equals(PageEnvelope.PUBLICATION)) { + value = envelope.getPublication(); + } else if (name.equals(PageEnvelope.PUBLICATION_LANGUAGES_CSV)) { + value = StringUtils.join(envelope.getPublication().getLanguages(), ','); + } else if (name.equals(PageEnvelope.DEFAULT_LANGUAGE)) { + value = envelope.getPublication().getDefaultLanguage(); + } else if (name.equals(PageEnvelope.LANGUAGE)) { + value = envelope.getLanguage(); + } else if (name.equals(PageEnvelope.BREADCRUMB_PREFIX)) { + value = envelope.getPublication().getBreadcrumbPrefix(); + } else if (name.equals(PageEnvelope.DOCUMENT_PATH)) { + value = getPath(envelope, objectModel); + } else { + Document document = envelope.getDocument(); + if (document != null) { + if (name.equals(PageEnvelope.DOCUMENT)) { + value = document; + } else if (name.equals(PageEnvelope.DOCUMENT_ID)) { + getLogger().warn( + "This attribute [ " + name + " ] is deprecated." + + " Use document-path or document-uuid instead!"); + value = document.getUUID(); + } else if (name.equals(PageEnvelope.DOCUMENT_PARENT)) { + value = document.getLocator().getParent().getPath(); + } else if (name.equals(PageEnvelope.DOCUMENT_NAME)) { + value = document.getName(); + } else if (name.equals(PageEnvelope.DOCUMENT_LABEL)) { + value = document.getLink().getLabel(); + } else if (name.equals(PageEnvelope.DOCUMENT_URL)) { + value = document.getCanonicalDocumentURL(); + } else if (name.equals(PageEnvelope.DOCUMENT_URL_WITHOUT_LANGUAGE)) { + value = document.getCanonicalWebappURL(); + } else if (name.equals(PageEnvelope.DOCUMENT_EXTENSION)) { + value = document.getExtension(); + } else if (name.equals(PageEnvelope.DOCUMENT_SOURCE_EXTENSION)) { + value = document.getSourceExtension(); + } else if (name.equals(PageEnvelope.DOCUMENT_UUID)) { + value = document.getUUID(); + } else if (name.equals(PageEnvelope.DOCUMENT_LANGUAGE)) { + value = document.getLanguage(); + } else if (name.equals(PageEnvelope.DOCUMENT_LANGUAGES)) { + value = document.getLanguages(); + } else if (name.equals(PageEnvelope.DOCUMENT_LANGUAGES_CSV)) { + value = StringUtils.join(document.getLanguages(), ','); + } else if (name.equals(PageEnvelope.DOCUMENT_LASTMODIFIED)) { + Date date = new Date(document.getLastModified()); + value = new SimpleDateFormat(DATE_FORMAT).format(date); + } else if (name.equals(PageEnvelope.DOCUMENT_MIME_TYPE)) { + value = document.getMimeType(); + } else if (name.equals(PageEnvelope.DOCUMENT_TYPE)) { + ResourceType resourceType = document.getResourceType(); + if (resourceType == null) { + value = null; + } else { + value = resourceType.getName(); + } + } + } + } + } catch (final Exception e) { + throw new ConfigurationException("Getting attribute for name [" + name + "] failed: ", + e); + } + + if (getLogger().isDebugEnabled()) { + getLogger().debug("Returning [" + name + "] = [" + value + "]"); + } + + return value; + } + + protected String getPath(PageEnvelope envelope, Map objectModel) throws SiteException { + String path; + Document doc = envelope.getDocument(); + if (doc == null) { + Publication pub = envelope.getPublication(); + URLInformation info = new URLInformation(); + String url = info.getWebappUrl(); + DocumentLocator loc; + try { + loc = pub.getDocumentBuilder().getLocator(pub.getSession(), url); + } catch (MalformedURLException e) { + throw new SiteException(e); + } + path = loc.getPath(); + } else { + path = doc.getLocator().getPath(); + } + return path; + } + + /** + * <code>DATE_FORMAT</code> The date format + */ + public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss Z"; + + /** + * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeNames(org.apache.avalon.framework.configuration.Configuration, + * java.util.Map) + */ + public Iterator getAttributeNames(Configuration modeConf, Map objectModel) + throws ConfigurationException { + return Arrays.asList(PageEnvelope.PARAMETER_NAMES).iterator(); + } + + /** + * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeValues(java.lang.String, + * org.apache.avalon.framework.configuration.Configuration, + * java.util.Map) + */ + public Object[] getAttributeValues(String name, Configuration modeConf, Map objectModel) + throws ConfigurationException { + Object[] objects = { getAttribute(name, modeConf, objectModel) }; + + return objects; + } + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/ResourceExistsModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/ResourceExistsModule.java new file mode 100644 index 0000000..c40ab46 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/ResourceExistsModule.java
@@ -0,0 +1,109 @@ +/* + * 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.components.modules.input; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +import org.apache.avalon.framework.activity.Disposable; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +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.modules.input.AbstractInputModule; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.SourceNotFoundException; +import org.apache.excalibur.source.SourceResolver; + +/** + * Checks if a certain resource exists and returns either the string "true" or "false". + * @version $Id$ + */ +public class ResourceExistsModule extends AbstractInputModule implements Serviceable, Disposable { + + /** + * @see org.apache.cocoon.components.modules.input.InputModule#getAttribute(java.lang.String, + * org.apache.avalon.framework.configuration.Configuration, java.util.Map) + */ + public Object getAttribute(String name, Configuration modeConf, Map objectModel) + throws ConfigurationException { + + String resourceURI = name; + + Source source = null; + boolean exists = false; + try { + source = this.resolver.resolveURI(resourceURI); + exists = source.exists(); + } catch (SourceNotFoundException e) { + exists = false; + } catch (Exception e) { + getLogger().warn("Exception resolving resource [" + resourceURI + "]", e); + exists = false; + } finally { + if (source != null) { + this.resolver.release(source); + } + } + + return Boolean.toString(exists); + } + + /** + * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeNames(org.apache.avalon.framework.configuration.Configuration, + * java.util.Map) + */ + public Iterator getAttributeNames(Configuration modeConf, Map objectModel) + throws ConfigurationException { + return Collections.EMPTY_SET.iterator(); + } + + private ServiceManager manager; + private SourceResolver resolver; + + /** + * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) + */ + public void service(ServiceManager _manager) throws ServiceException { + this.manager = _manager; + this.resolver = (SourceResolver) _manager.lookup(SourceResolver.ROLE); + } + + /** + * @see org.apache.avalon.framework.activity.Disposable#dispose() + */ + public void dispose() { + super.dispose(); + this.manager.release(this.resolver); + this.resolver = null; + this.manager = null; + } + + /** + * @see org.apache.cocoon.components.modules.input.InputModule#getAttributeValues(java.lang.String, + * org.apache.avalon.framework.configuration.Configuration, java.util.Map) + */ + public Object[] getAttributeValues(String name, Configuration modeConf, Map objectModel) + throws ConfigurationException { + Object result = this.getAttribute(name, modeConf, objectModel); + return (result == null ? null : new Object[] { result }); + } + +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/ResourceTypeModule.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/ResourceTypeModule.java new file mode 100644 index 0000000..c80abdf --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/components/modules/input/ResourceTypeModule.java
@@ -0,0 +1,166 @@ +/* + * 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.components.modules.input; + +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.Map; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.cocoon.components.modules.input.AbstractInputModule; +import org.apache.cocoon.environment.ObjectModelHelper; +import org.apache.cocoon.environment.Request; +import org.apache.commons.lang.StringUtils; +import org.apache.lenya.cms.publication.Document; +import org.apache.lenya.cms.publication.Publication; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.ResourceType; +import org.apache.lenya.cms.publication.ResourceTypeResolver; +import org.apache.lenya.cms.publication.Session; +import org.apache.lenya.util.ServletHelper; + +/** + * <p> + * Resource type module. + * </p> + * <p> + * The syntax is either <code>{resource-type:<attribute>}</code> (which uses the resource type + * of the currenlty requested document) or + * <code>{resource-type:<name>:<attribute>}</code> (which allows to access an arbitrary + * resource type). + * </p> + * <p> + * Attributes: + * </p> + * <ul> + * <li><strong><code>expires</code></strong> - the expiration date in RFC 822/1123 format, see + * {@link org.apache.lenya.cms.publication.ResourceType#getExpires()}</li> + * <li><strong><code>schemaUri</code></strong> - see {@link org.apache.lenya.xml.Schema#getURI()}</li> + * <li><strong><code>httpSchemaUri</code></strong> - the URI to request the schema over HTTP, + * without Proxy and context (use {proxy:} around it).</li> + * <li><strong><code>supportsFormat:{format}</code></strong> - true if the resource type supports + * this format, false otherwise</li> + * </ul> + */ +public class ResourceTypeModule extends AbstractInputModule { + + protected static final String SCHEMA_URI = "schemaUri"; + protected static final String HTTP_SCHEMA_URI = "httpSchemaUri"; + protected static final String EXPIRES = "expires"; + protected static final String SUPPORTS_FORMAT = "supportsFormat"; + + private Repository repository; + private ResourceTypeResolver resourceTypeResolver; + + public Object getAttribute(String name, Configuration modeConf, Map objectModel) + throws ConfigurationException { + Object value = null; + + try { + Request request = ObjectModelHelper.getRequest(objectModel); + Session session = this.repository.getSession(request); + + ResourceType resourceType; + Publication pub = null; + String attribute; + + String[] steps = name.split(":"); + if (steps.length == 1) { + String webappUrl = ServletHelper.getWebappURI(request); + Document document = session.getUriHandler().getDocument(webappUrl); + pub = document.getPublication(); + + attribute = name; + resourceType = document.getResourceType(); + } else { + attribute = steps[1]; + String resourceTypeName = steps[0]; + resourceType = getResourceTypeResolver().getResourceType(resourceTypeName); + } + + if (attribute.startsWith("format-")) { + String[] formatSteps = name.split("-"); + String format = formatSteps[1]; + value = resourceType.getFormatURI(format); + } else if (attribute.equals(SCHEMA_URI)) { + value = resourceType.getSchema().getURI(); + } else if (attribute.equals(HTTP_SCHEMA_URI)) { + String uri = resourceType.getSchema().getURI(); + value = transformFallbackUriToHttp(pub.getId(), uri); + } else if (attribute.equals(EXPIRES)) { + Date expires = resourceType.getExpires(); + SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy kk:mm:ss zzz"); + value = sdf.format(expires); + } else if (attribute.equals(SUPPORTS_FORMAT)) { + String format = steps[steps.length - 1]; + String[] formats = resourceType.getFormats(); + return Boolean.toString(Arrays.asList(formats).contains(format)); + } else { + throw new ConfigurationException("Attribute [" + name + "] not supported!"); + } + + } catch (Exception e) { + throw new ConfigurationException("Resolving attribute [" + name + "] failed: ", e); + } + + return value; + } + + /** + * Transforms a fallback URI for resources into a HTTP URL. + * + * Currently only supports module urls: + * + * fallback://lenya/modules/foo/resources/schemas/bar.rng -> + * prefix/pubid/modules/foo/schemas/bar.rng + * + * FIXME: allow other kind of fallback URIs + * + * @param pubid publication id of the current document + * @param prefix prefix which will be prepended to the resulting URL + * @param uri fallback uri, must start with fallback:// + * @return A string. + * @throws ConfigurationException + */ + protected String transformFallbackUriToHttp(String pubid, String uri) + throws ConfigurationException { + if (uri.startsWith("fallback://lenya/modules/")) { + String path = StringUtils.substringAfter(uri, "fallback://lenya/modules/"); + String module = StringUtils.substringBefore(path, "/"); + path = StringUtils.substringAfter(path, module + "/resources"); + return "/" + pubid + "/modules/" + module + path; + } else { + throw new ConfigurationException("Don't know how to create HTTP URL from : " + uri); + } + } + + public void setResourceTypeResolver(ResourceTypeResolver resourceTypeResolver) { + this.resourceTypeResolver = resourceTypeResolver; + } + + public ResourceTypeResolver getResourceTypeResolver() { + return resourceTypeResolver; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/flow/FlowHelper.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/flow/FlowHelper.java new file mode 100644 index 0000000..ffb55fc --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/flow/FlowHelper.java
@@ -0,0 +1,107 @@ +/* + * 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.flow; + +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon; +import org.apache.lenya.ac.AccessControlException; +import org.apache.lenya.cms.publication.PageEnvelope; +import org.apache.lenya.cms.publication.PageEnvelopeException; +import org.apache.lenya.cms.publication.util.DocumentHelper; +import org.apache.lenya.workflow.WorkflowException; + +/** + * Flowscript utility class. + * @version $Id$ + */ +public interface FlowHelper { + + /** + * The Avalon Role. + */ + String ROLE = FlowHelper.class.getName(); + + /** + * Returns the current page envelope. + * @param cocoon The FOM_Cocoon object. + * @return A page envelope. + * @throws PageEnvelopeException when something went wrong. + */ + PageEnvelope getPageEnvelope(FOM_Cocoon cocoon) throws PageEnvelopeException; + + /** + * Returns the request URI of the current request. + * @param cocoon The FOM_Cocoon object. + * @return A string. + */ + String getRequestURI(FOM_Cocoon cocoon); + + /** + * Returns the request object of the current request. + * @param cocoon The FOM_Cocoon object. + * @return A request object. + */ + HttpServletRequest getRequest(FOM_Cocoon cocoon); + + /** + * Returns the Cocoon Object Model + * @param cocoon The Flow Object Model of Cocoon + * @return The object model + */ + Map getObjectModel(FOM_Cocoon cocoon); + + /** + * Returns a DocumentHelper instance. + * @param cocoon The Flow Object Model of Cocoon + * @return The document helper + * @see DocumentHelper + */ + DocumentHelper getDocumentHelper(FOM_Cocoon cocoon); + + /** + * Resolves the request parameter value for a specific name. The parameter names are encoded as + * <code>{name}:{value}.{axis}</code>. This is a workaround for the <input type="image"/> + * bug in Internet Explorer. + * @param cocoon The FOM_Cocoon object. + * @param parameterName The request parameter name. + * @return A string. + */ + String getImageParameterValue(FOM_Cocoon cocoon, String parameterName); + + /** + * Trigger a workflow event for the document associated with the current PageEnvelope. + * @param cocoon The Cocoon Flow Object Model + * @param event The name of the workflow event to trigger. + * @throws WorkflowException If an workflow error occurs + * @throws PageEnvelopeException Page envelope can not operate properly. + * @throws AccessControlException If an access control violation occurs. + */ + void triggerWorkflow(FOM_Cocoon cocoon, String event) throws WorkflowException, + PageEnvelopeException, AccessControlException; + + /** + * Checkis in the current document from the PageEnvelope context. + * @param cocoon The Cocoon Flow Object Model + * @param backup Wether a new revision should be created. + * @throws Exception + */ + void reservedCheckIn(FOM_Cocoon cocoon, boolean backup) throws Exception; +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/flow/FlowHelperImpl.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/flow/FlowHelperImpl.java new file mode 100644 index 0000000..3cea2a9 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/flow/FlowHelperImpl.java
@@ -0,0 +1,152 @@ +/* + * 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. + * + */ + +/* $Id$ */ + +package org.apache.lenya.cms.cocoon.flow; + +import java.util.Enumeration; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon; +import org.apache.cocoon.environment.Request; +import org.apache.cocoon.util.AbstractLogEnabled; +import org.apache.lenya.ac.AccessControlException; +import org.apache.lenya.cms.publication.Document; +import org.apache.lenya.cms.publication.PageEnvelope; +import org.apache.lenya.cms.publication.PageEnvelopeException; +import org.apache.lenya.cms.publication.PageEnvelopeFactory; +import org.apache.lenya.cms.publication.Publication; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.Session; +import org.apache.lenya.cms.publication.URLInformation; +import org.apache.lenya.cms.publication.util.DocumentHelper; +import org.apache.lenya.cms.workflow.WorkflowUtil; +import org.apache.lenya.util.ServletHelper; +import org.apache.lenya.workflow.WorkflowException; + +/** + * Flowscript utility class. The FOM_Cocoon object is not passed in the constructor to avoid errors. + * This way, not the initial, but the current FOM_Cocoon object is used by the methods. + */ +public class FlowHelperImpl extends AbstractLogEnabled implements FlowHelper { + + private Repository repository; + + public void setRepository(Repository repository) { + this.repository = repository; + } + + /** + * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#getPageEnvelope(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon) + */ + public PageEnvelope getPageEnvelope(FOM_Cocoon cocoon) throws PageEnvelopeException { + HttpServletRequest request = getRequest(cocoon); + try { + Session session = this.repository.getSession(request); + PageEnvelopeFactory factory = PageEnvelopeFactory.getInstance(); + URLInformation info = new URLInformation(ServletHelper.getWebappURI(request)); + Publication publication = session.getPublication(info.getPublicationId()); + return factory.getPageEnvelope(cocoon.getObjectModel(), publication); + } catch (Exception e) { + throw new PageEnvelopeException(e); + } + } + + /** + * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#getRequestURI(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon) + */ + public String getRequestURI(FOM_Cocoon cocoon) { + return cocoon.getRequest().getRequestURI(); + } + + /** + * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#getRequest(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon) + */ + public HttpServletRequest getRequest(FOM_Cocoon cocoon) { + return cocoon.getRequest(); + } + + /** + * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#getObjectModel(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon) + */ + public Map getObjectModel(FOM_Cocoon cocoon) { + return cocoon.getObjectModel(); + } + + /** + * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#getDocumentHelper(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon) + */ + public DocumentHelper getDocumentHelper(FOM_Cocoon cocoon) { + return new DocumentHelper(cocoon.getObjectModel()); + } + + /** + * <code>SEPARATOR</code> The separator + */ + public static final String SEPARATOR = ":"; + + /** + * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#getImageParameterValue(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon, + * java.lang.String) + */ + public String getImageParameterValue(FOM_Cocoon cocoon, String parameterName) { + + getLogger().debug("Resolving parameter value for name [" + parameterName + "]"); + + Request request = cocoon.getRequest(); + String value = request.getParameter(parameterName); + + if (value == null) { + String prefix = parameterName + SEPARATOR; + Enumeration e = request.getParameterNames(); + while (e.hasMoreElements() && value == null) { + String name = (String) e.nextElement(); + if (name.startsWith(prefix)) { + getLogger().debug("Complete parameter name: [" + name + "]"); + value = name.substring(prefix.length(), name.length() - 2); + getLogger().debug("Resolved value: [" + value + "]"); + } + } + } + + return value; + } + + /** + * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#triggerWorkflow(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon, + * java.lang.String) + */ + public void triggerWorkflow(FOM_Cocoon cocoon, String event) throws WorkflowException, + PageEnvelopeException, AccessControlException { + Document document = getPageEnvelope(cocoon).getDocument(); + WorkflowUtil.invoke(document, event); + } + + /** + * @see org.apache.lenya.cms.cocoon.flow.FlowHelper#reservedCheckIn(org.apache.cocoon.components.flow.javascript.fom.FOM_Cocoon, + * boolean) + */ + public void reservedCheckIn(FOM_Cocoon cocoon, boolean backup) throws Exception { + final PageEnvelope pageEnvelope = getPageEnvelope(cocoon); + pageEnvelope.getDocument().checkin(); + } + +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/selection/LastModSourceSelector.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/selection/LastModSourceSelector.java new file mode 100644 index 0000000..fcafe85 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/selection/LastModSourceSelector.java
@@ -0,0 +1,103 @@ +/* + * 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.selection; + +import org.apache.cocoon.selection.Selector; +import org.apache.cocoon.util.AbstractLogEnabled; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.SourceNotFoundException; +import org.apache.excalibur.source.SourceResolver; +import org.apache.avalon.framework.activity.Disposable; +import org.apache.avalon.framework.parameters.Parameters; +import org.apache.avalon.framework.service.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.avalon.framework.service.Serviceable; +import org.apache.avalon.framework.thread.ThreadSafe; + +import java.util.Map; + +/** + * <p> + * Last Modified Source Selector. + * </p> + * + * <pre> + * <map:selector name="last-mod" src="org.apache.lenya.cms.cocoon.selection.LastModSourceSelector"/> + * + * <map:select type="last-mod"> + * <map:parameter name="compare-to" value="{sourceToCompareTo}"/> + * + * <map:when test="cachedsource"> + * <!-- executes iff cachedsource last-modified > courceToCompareTo last-modified --> + * <map:read src="{cachedsource}" mime-type="text/xml; charset=utf-8"/> + * </map:when> + * <map:otherwise> + * <map:read src="{sourceToCompareTo}" mime-type="text/xml; charset=utf-8"/> + * </map:otherwise> + * </map:select> + * </pre> + */ + +public class LastModSourceSelector extends AbstractLogEnabled + implements ThreadSafe, Serviceable, Disposable, Selector { + + private ServiceManager manager; + private SourceResolver resolver; + private Source source = null; + private Source compare = null; + + public void service(ServiceManager manager) throws ServiceException { + this.manager = manager; + this.resolver = (SourceResolver)manager.lookup(SourceResolver.ROLE); + } + + public void dispose() { + if (null != this.source) { + resolver.release(this.source); + this.source = null; + } + if (null != this.compare) { + resolver.release(this.compare); + this.compare = null; + } + this.manager.release(this.resolver); + this.resolver = null; + this.manager = null; + } + + public boolean select(String expression, Map objectModel, Parameters parameters) { + String sourceToCompare = parameters.getParameter("compare-to",null); + String compareToSource = expression; + long sourceModDate = 0; + long compareModDate = 0; + try { + source = resolver.resolveURI(sourceToCompare); + sourceModDate = source.getLastModified(); + compare = resolver.resolveURI(compareToSource); + compareModDate = compare.getLastModified(); + } catch (SourceNotFoundException e) { + return false; + } catch (Exception e) { + getLogger().warn("Exception resolving resource ", e); + return false; + } + boolean isNewer = (compareModDate > sourceModDate); + + return (sourceToCompare != null && isNewer); + + } +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/serialization/LinkSerializer.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/serialization/LinkSerializer.java new file mode 100644 index 0000000..9a41a81 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/serialization/LinkSerializer.java
@@ -0,0 +1,106 @@ +/* + * 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.serialization; + +import org.apache.cocoon.serialization.Serializer; +import org.apache.cocoon.Constants; +import org.apache.cocoon.xml.xlink.ExtendedXLinkPipe; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * Link serializer. + * TODO: add meaningful javadocs + */ +public class LinkSerializer extends ExtendedXLinkPipe implements Serializer { + + private PrintStream out; + + /** + * Set the {@link OutputStream} where the requested resource should be serialized. + */ + public void setOutputStream(OutputStream out) throws IOException { + this.out = new PrintStream(out); + } + + /** + * Get the mime-type of the output of this <code>Component</code>. + */ + public String getMimeType() { + return Constants.LINK_CONTENT_TYPE; + } + + public void simpleLink(String href, String role, String arcrole, String title, String show, + String actuate, String uri, String name, String raw, Attributes attr) + throws SAXException { + print(href); + super.simpleLink(href, role, arcrole, title, show, actuate, uri, name, raw, attr); + } + + public void startLocator(String href, String role, String title, String label, String uri, + String name, String raw, Attributes attr) throws SAXException { + if (traversable(href)) { + print(href); + } + super.startLocator(href, role, title, label, uri, name, raw, attr); + } + + private boolean traversable(String href) { + if (href.length() == 0) + return false; + if (href.charAt(0) == '#') + return false; + if (href.indexOf("://") != -1) + return false; + if (href.startsWith("mailto:")) + return false; + if (href.startsWith("news:")) + return false; + if (href.startsWith("javascript:")) + return false; + return true; + } + + private void print(String href) { + int ankerPos = href.indexOf('#'); + if (ankerPos == -1) { + // TODO: Xalan encodes international characters into URL encoding + out.println(href); + } else { + out.println(href.substring(0, ankerPos)); + } + } + + /** + * Test if the component wants to set the content length + */ + public boolean shouldSetContentLength() { + return false; + } + + /** + * Recyclable + */ + public void recycle() { + super.recycle(); + this.out = null; + } +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/AggregatingFallbackSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/AggregatingFallbackSourceFactory.java new file mode 100644 index 0000000..f08be25 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/AggregatingFallbackSourceFactory.java
@@ -0,0 +1,151 @@ +/* + * 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.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.cocoon.processing.ProcessInfoProvider; +import org.apache.cocoon.spring.configurator.WebAppContextUtils; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.store.impl.MRUMemoryStore; +import org.apache.lenya.cms.module.Module; +import org.apache.lenya.cms.publication.Publication; +import org.apache.lenya.cms.publication.Session; +import org.apache.lenya.cms.publication.URLInformation; +import org.apache.lenya.cms.publication.templating.AllExistingSourceResolver; +import org.apache.lenya.util.ServletHelper; + +/** + * <p> + * Aggregate all existing fallback sources by adding their XML content under the document element of + * the first encountered source. The document element of all subsequent sources is stripped. + * </p> + * <p> + * The fallback sources are resolved in bottom-up order, i.e. + * </p> + * <ul> + * <li>current publication</li> + * <li>template of the current publication</li> + * <li>template of the template publication</li> + * <li>...</li> + * <li>core</li> + * </ul> + * <p> + * If one of the fallback sources is not a well-formed XML document, a RuntimeException is thrown. + * </p> + */ +public class AggregatingFallbackSourceFactory extends FallbackSourceFactory { + + /** + * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, java.util.Map) + */ + public Source getSource(final String location, Map parameters) throws IOException, + MalformedURLException { + + String[] uris; + + if (useCache()) { + MRUMemoryStore store = getStore(); + final String cacheKey = getCacheKey(getPublicationId(), location); + final String[] cachedUris = (String[]) store.get(cacheKey); + if (cachedUris == null) { + uris = findUris(location, parameters); + store.hold(cacheKey, uris); + if (getLogger().isDebugEnabled()) { + getLogger() + .debug( + "No cached source URI for key " + cacheKey + + ", caching resolved URIs."); + } + } else { + uris = cachedUris; + if (getLogger().isDebugEnabled()) { + getLogger().debug("Using cached source URIs for key " + cacheKey); + } + } + } else { + uris = findUris(location, parameters); + } + return new AggregatingSource(location, uris, getSourceResolver()); + } + + protected String[] findUris(final String location, Map parameters) throws IOException, + MalformedURLException { + + FallbackUri uri = new FallbackUri(location); + String pubId = uri.getPubId(); + String path = uri.getPath(); + + try { + + final ProcessInfoProvider processInfo = (ProcessInfoProvider) WebAppContextUtils + .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE); + HttpServletRequest request = processInfo.getRequest(); + + if (pubId == null) { + String webappUrl = ServletHelper.getWebappURI(request); + URLInformation info = new URLInformation(webappUrl); + pubId = info.getPublicationId(); + } + + Session session = getRepository().getSession(request); + + String[] uris; + + if (session.existsPublication(pubId)) { + Publication pub = session.getPublication(pubId); + AllExistingSourceResolver resolver = new AllExistingSourceResolver(); + getTemplateManager().visit(pub, path, resolver); + uris = resolver.getUris(); + } else { + uris = new String[0]; + } + + List allUris = new ArrayList(); + allUris.addAll(Arrays.asList(uris)); + + String contextSourceUri = null; + if (path.startsWith("lenya/modules/")) { + final String moduleShortcut = path.split("/")[2]; + Module module = (Module) getModules().get(moduleShortcut); + final String modulePath = path.substring(("lenya/modules/" + moduleShortcut) + .length()); + contextSourceUri = module.getBaseUri() + modulePath; + } else { + contextSourceUri = "context://" + path; + } + if (org.apache.lenya.cms.cocoon.source.SourceUtil.exists(contextSourceUri, + getSourceResolver())) { + allUris.add(contextSourceUri); + } + + return (String[]) allUris.toArray(new String[allUris.size()]); + + } catch (Exception e) { + throw new RuntimeException("Resolving path [" + location + "] failed: ", e); + } + } + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/AggregatingSource.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/AggregatingSource.java new file mode 100644 index 0000000..5d81eb7 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/AggregatingSource.java
@@ -0,0 +1,179 @@ +package org.apache.lenya.cms.cocoon.source; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.cocoon.components.source.impl.MultiSourceValidity; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.SourceNotFoundException; +import org.apache.excalibur.source.SourceResolver; +import org.apache.excalibur.source.SourceValidity; +import org.apache.lenya.xml.DocumentHelper; +import org.apache.lenya.xml.NamespaceHelper; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * @see AggregatingFallbackSourceFactory + */ +public class AggregatingSource implements Source { + + private String uri; + private String[] sourceUris; + private SourceResolver resolver; + + /** + * @param uri + * @param uris + */ + public AggregatingSource(String uri, String[] uris, SourceResolver resolver) { + this.sourceUris = (String[]) uris.clone(); + this.uri = uri; + this.resolver = resolver; + } + + public String toString() { + return getURI(); + } + + protected void loadDom() { + try { + for (int i = 0; i < sourceUris.length; i++) { + + Document sourceDom = SourceUtil.readDOM(sourceUris[i], this.resolver); + + if (sourceDom == null) { + throw new RuntimeException("The source [" + sourceUris[i] + + "] doesn't contain XML."); + } + + Element docElement = sourceDom.getDocumentElement(); + if (this.dom == null) { + String namespaceUri = docElement.getNamespaceURI(); + String prefix = docElement.getPrefix(); + String localName = docElement.getLocalName(); + + if (namespaceUri == null) { + this.dom = DocumentHelper.createDocument(null, localName, null); + } else { + NamespaceHelper helper = new NamespaceHelper(namespaceUri, prefix, + localName); + this.dom = helper.getDocument(); + } + } + + Element[] elements = DocumentHelper.getChildren(docElement); + for (int e = 0; e < elements.length; e++) { + Element clone = (Element) this.dom.importNode(elements[e], true); + this.dom.getDocumentElement().appendChild(clone); + } + } + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private Document dom; + private byte[] data; + + protected Document getDom() { + if (this.dom == null) { + loadDom(); + } + return this.dom; + } + + protected byte[] getData() { + if (this.data == null) { + Document dom = getDom(); + if (dom != null) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + DocumentHelper.writeDocument(dom, out); + } catch (Exception e) { + throw new RuntimeException(e); + } + this.data = out.toByteArray(); + } + } + return this.data; + } + + public boolean exists() { + return this.sourceUris.length > 0; + } + + public long getContentLength() { + return getData().length; + } + + public InputStream getInputStream() throws IOException, SourceNotFoundException { + if (!exists()) { + throw new SourceNotFoundException(this + " does not exist!"); + } + return new ByteArrayInputStream(getData()); + } + + public long getLastModified() { + long lastModified = 0; + for (int i = 0; i < this.sourceUris.length; i++) { + try { + lastModified = Math.max(lastModified, SourceUtil.getLastModified(sourceUris[i], + this.resolver)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return lastModified; + } + + public String getMimeType() { + return "application/xml"; + } + + public String getScheme() { + return "aggregate-template"; + } + + public String getURI() { + return this.uri; + } + + private SourceValidity validity; + + public SourceValidity getValidity() { + if (this.validity == null) { + try { + MultiSourceValidity aggregatedValidity = new MultiSourceValidity(resolver, + MultiSourceValidity.CHECK_ALWAYS); + for (int i = 0; i < this.sourceUris.length; i++) { + Source source = null; + try { + source = resolver.resolveURI(this.sourceUris[i]); + aggregatedValidity.addSource(source); + } finally { + if (source != null) { + resolver.release(source); + } + } + } + aggregatedValidity.close(); + this.validity = aggregatedValidity; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return this.validity; + } + + public void refresh() { + this.dom = null; + this.data = null; + this.validity = null; + } + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/DocumentSource.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/DocumentSource.java new file mode 100644 index 0000000..095aabf --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/DocumentSource.java
@@ -0,0 +1,27 @@ +/* + * 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 org.apache.excalibur.source.ModifiableSource; +import org.apache.lenya.cms.publication.Document; + +public interface DocumentSource extends ModifiableSource { + + Document getDocument(); + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/FallbackSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/FallbackSourceFactory.java new file mode 100644 index 0000000..d44da8b --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/FallbackSourceFactory.java
@@ -0,0 +1,268 @@ +/* + * 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 javax.servlet.http.HttpServletRequest; + +import org.apache.cocoon.processing.ProcessInfoProvider; +import org.apache.cocoon.spring.configurator.WebAppContextUtils; +import org.apache.cocoon.util.AbstractLogEnabled; +import org.apache.commons.lang.Validate; +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.excalibur.store.impl.MRUMemoryStore; +import org.apache.lenya.cms.module.Module; +import org.apache.lenya.cms.publication.Publication; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.Session; +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; +import org.apache.lenya.util.ServletHelper; + +/** + * <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, + URIAbsolutizer { + + protected MRUMemoryStore store; + private SourceResolver resolver; + private Repository repository; + private PublicationTemplateManager templateManager; + private Map modules; + + /** + * Configure the spring bean accordingly if you want to use a store. + * @param store The store. + */ + public void setStore(MRUMemoryStore store) { + Validate.notNull(store); + this.store = store; + } + + protected boolean useCache() { + return this.store != null; + } + + protected MRUMemoryStore getStore() { + return this.store; + } + + public void setSourceResolver(SourceResolver resolver) { + this.resolver = resolver; + } + + protected SourceResolver getSourceResolver() { + return this.resolver; + } + + public void setTemplateManager(PublicationTemplateManager mgr) { + this.templateManager = mgr; + } + + protected PublicationTemplateManager getTemplateManager() { + return this.templateManager; + } + + public void setModules(Map modules) { + this.modules = modules; + } + + protected Map getModules() { + return this.modules; + } + + /** + * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, java.util.Map) + */ + public Source getSource(final String location, Map parameters) throws IOException, + MalformedURLException { + + Source source; + + if (useCache()) { + MRUMemoryStore store = getStore(); + final String pubId = getPublicationId(); + final String cacheKey = getCacheKey(pubId, location); + final String cachedSourceUri = (String) store.get(cacheKey); + + if (cachedSourceUri == null) { + source = findSource(location, parameters); + final String resolvedSourceUri = source.getURI(); + store.hold(cacheKey, resolvedSourceUri); + if (getLogger().isDebugEnabled()) { + getLogger().debug( + "No cached source URI for key " + cacheKey + ", caching URI " + + resolvedSourceUri); + } + } else { + source = this.resolver.resolveURI(cachedSourceUri); + if (getLogger().isDebugEnabled()) { + getLogger().debug( + "Using cached source URI " + cachedSourceUri + " for key " + cacheKey); + } + } + + } else { + source = findSource(location, parameters); + } + + return source; + } + + /** + * @param pubId The publication ID. + * @param fallbackUri The fallback:// (or template-fallback:// etc.) URI. + * @return A string. + */ + public static String getCacheKey(final String pubId, final String fallbackUri) { + String cacheKey = pubId == null ? fallbackUri : pubId + ":" + fallbackUri; + return cacheKey; + } + + protected String getPublicationId() { + final ProcessInfoProvider processInfo = (ProcessInfoProvider) WebAppContextUtils + .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE); + HttpServletRequest request = processInfo.getRequest(); + String webappUri = ServletHelper.getWebappURI(request); + URLInformation info = new URLInformation(webappUri); + String pubId = null; + try { + Session session = this.repository.getSession(request); + String pubIdCandidate = info.getPublicationId(); + if (pubIdCandidate != null && session.existsPublication(pubIdCandidate)) { + pubId = pubIdCandidate; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return pubId; + } + + protected Source findSource(final String location, Map parameters) throws IOException, + MalformedURLException { + + FallbackUri uri = new FallbackUri(location); + + String pubId = uri.getPubId(); + String path = uri.getPath(); + + Source source = null; + try { + final ProcessInfoProvider processInfo = (ProcessInfoProvider) WebAppContextUtils + .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE); + HttpServletRequest request = processInfo.getRequest(); + + if (pubId == null) { + String webappUrl = request.getRequestURI().substring( + request.getContextPath().length()); + + URLInformation info = new URLInformation(webappUrl); + pubId = info.getPublicationId(); + } + + Session session = this.repository.getSession(request); + if (session.existsPublication(pubId)) { + Publication pub = session.getPublication(pubId); + VisitingSourceResolver resolver = getSourceVisitor(); + this.templateManager.visit(pub, path, resolver); + source = resolver.getSource(); + } + + if (source == null) { + if (path.startsWith("lenya/modules/")) { + final String moduleShortcut = path.split("/")[2]; + if (!this.modules.containsKey(moduleShortcut)) { + throw new RuntimeException("The module '" + moduleShortcut + "' is not registered."); + } + Module module = (Module) this.modules.get(moduleShortcut); + String baseUri = module.getBaseUri(); + final String modulePath = path.substring(("lenya/modules/" + moduleShortcut) + .length()); + source = this.resolver.resolveURI(baseUri + modulePath); + } else { + String contextUri = "context://" + path; + source = this.resolver.resolveURI(contextUri); + } + } + + if (getLogger().isDebugEnabled()) { + getLogger().debug("Resolved source: [" + source.getURI() + "]"); + } + + } catch (Exception e) { + throw new RuntimeException("Resolving path [" + location + "] failed: ", e); + } + + return source; + } + + protected VisitingSourceResolver getSourceVisitor() { + return new ExistingSourceResolver(); + } + + /** + * @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()); + } + this.resolver.release(source); + } + } + + /** + * @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); + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public Repository getRepository() { + return repository; + } + +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/FallbackUri.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/FallbackUri.java new file mode 100644 index 0000000..e1725e4 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/FallbackUri.java
@@ -0,0 +1,81 @@ +/* + * 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.net.MalformedURLException; +import java.util.StringTokenizer; + +public class FallbackUri { + + private String uri; + private String pubId; + private String path; + private String queryString; + + public FallbackUri(String uri) throws MalformedURLException { + + this.uri = uri; + + // Remove the protocol and the first '//' + int pos = uri.indexOf("://"); + + if (pos == -1) { + throw new MalformedURLException("The URI [" + uri + + "] does not contain the string '://'"); + } + + String path = uri.substring(pos + 3); + + // extract publication ID + String prefix = uri.substring(0, pos); + StringTokenizer tokens = new StringTokenizer(prefix, ":"); + if (tokens.countTokens() > 1) { + tokens.nextToken(); + this.pubId = tokens.nextToken(); + } + + // remove query string + int questionMarkIndex = path.indexOf("?"); + if (questionMarkIndex > -1) { + this.queryString = path.substring(questionMarkIndex); + path = path.substring(0, questionMarkIndex); + } + + if (path.length() == 0) { + throw new MalformedURLException("The path after the protocol must not be empty!"); + } + + this.path = path; + } + + public String getPubId() { + return this.pubId; + } + + public String getPath() { + return this.path; + } + + public String getQeryString() { + return this.queryString; + } + + public String getUri() { + return this.uri; + } +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/LenyaDocSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/LenyaDocSourceFactory.java new file mode 100644 index 0000000..9c6936c --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/LenyaDocSourceFactory.java
@@ -0,0 +1,208 @@ +/* + * 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 javax.servlet.http.HttpServletRequest; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.cocoon.processing.ProcessInfoProvider; +import org.apache.cocoon.spring.configurator.WebAppContextUtils; +import org.apache.cocoon.util.AbstractLogEnabled; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.SourceException; +import org.apache.excalibur.source.SourceFactory; +import org.apache.lenya.cms.publication.Document; +import org.apache.lenya.cms.publication.Publication; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.ResourceNotFoundException; +import org.apache.lenya.cms.publication.Session; +import org.apache.lenya.cms.publication.URLInformation; +import org.apache.lenya.cms.repository.NodeFactory; +import org.apache.lenya.util.ServletHelper; + +/** + * A factory for the "lenyadoc" scheme (virtual protocol), which is used to resolve any + * src="lenyadoc:<...>" attributes in sitemaps. + * + * <code>lenyadoc://<publication>/<area>/<language>/<uuid></code> + * <code>lenyadoc:/<language>/<uuid></code> + * + * If we want to request the meta data for a document + * instead of the document itself, we need to use + * + * <code>lenyadoc:meta:/<language>/<uuid></code> + * <code>lenyadoc:meta://<publication>/<area>/<language>/<uuid></code> + * + * @version $Id:$ + * @deprecated Use <code>lenya-document</code> instead (see {@link org.apache.lenya.cms.cocoon.source.DocumentSourceFactory}. + */ +public class LenyaDocSourceFactory extends AbstractLogEnabled implements SourceFactory { + + protected static final String SCHEME = "lenyadoc"; + + private Repository repository; + private NodeFactory nodeFactory; + + /** + * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration) + */ + public void configure(Configuration configuration) throws ConfigurationException { + } + + /** + * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, java.util.Map) + */ + public Source getSource(String location, Map parameters) throws MalformedURLException, + IOException, SourceException { + String scheme = null; + String area = null; + String language = null; + String uuid = null; + Publication pub; + + // Parse the url + int start = 0; + int end; + + // Scheme + end = location.indexOf(':', start); + if (end == -1) { + throw new MalformedURLException("Malformed lenyadoc: URI: can not find scheme part [" + + location + "]"); + } + scheme = location.substring(start, end); + if (!SCHEME.equals(scheme)) { + throw new MalformedURLException("Malformed lenyadoc: URI: unknown scheme [" + location + + "]"); + } + + ProcessInfoProvider processInfoProvider = (ProcessInfoProvider) WebAppContextUtils + .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE); + HttpServletRequest request = processInfoProvider.getRequest(); + Session session = this.repository.getSession(request); + start = end + 1; + + // Absolute vs. relative + if (location.startsWith("//", start)) { + // Absolute: get publication id + start += 2; + end = location.indexOf('/', start); + if (end == -1) { + throw new MalformedURLException("Malformed lenyadoc: URI: publication part not found [" + + location + "]"); + } + String publicationId = location.substring(start, end); + try { + pub = session.getPublication(publicationId); + } catch (ResourceNotFoundException e) { + throw new MalformedURLException("Malformed lenyadoc: Publication [" + publicationId + + "] does not exist or could not be initialized"); + } + if (pub == null || !pub.exists()) { + throw new SourceException("The publication [" + publicationId + "] does not exist!"); + } + + // Area + start = end + 1; + end = location.indexOf('/', start); + if (end == -1) { + throw new MalformedURLException("Malformed lenyadoc: URI: cannot find area [" + + location + "]"); + } + area = location.substring(start, end); + + } else if (location.startsWith("/", start)) { + end += 1; + // Relative: get publication id and area from page envelope + try { + String id = new URLInformation(ServletHelper.getWebappURI(request)).getPublicationId(); + pub = session.getPublication(id); + } catch (ResourceNotFoundException e) { + throw new SourceException("Error getting publication id / area from page envelope [" + + location + "]"); + } + if (pub != null && pub.exists()) { + String url = ServletHelper.getWebappURI(request); + area = new URLInformation(url).getArea(); + } else { + throw new SourceException("Error getting publication id / area from page envelope [" + + location + "]"); + } + } else { + throw new MalformedURLException("Malformed lenyadoc: URI [" + location + "]"); + } + + // Language + start = end + 1; + end = location.indexOf('/', start); + if (end == -1) { + throw new MalformedURLException("Malformed lenyadoc: URI: cannot find language [" + + location + "]"); + } + language = location.substring(start, end); + + // UUID + start = end + 1; + uuid = location.substring(start); + + if (getLogger().isDebugEnabled()) { + getLogger().debug("Creating repository source for URI [" + location + "]"); + } + Document document; + try { + document = pub.getArea(area).getDocument(uuid, language); + } catch (ResourceNotFoundException e) { + throw new MalformedURLException("Malformed lenyadoc: Document [" + uuid + ":" + + language + "] could not be created."); + } + + String lenyaURL = document.getSourceURI(); + + if (getLogger().isDebugEnabled()) { + getLogger().debug("Mapping 'lenyadoc:' URL [" + location + "] to 'lenya:' URL [" + + lenyaURL + "]"); + getLogger().debug("Creating repository source for URI [" + lenyaURL + "]"); + } + + return new RepositorySource(getNodeFactory(), lenyaURL, session, getLogger()); + } + + /** + * @see org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source) + */ + public void release(Source source) { + // Source will be released by delegated source factory. + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setNodeFactory(NodeFactory nodeFactory) { + this.nodeFactory = nodeFactory; + } + + public NodeFactory getNodeFactory() { + return nodeFactory; + } +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/LenyaSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/LenyaSourceFactory.java new file mode 100644 index 0000000..e665683 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/LenyaSourceFactory.java
@@ -0,0 +1,134 @@ +/* + * 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 javax.servlet.http.HttpServletRequest; + +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.DefaultConfiguration; +import org.apache.cocoon.components.flow.FlowHelper; +import org.apache.cocoon.components.modules.input.JXPathHelper; +import org.apache.cocoon.components.modules.input.JXPathHelperConfiguration; +import org.apache.cocoon.processing.ProcessInfoProvider; +import org.apache.cocoon.spring.configurator.WebAppContextUtils; +import org.apache.cocoon.util.AbstractLogEnabled; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.SourceException; +import org.apache.excalibur.source.SourceFactory; +import org.apache.lenya.cms.publication.Publication; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.Session; +import org.apache.lenya.cms.repository.NodeFactory; +import org.apache.lenya.cms.repository.RepositoryException; +import org.apache.lenya.util.Query; + +/** + * A factory for the "lenya" scheme (virtual protocol), which is used to resolve any src="lenya:..." + * attributes in sitemaps. This implementation constructs the path to the source document from the + * page envelope and delegates any further resolving to the "context" source resolver of Cocoon. + * + * @version $Id$ + */ +public class LenyaSourceFactory extends AbstractLogEnabled implements SourceFactory { + + protected static final String SCHEME = "lenya:"; + + /** fallback if no configuration is available */ + protected static final String DEFAULT_DELEGATION_SCHEME = "context:"; + protected static final String DEFAULT_DELEGATION_PREFIX = "/" + + Publication.PUBLICATION_PREFIX_URI; + + private NodeFactory nodeFactory; + private Repository repository; + + /** + * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, java.util.Map) + */ + public Source getSource(final String location, final Map parameters) + throws MalformedURLException, IOException, SourceException { + + String sessionName = null; + + String[] uriAndQuery = location.split("\\?"); + if (uriAndQuery.length > 1) { + Query query = new Query(uriAndQuery[1]); + sessionName = query.getValue("session"); + } + + Session session; + try { + session = getSession(sessionName); + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + + if (getLogger().isDebugEnabled()) { + getLogger().debug("Creating repository source for URI [" + location + "]"); + } + + return new RepositorySource(this.nodeFactory, location, session, getLogger()); + + } + + protected Session getSession(String sessionName) throws RepositoryException { + Session session; + ProcessInfoProvider process = (ProcessInfoProvider) WebAppContextUtils + .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE); + if (sessionName == null) { + HttpServletRequest request = process.getRequest(); + session = this.repository.getSession(request); + } else if (sessionName.equals("usecase")) { + session = getUsecaseSession(process.getObjectModel()); + } else { + throw new RepositoryException("Invalid session: [" + sessionName + "]"); + } + + return session; + } + + protected Session getUsecaseSession(Map objectModel) throws RepositoryException { + try { + Configuration config = new DefaultConfiguration("foo"); + JXPathHelperConfiguration helperConfig = JXPathHelper.setup(config); + Object contextObject = FlowHelper.getContextObject(objectModel); + return (Session) JXPathHelper.getAttribute("usecase/session", config, helperConfig, + contextObject); + } catch (Exception e) { + throw new RepositoryException(e); + } + } + + /** + * Does nothing because the delegated factory does this. + * @see org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source) + */ + public void release(Source source) { + // do nothing + } + + public void setNodeFactory(NodeFactory nodeFactory) { + this.nodeFactory = nodeFactory; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/PublicationSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/PublicationSourceFactory.java new file mode 100644 index 0000000..70c5273 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/PublicationSourceFactory.java
@@ -0,0 +1,70 @@ +package org.apache.lenya.cms.cocoon.source; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.cocoon.processing.ProcessInfoProvider; +import org.apache.cocoon.spring.configurator.WebAppContextUtils; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.SourceFactory; +import org.apache.excalibur.source.SourceResolver; +import org.apache.lenya.cms.publication.Publication; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.ResourceNotFoundException; +import org.apache.lenya.cms.publication.Session; + +/** + * Syntax: pub:{pubId}:/path/to/file + */ +public class PublicationSourceFactory implements SourceFactory { + + protected static final String PROTOCOL = "pub"; + + private Repository repository; + private SourceResolver sourceResolver; + + public Source getSource(final String location, final Map parameters) throws IOException, + MalformedURLException { + + final String pathInfo = location.substring(PROTOCOL.length() + 1); + + final int colonIndex = pathInfo.indexOf(":"); + if (colonIndex < 0) { + throw new MalformedURLException("The URI " + location + + " must contain the publication ID."); + } + + final String pubId = pathInfo.substring(0, colonIndex); + ProcessInfoProvider process = (ProcessInfoProvider) WebAppContextUtils + .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE); + HttpServletRequest request = process.getRequest(); + Session session = this.repository.getSession(request); + if (session.existsPublication(pubId)) { + Publication pub = session.getPublication(pubId); + final String path = pathInfo.substring(colonIndex + 1); + final String uri = pub.getSourceUri() + path; + return this.sourceResolver.resolveURI(uri); + } else { + throw new ResourceNotFoundException("The publication " + pubId + " does not exist."); + } + } + + /** + * Does nothing because the delegated factory does this. + * @see org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source) + */ + public void release(Source source) { + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setSourceResolver(SourceResolver sourceResolver) { + this.sourceResolver = sourceResolver; + } + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/RepositorySource.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/RepositorySource.java new file mode 100644 index 0000000..311b513 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/RepositorySource.java
@@ -0,0 +1,398 @@ +/* + * 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.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.net.MalformedURLException; +import java.util.Collection; + +import javax.servlet.http.HttpServletRequest; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.apache.cocoon.processing.ProcessInfoProvider; +import org.apache.cocoon.spring.configurator.WebAppContextUtils; +import org.apache.commons.logging.Log; +import org.apache.excalibur.source.ModifiableTraversableSource; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.SourceException; +import org.apache.excalibur.source.SourceNotFoundException; +import org.apache.excalibur.source.SourceValidity; +import org.apache.excalibur.source.impl.AbstractSource; +import org.apache.lenya.cms.publication.Document; +import org.apache.lenya.cms.publication.Publication; +import org.apache.lenya.cms.publication.Session; +import org.apache.lenya.cms.publication.URLInformation; +import org.apache.lenya.cms.repository.ContentHolder; +import org.apache.lenya.cms.repository.Node; +import org.apache.lenya.cms.repository.NodeFactory; +import org.apache.lenya.cms.repository.RepositoryException; +import org.apache.lenya.cms.repository.SessionHolder; +import org.apache.lenya.util.Query; +import org.apache.lenya.util.ServletHelper; + +/** + * Repository source. + * + * @version $Id$ + */ +public class RepositorySource extends AbstractSource implements ModifiableTraversableSource { + + private ContentHolder content; + private Session session; + private Log logger; + protected static final String SCHEME = "lenya"; + + private NodeFactory nodeFactory; + + /** + * @param nodeFactory The node factory. + * @param uri The source URI. + * @param session The repository session. + * @param logger The logger. + * @throws SourceException if an error occurs. + * @throws MalformedURLException if an error occurs. + */ + public RepositorySource(NodeFactory nodeFactory, String uri, Session session, Log logger) + throws SourceException, MalformedURLException { + this.logger = logger; + this.nodeFactory = nodeFactory; + + if (getLogger().isDebugEnabled()) + getLogger().debug("Init RepositorySource: " + uri); + + if (session == null) { + throw new IllegalArgumentException("The repository session must not be null!"); + } + this.session = session; + + if (uri == null) { + throw new MalformedURLException("The source URI must not be null!"); + } + + setSystemId(uri); + + // Scheme + int start = 0; + int end = uri.indexOf(':'); + if (end == -1) + throw new MalformedURLException( + "Malformed uri for xmodule source (cannot find scheme) : " + uri); + + String scheme = uri.substring(start, end); + if (!SCHEME.equals(scheme)) + throw new MalformedURLException("Malformed uri for a xmodule source : " + uri); + + setScheme(scheme); + + try { + + String sourceUri; + int revisionNumber = -1; + + int questionMarkIndex = uri.indexOf("?"); + if (questionMarkIndex > -1) { + sourceUri = uri.substring(0, questionMarkIndex); + Query query = new Query(uri.substring(questionMarkIndex + 1)); + String revisionString = query.getValue("rev", null); + if (revisionString != null) { + ProcessInfoProvider process = (ProcessInfoProvider) WebAppContextUtils + .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE); + HttpServletRequest request = process.getRequest(); + String webappUrl = ServletHelper.getWebappURI(request); + String pubId = new URLInformation(webappUrl).getPublicationId(); + Publication pub = this.session.getPublication(pubId); + Document currentDoc = pub.getSession().getUriHandler().getDocument(webappUrl); + if (currentDoc.getSourceURI().equals(sourceUri)) { + revisionNumber = Integer.valueOf(revisionString).intValue(); + } + } + } else { + sourceUri = uri; + } + + org.apache.lenya.cms.repository.Session repoSession = ((SessionHolder) session) + .getRepositorySession(); + if (revisionNumber == -1) { + this.content = (ContentHolder) repoSession + .getRepositoryItem(nodeFactory, sourceUri); + } else { + Node node = (Node) repoSession.getRepositoryItem(nodeFactory, sourceUri); + this.content = node.getHistory().getRevision(revisionNumber); + } + + } catch (Exception e) { + throw new SourceException("Creating repository node failed: ", e); + } + } + + /** + * @return The repository node which is accessed by this source. + */ + public Node getNode() { + + if (!(this.content instanceof Node)) { + throw new RuntimeException( + "This operation can only be invoked on nodes, not on revisions."); + } + + return (Node) this.content; + } + + protected Log getLogger() { + return this.logger; + } + + /** + * @see org.apache.excalibur.source.ModifiableSource#getOutputStream() + */ + public OutputStream getOutputStream() throws IOException { + try { + return getNode().getOutputStream(); + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + } + + /** + * @see org.apache.excalibur.source.ModifiableSource#delete() + */ + public void delete() { + try { + getNode().delete(); + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + } + + /** + * @see org.apache.excalibur.source.ModifiableSource#canCancel(java.io.OutputStream) + */ + public boolean canCancel(OutputStream arg0) { + return false; + } + + /** + * @see org.apache.excalibur.source.ModifiableSource#cancel(java.io.OutputStream) + */ + public void cancel(OutputStream arg0) throws IOException { + } + + /** + * @see org.apache.excalibur.source.Source#exists() + */ + public boolean exists() { + try { + if (getContent().exists()) { + return true; + } else { + return isCollection(); + } + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + } + + /** + * @see org.apache.excalibur.source.Source#getInputStream() + */ + public InputStream getInputStream() throws IOException, SourceNotFoundException { + if (getLogger().isDebugEnabled()) + getLogger().debug("Get InputStream for " + getURI()); + if (!exists()) { + throw new SourceNotFoundException("The source [" + getURI() + "] does not exist!"); + } + try { + return getContent().getInputStream(); + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + } + + protected InputStream convert(org.w3c.dom.Document edoc) throws IOException { + + final org.w3c.dom.Document doc = edoc; + final PipedOutputStream pos = new PipedOutputStream(); + PipedInputStream pis = new PipedInputStream(); + pis.connect(pos); + + (new Thread(new Runnable() { + + public void run() { + try { + transform(doc, pos); + } catch (TransformerException e) { + throw new RuntimeException( + "Failed to tranform org.w3c.dom.Document to PipedOutputStream", e); + } finally { + try { + pos.close(); + } catch (Exception ignore) { + ignore.printStackTrace(); + } + } + } + }, getClass().getName() + ".convert(org.w3c.dom.Document edoc)")).start(); + + return pis; + } + + void transform(org.w3c.dom.Document edoc, PipedOutputStream pos) throws TransformerException { + + TransformerFactory tFactory = TransformerFactory.newInstance(); + Transformer transformer = tFactory.newTransformer(); + + transformer.setOutputProperty("encoding", "UTF-8"); + transformer.setOutputProperty("indent", "yes"); + + transformer.transform(new DOMSource(edoc), new StreamResult(pos)); + + } + + /** + * @return The content of this source. + */ + public ContentHolder getContent() { + return this.content; + } + + /** + * @see org.apache.excalibur.source.Source#getContentLength() + */ + public long getContentLength() { + try { + return getContent().getContentLength(); + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + } + + /** + * @see org.apache.excalibur.source.Source#getLastModified() + */ + public long getLastModified() { + try { + return getContent().getLastModified(); + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + } + + /** + * @return The source URI. + */ + public String getSourceURI() { + return getContent().getSourceURI(); + } + + /** + * @see org.apache.excalibur.source.Source#getMimeType() + */ + public String getMimeType() { + try { + return getContent().getMimeType(); + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + } + + /** + * + */ + public Source getParent() { + getLogger().warn("getParent() not implemented yet!"); + return null; + } + + /** + * + */ + public void makeCollection() { + getLogger().warn("RepositorySource().makeCollection() not implemented yet!"); + } + + /** + * + */ + public String getName() { + // Quick and dirty + String name = new java.io.File(getURI()).getName(); + if (getLogger().isDebugEnabled()) + getLogger().debug("getName(): URI: " + name); + return name; + } + + /** + * + */ + public Source getChild(String name) { + getLogger().warn("getChild() not implemented yet!"); + return null; + } + + /** + * + */ + public Collection getChildren() { + try { + Collection children = getNode().getChildren(); + java.util.Iterator iterator = children.iterator(); + java.util.Vector newChildren = new java.util.Vector(); + while (iterator.hasNext()) { + Node child = (Node) iterator.next(); + newChildren.add(new RepositorySource(getNodeFactory(), child.getSourceURI(), + this.session, getLogger())); + } + return newChildren; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * + */ + public boolean isCollection() { + try { + return getNode().isCollection(); + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + } + + private SourceValidity validity; + + public SourceValidity getValidity() { + if (this.validity == null) { + this.validity = new RepositorySourceValidity(this); + } + return this.validity; + } + + public NodeFactory getNodeFactory() { + return nodeFactory; + } + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/RepositorySourceValidity.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/RepositorySourceValidity.java new file mode 100644 index 0000000..6249618 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/RepositorySourceValidity.java
@@ -0,0 +1,71 @@ +/* + * 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 org.apache.excalibur.source.SourceValidity; + +/** + * Validity for repository sources. + */ +public class RepositorySourceValidity implements SourceValidity { + + private static final long serialVersionUID = 1L; + + private String sourceUri; + private long lastModified; + + /** + * @param source The source this validity is for. + */ + public RepositorySourceValidity(RepositorySource source) { + this.sourceUri = source.getSourceURI(); + this.lastModified = source.exists() ? source.getLastModified() : 0; + } + + public int isValid() { + return SourceValidity.UNKNOWN; + } + + public int isValid(SourceValidity validity) { + if (validity instanceof RepositorySourceValidity) { + RepositorySourceValidity repoValidity = (RepositorySourceValidity) validity; + String repoValidityUri = repoValidity.getSourceURI(); + + if (!repoValidityUri.equals(this.sourceUri)) { + throw new RuntimeException("Wrong source URI: [" + repoValidityUri + + "] instead of [" + this.sourceUri + "]!"); + } + if (this.lastModified >= repoValidity.getLastModified()) { + return SourceValidity.VALID; + } else { + return SourceValidity.INVALID; + } + } else { + return SourceValidity.INVALID; + } + } + + protected long getLastModified() { + return this.lastModified; + } + + protected String getSourceURI() { + return this.sourceUri; + } + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/SiteSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/SiteSourceFactory.java new file mode 100644 index 0000000..068018a --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/SiteSourceFactory.java
@@ -0,0 +1,154 @@ +/* + * 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 javax.servlet.http.HttpServletRequest; + +import org.apache.cocoon.processing.ProcessInfoProvider; +import org.apache.cocoon.spring.configurator.WebAppContextUtils; +import org.apache.cocoon.util.AbstractLogEnabled; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.SourceException; +import org.apache.excalibur.source.SourceFactory; +import org.apache.excalibur.source.SourceNotFoundException; +import org.apache.excalibur.source.SourceResolver; +import org.apache.lenya.cms.publication.Document; +import org.apache.lenya.cms.publication.Repository; +import org.apache.lenya.cms.publication.Session; +import org.apache.lenya.cms.publication.URLInformation; +import org.apache.lenya.cms.site.SiteStructure; +import org.apache.lenya.util.ServletHelper; + +/** + * <p> + * This source factory allows to access documents based on their path in the site structure. + * </p> + * <p> + * Relative addressing refers to the current publication and area. + * </p> + * <p> + * Syntax: + * </p> + * <ul> + * <li>Absolute: <code>site://{pubId}/{area}/{language}{path}</code></li> + * <li>Relative: <code>site:/{language}{path}</code></li> + * </ul> + * <p> + * Usage examples: + * </p> + * <ul> + * <li><code>site://default/authoring/en/news/today</code></li> + * <li><code>site:/en/news/today</code></li> + * </ul> + */ +public class SiteSourceFactory extends AbstractLogEnabled implements SourceFactory { + + private SourceResolver sourceResolver; + private Repository repository; + + /** + * @see org.apache.excalibur.source.SourceFactory#getSource(java.lang.String, java.util.Map) + */ + public Source getSource(String location, Map parameters) throws MalformedURLException, + IOException, SourceException { + ProcessInfoProvider process = (ProcessInfoProvider) WebAppContextUtils + .getCurrentWebApplicationContext().getBean(ProcessInfoProvider.ROLE); + HttpServletRequest request = process.getRequest(); + + String areaName = null; + String pubId; + + StringTokenizer locationSteps = new StringTokenizer(location, "?"); + String completePath = locationSteps.nextToken(); + + String relativePath; + try { + + String scheme = completePath.substring(0, completePath.indexOf(":") + 1); + final String absolutePath = completePath.substring(scheme.length()); + if (absolutePath.startsWith("//")) { + final String fullPath = absolutePath.substring(2); + StringTokenizer steps = new StringTokenizer(fullPath, "/"); + pubId = steps.nextToken(); + areaName = steps.nextToken(); + String prefix = pubId + "/" + areaName; + relativePath = fullPath.substring(prefix.length()); + } else if (absolutePath.startsWith("/")) { + String webappUrl = ServletHelper.getWebappURI(request); + URLInformation info = new URLInformation(webappUrl); + pubId = info.getPublicationId(); + areaName = info.getArea(); + relativePath = absolutePath; + } else { + throw new MalformedURLException("The path [" + absolutePath + + "] must start with at least one slash."); + } + + Session session = this.repository.getSession(request); + SiteStructure site = session.getPublication(pubId).getArea(areaName).getSite(); + + String[] steps = relativePath.substring(1).split("/"); + + String language = steps[0]; + String prefix = "/" + language; + String path = relativePath.substring(prefix.length()); + + if (site.contains(path, language)) { + Document doc = site.getNode(path).getLink(language).getDocument(); + String docUri = "lenya-document:" + doc.getUUID() + ",lang=" + doc.getLanguage() + + ",area=" + doc.getArea() + ",pub=" + doc.getPublication().getId(); + + if (locationSteps.hasMoreTokens()) { + String queryString = locationSteps.nextToken(); + docUri = docUri + "?" + queryString; + } + return this.sourceResolver.resolveURI(docUri); + } else { + throw new SourceNotFoundException("The source [" + location + "] doesn't exist."); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * @see org.apache.excalibur.source.SourceFactory#release(org.apache.excalibur.source.Source) + */ + public void release(Source source) { + this.sourceResolver.release(source); + } + + public SourceResolver getSourceResolver() { + return sourceResolver; + } + + public void setSourceResolver(SourceResolver sourceResolver) { + this.sourceResolver = sourceResolver; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/TemplateFallbackSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/TemplateFallbackSourceFactory.java new file mode 100644 index 0000000..efd3fc9 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/TemplateFallbackSourceFactory.java
@@ -0,0 +1,35 @@ +/* + * 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 org.apache.lenya.cms.publication.templating.ExistingAncestorSourceResolver; +import org.apache.lenya.cms.publication.templating.VisitingSourceResolver; + +/** + * Source factory following the fallback principle, resolving the existing ancestor of the existing resource. + * For more information about the URL syntax, see {@link FallbackSourceFactory}. + * + * @version $Id: FallbackSourceFactory.java 264153 2005-08-29 15:11:14Z andreas $ + */ +public class TemplateFallbackSourceFactory extends FallbackSourceFactory { + + protected VisitingSourceResolver getSourceVisitor() { + return new ExistingAncestorSourceResolver(); + } + +} \ No newline at end of file
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/ZipSource.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/ZipSource.java new file mode 100644 index 0000000..7d57a59 --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/ZipSource.java
@@ -0,0 +1,157 @@ +/* + * 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; +//package org.apache.cocoon.components.source.impl; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.cocoon.util.AbstractLogEnabled; +import org.apache.cocoon.util.MIMEUtils; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.SourceNotFoundException; +import org.apache.excalibur.source.SourceValidity; + +/** + * @author <a href="http://apache.org/~reinhard">Reinhard Poetz</a> + * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a> + * @version CVS $Id: ZipSource.java 30932 2004-07-29 17:35:38Z vgritsenko $ + * @since 2.1.4 + */ +public class ZipSource extends AbstractLogEnabled implements Source { + + Source archive; + String documentName; + + /** + * Ctor. + * @param archive The archive source. + * @param fileName The filename. + */ + public ZipSource(Source archive, String fileName) { + this.archive = archive; + this.documentName = fileName; + } + + public boolean exists() { + if(!this.archive.exists()) { + return false; + } + ZipInputStream zipStream = null; + ZipEntry document = null; + boolean found = false; + try { + zipStream = new ZipInputStream(this.archive.getInputStream()); + do { + document = zipStream.getNextEntry(); + if (document != null) { + if (document.getName().equals(this.documentName)) { + found = true; + } else { + zipStream.closeEntry(); + } + } + } while (document != null && found == false); + } catch(IOException ioe) { + return false; + } finally { + try { + zipStream.close(); + } catch (IOException ioe) { + this.getLogger().error("Error while closing ZipInputStream: " + this.documentName); + } + } + return found; + } + + public InputStream getInputStream() + throws IOException, SourceNotFoundException { + + ZipInputStream zipStream = + new ZipInputStream(this.archive.getInputStream()); + ZipEntry document = null; + boolean found = false; + do { + document = zipStream.getNextEntry(); + if (document != null) { + if (document.getName().equals(this.documentName)) { + found = true; + } else { + // go to next entry + zipStream.closeEntry(); + } + } + } while (document != null && found == false); + + if (document == null) { + throw new SourceNotFoundException( + "The document " + + documentName + + " is not in the archive " + + this.archive.getURI()); + } + + // now we will extract the document and write it into a byte array + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + int length = -1; + while (zipStream.available() > 0) { + length = zipStream.read(buffer, 0, 8192); + if (length > 0) { + baos.write(buffer, 0, length); + } + } + zipStream.close(); + baos.flush(); + + // return an input stream + return new ByteArrayInputStream(baos.toByteArray()); + } + + public String getURI() { + return this.archive.getURI() + "/" + this.documentName; + } + + public String getScheme() { + return ZipSourceFactory.ZIP_SOURCE_SCHEME; + } + + public SourceValidity getValidity() { + return this.archive.getValidity(); + } + + public void refresh() { + } + + public String getMimeType() { + String ext = this.documentName.substring( this.documentName.lastIndexOf(".") ); + return MIMEUtils.getMIMEType( ext ); + } + + public long getContentLength() { + return -1; + } + + public long getLastModified() { + return this.archive.getLastModified(); + } + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/ZipSourceFactory.java b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/ZipSourceFactory.java new file mode 100644 index 0000000..e26596b --- /dev/null +++ b/org.apache.lenya.core.cocoon/src/main/java/org/apache/lenya/cms/cocoon/source/ZipSourceFactory.java
@@ -0,0 +1,97 @@ +/* + * 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.cocoon.components.source.impl; +package org.apache.lenya.cms.cocoon.source; + + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.Map; + +import org.apache.avalon.framework.service.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.avalon.framework.service.Serviceable; +import org.apache.avalon.framework.thread.ThreadSafe; +import org.apache.cocoon.util.AbstractLogEnabled; +import org.apache.excalibur.source.Source; +import org.apache.excalibur.source.SourceException; +import org.apache.excalibur.source.SourceFactory; +import org.apache.excalibur.source.SourceResolver; + + +/** Implementation of a {@link Source} that gets its content from + * and ZIP archive. + * + * A ZIP source can be reached using the zip:// pseudo-protocol. The syntax is + * zip://myFile.xml@myZip.zip (zip://[file]@[archive]) + * + * @author <a href="http://apache.org/~reinhard">Reinhard Poetz</a> + * @version CVS $Id: ZipSourceFactory.java 30932 2004-07-29 17:35:38Z vgritsenko $ + * @since 2.1.4 + */ +public class ZipSourceFactory extends AbstractLogEnabled + implements SourceFactory, ThreadSafe, Serviceable { + + protected ServiceManager manager; + /** + * The URL scheme, including the colon. + */ + public static final String ZIP_SOURCE_SCHEME = "zip:"; + + public Source getSource(String location, Map parameters) + throws IOException, MalformedURLException { + + if ( this.getLogger().isDebugEnabled() ) { + this.getLogger().debug("Processing " + location); + } + + // syntax checks + int separatorPos = location.indexOf('@'); + if (separatorPos == -1) { + throw new MalformedURLException("@ required in URI: " + location); + } + int protocolEnd = location.indexOf("://"); + if (protocolEnd == -1) { + throw new MalformedURLException("URI does not contain '://' : " + location); + } + + // get the source of the archive and return the ZipSource passing + // a source retrieved from the SourceResolver + String documentName = location.substring(protocolEnd + 3, separatorPos); + Source archive; + SourceResolver resolver = null; + try { + resolver = (SourceResolver)this.manager.lookup( SourceResolver.ROLE ); + archive = resolver.resolveURI(location.substring(separatorPos + 1)); + } catch (ServiceException se) { + throw new SourceException("SourceResolver is not available.", se); + } finally { + this.manager.release(resolver); + } + return new ZipSource(archive, documentName); + } + + + public void release(Source source) { + // not necessary here + } + + public void service(ServiceManager manager) throws ServiceException { + this.manager = manager; + } + +}
diff --git a/org.apache.lenya.core.cocoon/src/main/resources/META-INF/cocoon/spring/block-servlet-service.xml b/org.apache.lenya.core.cocoon/src/main/resources/META-INF/cocoon/spring/block-servlet-service.xml index 8bff2f4..4a18f78 100644 --- a/org.apache.lenya.core.cocoon/src/main/resources/META-INF/cocoon/spring/block-servlet-service.xml +++ b/org.apache.lenya.core.cocoon/src/main/resources/META-INF/cocoon/spring/block-servlet-service.xml
@@ -25,8 +25,8 @@ http://cocoon.apache.org/schema/servlet http://cocoon.apache.org/schema/servlet/cocoon-servlet-1.0.xsd"> <!-- exemple from the archetype --> - <bean name="org.apache.lenya.lenya-core-cocoon-components.service" class="org.apache.cocoon.sitemap.SitemapServlet"> - <servlet:context mount-path="/lenya-core-cocoon-components" context-path="blockcontext:/lenya-core-cocoon-components/"/> + <bean name="org.apache.lenya.core.cocoon.block" class="org.apache.cocoon.sitemap.SitemapServlet"> + <servlet:context mount-path="/lenya.core.cocoon" context-path="blockcontext:/lenya.core.cocoon/"/> </bean> </beans>