Java tutorial
/* * Copyright 2007 Open Source Applications Foundation * * Licensed 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.osaf.cosmo.dav.acl.report; import java.util.HashSet; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.webdav.DavResourceIterator; import org.apache.jackrabbit.webdav.property.DavPropertyName; import org.apache.jackrabbit.webdav.version.report.ReportInfo; import org.apache.jackrabbit.webdav.version.report.ReportType; import org.apache.jackrabbit.webdav.xml.DomUtil; import org.apache.jackrabbit.webdav.xml.ElementIterator; import org.osaf.cosmo.dav.BadRequestException; import org.osaf.cosmo.dav.DavCollection; import org.osaf.cosmo.dav.DavException; import org.osaf.cosmo.dav.DavResource; import org.osaf.cosmo.dav.DavResourceLocator; import org.osaf.cosmo.dav.acl.AclConstants; import org.osaf.cosmo.dav.acl.property.PrincipalCollectionSet; import org.osaf.cosmo.dav.acl.resource.DavUserPrincipal; import org.osaf.cosmo.dav.acl.resource.DavUserPrincipalCollection; import org.osaf.cosmo.dav.property.DavProperty; import org.osaf.cosmo.dav.report.MultiStatusReport; import org.w3c.dom.CharacterData; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * <p> * Represents the <code>DAV:principal-property-search</code> report that * provides a mechanism for finding principal resources whose property values * match given input strings. * </p> * <p> * For a given search spec, the resource must have every listed property * and the value of each property must match the search spec's match * string with a case-insensitive substring search. Every search spec must * match in order for the resource to be added as a query result. * </p> * <p> * Both forms of the report may optionally include a <code>DAV:prop</code> * element specifying the names of properties that are to be included in the * response for each matching resource. If <code>DAV:prop</code> is not * included in the report, then only the href and response status are * provided for each resource. * </p> * <p> * As per RFC 3744, the report must be specified with depth 0. The report * must be targeted at a collection. * </p> */ public class PrincipalPropertySearchReport extends MultiStatusReport implements AclConstants { private static final Log log = LogFactory.getLog(PrincipalPropertySearchReport.class); public static final ReportType REPORT_TYPE_PRINCIPAL_PROPERTY_SEARCH = ReportType .register(ELEMENT_ACL_PRINCIPAL_PROPERTY_SEARCH, NAMESPACE, PrincipalPropertySearchReport.class); private Set<SearchSpec> searchSpecs; private boolean searchPrincipalCollections; // Report methods public ReportType getType() { return REPORT_TYPE_PRINCIPAL_PROPERTY_SEARCH; } // ReportBase methods /** * Parses the report info, extracting search specifications, response * properties and "search principal collections" flag. */ protected void parseReport(ReportInfo info) throws DavException { if (!getType().isRequestedReportType(info)) throw new DavException("Report not of type " + getType()); if (!getResource().isCollection()) throw new BadRequestException(getType() + " report must target a collection"); if (info.getDepth() != DEPTH_0) throw new BadRequestException(getType() + " report must be made with depth 0"); setPropFindProps(info.getPropertyNameSet()); setPropFindType(PROPFIND_BY_PROPERTY); searchSpecs = findSearchSpecs(info); searchPrincipalCollections = findSearchPrincipalCollections(info); } /** * <p> * Executes the report query and stores the result. Behaves like the * superclass method except that it does not check depth, since the * report by definition always uses depth 0. * </p> */ protected void runQuery() throws DavException { DavCollection collection = (DavCollection) getResource(); if (!searchPrincipalCollections) { doQueryChildren(collection); return; } if (log.isDebugEnabled()) log.debug("Searching DAV:principal-collection-set URLs"); PrincipalCollectionSet pcs = (PrincipalCollectionSet) collection.getProperty(PRINCIPALCOLLECTIONSET); if (pcs == null) return; for (String href : pcs.getHrefs()) { DavResourceLocator locator = collection.getResourceLocator().getFactory() .createResourceLocatorByUri(collection.getResourceLocator().getContext(), href); DavUserPrincipalCollection pc = (DavUserPrincipalCollection) collection.getResourceFactory() .resolve(locator); if (pc == null) throw new DavException("Principal collection " + href + " not resolved"); doQueryChildren(pc); } } /** * Does nothing. */ protected void doQuerySelf(DavResource resource) { } /** * Tests every principal member of the collection to see if it matches * the report's search specs. */ protected void doQueryChildren(DavCollection collection) throws DavException { for (DavResourceIterator i = collection.getMembers(); i.hasNext();) { DavResource member = (DavResource) i.nextResource(); if (member instanceof DavUserPrincipal) { if (log.isDebugEnabled()) log.debug("Testing " + member.getResourcePath()); if (matchPrincipal((DavUserPrincipal) member)) { if (log.isDebugEnabled()) log.debug("Matched " + member.getResourcePath()); getResults().add(member); } } } } // our methods public Set<SearchSpec> getSearchSpecs() { return searchSpecs; } public boolean isSearchPrincipalCollections() { return searchPrincipalCollections; } private static Set<SearchSpec> findSearchSpecs(ReportInfo info) throws DavException { HashSet<SearchSpec> specs = new HashSet<SearchSpec>(); ElementIterator psi = DomUtil.getChildren(info.getReportElement(), "property-search", NAMESPACE); if (!psi.hasNext()) throw new BadRequestException( "Expected at least one DAV:property-search child of DAV:principal-property-search"); while (psi.hasNext()) specs.add(SearchSpec.createFromXml(psi.nextElement())); return specs; } private static boolean findSearchPrincipalCollections(ReportInfo info) throws DavException { return DomUtil.hasChildElement(info.getReportElement(), "apply-to-principal-collection-set", NAMESPACE); } private boolean matchPrincipal(DavUserPrincipal principal) throws DavException { for (SearchSpec spec : searchSpecs) { if (!matchSpec(spec, principal)) return false; } return true; } private boolean matchSpec(SearchSpec spec, DavUserPrincipal principal) throws DavException { for (DavPropertyName name : spec.getProperties()) { if (!matchProperty((DavProperty) principal.getProperty(name), spec.getMatch())) return false; } return true; } private boolean matchProperty(DavProperty prop, String match) { if (prop == null) return false; if (log.isDebugEnabled()) log.debug("Matching " + prop.getName().toString() + " against " + match); Object value = prop.getValue(); if (value instanceof Element) return matchText((Element) value, match); return matchText(value.toString(), match); } private boolean matchText(Element parent, String match) { NodeList children = parent.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) { Element e = (Element) child; if (!matchText(e, match)) return false; } else if (child.getNodeType() == Node.TEXT_NODE || child.getNodeType() == Node.CDATA_SECTION_NODE) { String data = ((CharacterData) child).getData(); if (!matchText(data, match)) return false; } // else we skip the node } return true; } private boolean matchText(String test, String match) { if (log.isDebugEnabled()) log.debug("Matching " + test + " against " + match); return StringUtils.containsIgnoreCase(test, match); } public static class SearchSpec { private Set<DavPropertyName> properties; private String match; public SearchSpec(Set<DavPropertyName> properties, String match) { this.properties = properties; this.match = match; } public Set<DavPropertyName> getProperties() { return properties; } public String getMatch() { return match; } public static SearchSpec createFromXml(Element root) throws DavException { if (!DomUtil.matches(root, "property-search", NAMESPACE)) throw new IllegalArgumentException("Expected root element DAV:property-search"); Element p = DomUtil.getChildElement(root, "prop", NAMESPACE); if (p == null) throw new BadRequestException("Expected DAV:prop child of DAV:property-search"); ElementIterator pi = DomUtil.getChildren(p); if (!pi.hasNext()) throw new BadRequestException("Expected at least one child of DAV:prop"); HashSet<DavPropertyName> properties = new HashSet<DavPropertyName>(); while (pi.hasNext()) { DavPropertyName name = DavPropertyName.createFromXml(pi.nextElement()); properties.add(name); } Element m = DomUtil.getChildElement(root, "match", NAMESPACE); if (m == null) throw new BadRequestException("Expected DAV:match child of DAV:property-search"); String match = DomUtil.getText(m); return new SearchSpec(properties, match); } } }