You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by si...@apache.org on 2007/06/03 21:42:13 UTC
svn commit: r543970 - in /cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl: ./
src/main/java/org/apache/cocoon/jcr/transforming/
Author: simoneg
Date: Sun Jun 3 12:42:12 2007
New Revision: 543970
URL: http://svn.apache.org/viewvc?view=rev&rev=543970
Log:
Added JCRQueryTransformer to perform queries on JCR repositories
Added:
cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/src/main/java/org/apache/cocoon/jcr/transforming/
cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/src/main/java/org/apache/cocoon/jcr/transforming/JCRQueryTransformer.java
cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/src/main/java/org/apache/cocoon/jcr/transforming/JCRSelection.java
Modified:
cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/status.xml
Added: cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/src/main/java/org/apache/cocoon/jcr/transforming/JCRQueryTransformer.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/src/main/java/org/apache/cocoon/jcr/transforming/JCRQueryTransformer.java?view=auto&rev=543970
==============================================================================
--- cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/src/main/java/org/apache/cocoon/jcr/transforming/JCRQueryTransformer.java (added)
+++ cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/src/main/java/org/apache/cocoon/jcr/transforming/JCRQueryTransformer.java Sun Jun 3 12:42:12 2007
@@ -0,0 +1,294 @@
+/*
+ * 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.jcr.transforming;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import javax.jcr.Item;
+import javax.jcr.LoginException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.QueryResult;
+import javax.xml.namespace.QName;
+
+import org.apache.avalon.framework.CascadingRuntimeException;
+import org.apache.cocoon.transformation.AbstractSAXTransformer;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * This transformer performs a query on a JCR server.
+ * <p>
+ * It expects a "query" element without namespace containing the
+ * query to execute, either in XPath or SQL, and eventually the properties to
+ * fetch (since JSR-170 does not define a standard for specifying them in xpath
+ * queries).
+ * </p>
+ * <p>
+ * It will then replace it with a "result" element containing the query results
+ * in elements named "node".
+ * </p>
+ * <h2>Sample invocation:</h2>
+ * <pre>
+ * <query language="xpath"
+ * xmlns:jcr="http://www.jcp.org/jcr/1.0"
+ * xmlns:cms="http://www.mycms.org">
+ * <jcr:encoding/>
+ * <jcr:isCheckedOut/>
+ * <cms:data>
+ * <cms:author/>
+ * <cms:workflow>
+ * <cms:lastStep/>
+ * </cms:workflow>
+ * </cms:data>
+ * jcr:root/site/articles/*
+ * </query>
+ * </pre>
+ * <h2>Sample output:</h2>
+ * <pre>
+ * <result xmlns:cms="http://www.mycms.org"
+ * xmlns:jcr="http://www.jcp.org/jcr/1.0">
+ * <node href="/site/articles/home.xml" name="home.xml" type="nt:folder" uuid="2359e49d-e84b-47aa-b27c-fb46b86abb19">
+ * <jcr:encoding type="String">UTF-8</jcr:encoding>
+ * <jcr:isCheckedOut type="Boolean">false</jcr:isCheckedOut>
+ * <cms:data href="/site/articles/home.xml/cms:data" name="cms:data" type="cms:dataRecord" uuid="72e36378-f26f-4548-993f-21b5c97306f0">
+ * <cms:author type="String">Simone</cms:author>
+ * <cms:workflow href="/site/articles/home.xml/cms:data/cms:workflow" name="cms:workflow" type="cms:workflowRecord" uuid="72e36378-f26f-4548-993f-31a6d9931e4">
+ * <cms:lastStep type="Integer">1</cms:lastStep>
+ * </cms:workflow>
+ * </cms:data>
+ * <jcr:primaryType type="Name">cms:article</jcr:primaryType>
+ * </node>
+ * </result>
+ * </pre>
+ *
+ * TODO: render bynary properties
+ *
+ * @version $Id$
+ */
+public class JCRQueryTransformer extends AbstractSAXTransformer {
+
+ /**
+ * Wether we are inside the query element or not
+ */
+ protected boolean recording = false;
+
+ /**
+ * Language of the query, as taken from query/@language
+ */
+ protected String language;
+
+ /**
+ * Query statement
+ */
+ protected String query;
+
+ /**
+ * Basic selection, this will hold all properties and subnodes fetching hierarchy.
+ */
+ protected JCRSelection selection;
+
+ /**
+ * Latest JCRSelection created, so that children can be properly assigned.
+ */
+ protected Stack currentSelection;
+
+ protected Repository repo;
+
+ public void startElement(String uri, String name, String raw,
+ Attributes attr) throws SAXException {
+ if (name.equals("query")) {
+ recording = true;
+ language = attr.getValue("language");
+ selection = new JCRSelection();
+ currentSelection = new Stack();
+ } else if (recording) {
+ JCRSelection eleselection = new JCRSelection(uri, name);
+ if (currentSelection.empty()) {
+ selection.addSubnode(eleselection);
+ } else {
+ JCRSelection parent = (JCRSelection) currentSelection.peek();
+ parent.addSubnode(eleselection);
+ }
+ currentSelection.push(eleselection);
+ } else {
+ super.startElement(uri, name, raw, attr);
+ }
+ }
+
+ public void endElement(String uri, String name, String raw) throws SAXException {
+ if (name.equals("query")) {
+ recording = false;
+ lazyInit();
+
+ Session session;
+ try {
+ // TODO: accept a different workspace?
+ session = repo.login();
+ } catch (LoginException e) {
+ throw new SAXException("Login to repository failed", e);
+ } catch (RepositoryException e) {
+ throw new SAXException("Cannot access repository", e);
+ }
+
+ try {
+ AttributesImpl emptyAttrs = new AttributesImpl();
+ this.contentHandler.startElement("", "result", "result", emptyAttrs);
+
+ QueryManager queryManager = session.getWorkspace().getQueryManager();
+ Query query2 = queryManager.createQuery(this.query, this.language);
+ QueryResult result = query2.execute();
+ String[] columnNames = result.getColumnNames();
+ for (int i = 0; i < columnNames.length; i++) {
+ int pos = -1;
+ if ((pos = columnNames[i].indexOf(':')) != -1) {
+ String prefix = columnNames[i].substring(0, pos);
+ String local = columnNames[i].substring(pos + 1);
+ String namespaceURI = session.getNamespaceURI(prefix);
+ selection.addSubnode(new JCRSelection(namespaceURI, local));
+ }
+ }
+
+ Set selns = selection.getNamespaces();
+ Map namespaces = new HashMap();
+ char spareprefix = 'a';
+ for (Iterator iter = selns.iterator(); iter.hasNext();) {
+ String selnamespace = (String) iter.next();
+ if (!namespaces.containsKey(selnamespace)) {
+ String prefix = session.getNamespacePrefix(selnamespace);
+ if (prefix == null || prefix.length() == 0) {
+ prefix = "jcr" + spareprefix++;
+ }
+ this.contentHandler.startPrefixMapping(prefix, selnamespace);
+ namespaces.put(selnamespace, prefix);
+ }
+ }
+
+ NodeIterator nodes = result.getNodes();
+
+ while (nodes.hasNext()) {
+ Node node = nodes.nextNode();
+ sendItem(node, this.selection, namespaces);
+ }
+
+ this.contentHandler.endElement("", "result", "result");
+ } catch (InvalidQueryException e) {
+ throw new SAXException("Invalid query " + this.query + " (" + this.language + ")", e);
+ } catch (Exception e) {
+ throw new SAXException(e);
+ }
+ } else if (!recording) {
+ super.endElement(uri, name, raw);
+ } else {
+ currentSelection.pop();
+ }
+ }
+
+ private void sendItem(Item item, JCRSelection acsel, Map namespaces) throws Exception {
+ QName name = acsel.getName();
+ if (name == null) {
+ name = new QName("", "node");
+ }
+ String raw = name.getLocalPart();
+ if (namespaces.containsKey(name.getNamespaceURI())) {
+ raw = namespaces.get(name.getNamespaceURI()) + ":" + raw;
+ }
+
+ if (item.isNode()) {
+ Node node = (Node) item;
+
+ AttributesImpl nodeAttrs = new AttributesImpl();
+ nodeAttrs.addAttribute("", "href", "href", "xsd:string", node.getPath());
+ nodeAttrs.addAttribute("", "name", "name", "xsd:string", node.getName());
+ nodeAttrs.addAttribute("", "type", "type", "xsd:string", node.getDefinition().getDeclaringNodeType().getName());
+ nodeAttrs.addAttribute("", "uuid", "uuid", "xsd:string", node.getUUID());
+ this.contentHandler.startElement(name.getNamespaceURI(), name.getLocalPart(), raw, nodeAttrs);
+ for (Iterator iter = acsel.getSubnodes().iterator(); iter.hasNext();) {
+ JCRSelection subnode = (JCRSelection) iter.next();
+ String prefix = (String) namespaces.get(subnode.getName().getNamespaceURI());
+ String fullname = prefix + ":" + subnode.getName().getLocalPart();
+ if (node.hasProperty(fullname)) {
+ Item subitem = node.getProperty(fullname);
+ sendItem(subitem, subnode, namespaces);
+ } else if (node.hasNode(fullname)) {
+ Item subitem = node.getNode(fullname);
+ sendItem(subitem, subnode, namespaces);
+ }
+ }
+ this.contentHandler.endElement(name.getNamespaceURI(), name.getLocalPart(), raw);
+ } else {
+ Property property = (Property) item;
+
+ Value[] vals = null;
+ try {
+ vals = property.getValues();
+ } catch (ValueFormatException e) {
+ vals = new Value[] { property.getValue() };
+ }
+
+ for (int i = 0; i < vals.length; i++) {
+ AttributesImpl propAttrs = new AttributesImpl();
+ int type = property.getType();
+ propAttrs.addAttribute("", "type", "type", "xsd:string", PropertyType.nameFromValue(type));
+ this.contentHandler.startElement(name.getNamespaceURI(), name.getLocalPart(), raw, propAttrs);
+
+ if (type == PropertyType.BINARY) {
+ // TODO how to send a binary value? BASE64 or simple CDATA?
+ } else {
+ String val = vals[i].getString();
+ this.contentHandler.characters(val.toCharArray(), 0, val.length());
+ }
+
+ this.contentHandler.endElement(name.getNamespaceURI(), name.getLocalPart(), raw);
+ }
+ }
+ }
+
+ public void characters(char[] buff, int offset, int len) throws SAXException {
+ if (!recording) {
+ super.characters(buff, offset, len);
+ } else {
+ this.query = new String(buff, offset, len).trim();
+ }
+ }
+
+ protected void lazyInit() {
+ if (this.repo == null) {
+ try {
+ this.repo = (Repository)manager.lookup(Repository.class.getName());
+ } catch (Exception e) {
+ throw new CascadingRuntimeException("Cannot lookup repository", e);
+ }
+ }
+ }
+
+}
Added: cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/src/main/java/org/apache/cocoon/jcr/transforming/JCRSelection.java
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/src/main/java/org/apache/cocoon/jcr/transforming/JCRSelection.java?view=auto&rev=543970
==============================================================================
--- cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/src/main/java/org/apache/cocoon/jcr/transforming/JCRSelection.java (added)
+++ cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/src/main/java/org/apache/cocoon/jcr/transforming/JCRSelection.java Sun Jun 3 12:42:12 2007
@@ -0,0 +1,119 @@
+/*
+ * 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.jcr.transforming;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.xml.namespace.QName;
+
+/**
+ * Represents a selection of a property or of a subnode and its properties.
+ *
+ * @version $Id$
+ */
+public class JCRSelection {
+
+ /**
+ * Name of the property or subnode.
+ */
+ private QName name;
+ /**
+ * In case it is a subnode, other subnodes or properties can be added.
+ */
+ private Set subnodes;
+
+ public JCRSelection() {}
+
+ /**
+ * @param namespaceURI Namespace URI of the property or subnode name
+ * @param localname Local name (excluding prefix) of the property or subnode.
+ */
+ public JCRSelection(String namespaceURI, String localname) {
+ this.setName(namespaceURI, localname);
+ }
+
+ /**
+ * @return The name of the property of subnode.
+ */
+ public QName getName() {
+ return name;
+ }
+ public void setName(QName name) {
+ this.name = name;
+ }
+
+ public void setName(String namespaceURI, String localname) {
+ this.name = new QName(namespaceURI, localname);
+ }
+
+ /**
+ * @return The set of properties and/or subnodes.
+ */
+ public Set getSubnodes() {
+ return subnodes;
+ }
+ public void setSubnodes(Set subnodes) {
+ this.subnodes = subnodes;
+ }
+
+ /**
+ * Adds a subnode or property.
+ * @param subnode The subnode or poerty to add
+ */
+ public void addSubnode(JCRSelection subnode) {
+ if (this.subnodes == null) this.subnodes = new HashSet();
+ this.subnodes.add(subnode);
+ }
+ public boolean hasSubnodes() {
+ return this.subnodes != null && this.subnodes.size() > 0;
+ }
+
+ /**
+ * @return A set of all namespaces URIs used in this node and its subnodes.
+ */
+ public Set getNamespaces() {
+ Set ret = new HashSet();
+ fillNamespaces(ret);
+ return ret;
+ }
+
+ private void fillNamespaces(Set ret) {
+ if (this.name != null) ret.add(this.name.getNamespaceURI());
+ if (this.subnodes != null) {
+ for (Iterator iter = this.subnodes.iterator(); iter.hasNext();) {
+ JCRSelection subnode = (JCRSelection) iter.next();
+ subnode.fillNamespaces(ret);
+ }
+ }
+ }
+
+ public boolean equals(Object other) {
+ if (!(other instanceof JCRSelection)) return false;
+ JCRSelection oth = (JCRSelection) other;
+ return oth.name == null ? this.name == null : this.name == null ? false : this.name.equals(oth.name);
+ }
+
+ public int hashCode() {
+ if (this.name == null) return 0;
+ return this.name.hashCode();
+ }
+
+
+
+}
Modified: cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/status.xml
URL: http://svn.apache.org/viewvc/cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/status.xml?view=diff&rev=543970&r1=543969&r2=543970
==============================================================================
--- cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/status.xml (original)
+++ cocoon/trunk/blocks/cocoon-jcr/cocoon-jcr-impl/status.xml Sun Jun 3 12:42:12 2007
@@ -66,6 +66,9 @@
<!-- The following list contains the changes since the latest 2.1.x version. -->
<changes>
<release version="@version@" date="@date@">
+ <action dev="SG" type="add">
+ Added JCR query transformer to perform queries on JCR repositories.
+ </action>
</release>
</changes>