Package org.apache.clerezza.platform.content

Source Code of org.apache.clerezza.platform.content.DiscobitsTypeHandler

/*
* 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.clerezza.platform.content;

import org.apache.clerezza.platform.graphnodeprovider.GraphNodeProvider;
import org.apache.clerezza.rdf.metadata.MetaDataGenerator;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import java.util.Set;
import java.util.concurrent.locks.Lock;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.PUT;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.core.Response.Status;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import org.apache.clerezza.jaxrs.utils.RedirectUtil;
import org.apache.clerezza.platform.Constants;
import org.apache.clerezza.platform.content.WebDavUtils.PropertyMap;
import org.apache.clerezza.platform.content.collections.CollectionCreator;
import org.apache.clerezza.platform.content.webdav.MKCOL;
import org.apache.clerezza.platform.content.webdav.MOVE;
import org.apache.clerezza.platform.content.webdav.PROPFIND;
import org.apache.clerezza.platform.content.webdav.PROPPATCH;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.apache.felix.scr.annotations.Services;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.clerezza.platform.graphprovider.content.ContentGraphProvider;
import org.apache.clerezza.platform.typehandlerspace.OPTIONS;
import org.apache.clerezza.platform.typehandlerspace.SupportedTypes;
import org.apache.clerezza.rdf.core.MGraph;
import org.apache.clerezza.rdf.core.Triple;
import org.apache.clerezza.rdf.core.TripleCollection;
import org.apache.clerezza.rdf.core.UriRef;
import org.apache.clerezza.rdf.core.access.LockableMGraph;
import org.apache.clerezza.rdf.core.impl.TripleImpl;
import org.apache.clerezza.rdf.ontologies.HIERARCHY;
import org.apache.clerezza.rdf.ontologies.RDF;
import org.apache.clerezza.rdf.utils.GraphNode;
import org.apache.clerezza.rdf.utils.UnionMGraph;
import org.apache.clerezza.rdf.utils.UriMutatingTripleCollection;
import org.apache.clerezza.web.fileserver.util.MediaTypeGuesser;
import org.osgi.service.component.ComponentContext;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.wymiwyg.wrhapi.HeaderName;

/**
* This Class allows getting and putting content structured using the
* Discobits ontology.
*
* Is an implementation of DiscobitsHandler and additionally registers as
* TypeHanlder to allow HTTP GET and PUT.
*
* @author reto, tho, agron, mir
*/
@Component(metatype=true)
@Services({
  @Service(Object.class),
  @Service(DiscobitsHandler.class)
})
@Property(name="org.apache.clerezza.platform.typehandler", boolValue=true)
@Reference(name="metaDataGenerator",
  policy=ReferencePolicy.DYNAMIC,
  cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE,
  referenceInterface=MetaDataGenerator.class
)
@SupportedTypes(types = { "http://www.w3.org/2000/01/rdf-schema#Resource" }, prioritize = false)
public class DiscobitsTypeHandler extends AbstractDiscobitsHandler
    implements DiscobitsHandler {
@Property(value="600", label="Max-Age", description="Specifies the value of the max-age field"
    + "set in the cache-control header of InfoDiscoBit-Responses, as per RFC 2616 this is a number of "
    + "seconds")
  public static final String MAX_AGE = "max-age";

  @Reference
  protected ContentGraphProvider cgProvider;

  @Reference
  PageNotFoundService notFoundPageService;

  @Reference
  GraphNodeProvider graphNodeProvider;

  private static final Logger logger = LoggerFactory.getLogger(DiscobitsTypeHandler.class);

  private final Set<MetaDataGenerator> metaDataGenerators =
      Collections.synchronizedSet(new HashSet<MetaDataGenerator>());

  private String cacheControlHeaderValue;

  protected void activate(ComponentContext context) {
    cacheControlHeaderValue = "max-age="+(String) context.getProperties().get(MAX_AGE);
  }

  /**
   * TypeHandle method for rdf types "TitledContext", "InfoDiscoBit",
   * "OrderedContent" and "XHTMLInfoDiscoBit".
   *
   * @param uriInfo
   * @return
   */
  @GET
  @Produces({"*/*"})
  public Object getResource(@Context UriInfo uriInfo) {
    final UriRef uri = new UriRef(uriInfo.getAbsolutePath().toString());
      final GraphNode graphNode = getResourceAsGraphNode(uriInfo);
    if (graphNode == null) {
      return resourceUnavailable(uri, uriInfo);
    }
    InfoDiscobit infoDiscobit = InfoDiscobit.createInstance(graphNode);
    if (infoDiscobit != null) {
      Response response = Response.ok().
          header(HttpHeaders.CACHE_CONTROL, cacheControlHeaderValue).
          entity(infoDiscobit).
          build();
      return response;
    } else {
      return graphNode;
    }
  }

  private GraphNode getResourceAsGraphNode(UriInfo uriInfo) {
    final UriRef uri = new UriRef(uriInfo.getAbsolutePath().toString());
    GraphNode result = graphNodeProvider.getLocal(uri);
    //could chck if nodeContext > 0, but this would be less efficient
    TripleCollection tc = result.getGraph();
    if (tc.filter(uri, null, null).hasNext()) {
      return result;
    }
    if (tc.filter(null, null, uri).hasNext()) {
      return result;
    }
    return null;
  }


 
  /**
   * Creates an <code>InfoDiscoBit</code> at the specified location
   *
   * @param uriInfo the uri of the InforDiscoBit to be created
   * @param data the content of the upload
   */
  @PUT
  public Response putInfoDiscobit(@Context UriInfo uriInfo, @Context HttpHeaders headers, byte[] data) {
    final String contentType;
    {
      final List<String> contentTypeHeaders = headers.getRequestHeader(HttpHeaders.CONTENT_TYPE);
      if (contentTypeHeaders == null) {
        logger.warn("Content-Type not specified");
        final MediaType guessTypeForName = MediaTypeGuesser.getInstance().
            guessTypeForName(uriInfo.getAbsolutePath().toString());
        contentType = guessTypeForName == null ?
          MediaType.APPLICATION_OCTET_STREAM : guessTypeForName.toString();
      } else {
        contentType = contentTypeHeaders.get(0);
      }
     
    } 
    final UriRef infoDiscoBitUri = new UriRef(uriInfo.getAbsolutePath().toString());
    put(infoDiscoBitUri, MediaType.valueOf(contentType), data);
    return Response.status(Status.CREATED).build();
  }

  /**
   * Creates a new collection at the specified uri
   *
   * @param uriInfo
   * @return
   * <ul>
   <li>201 "Created" response if method succeeded
   *  <li>405 "Method Not Allowed" response if collection already exists
   * </ul>
   */
  @MKCOL
  public Object mkcol(@Context UriInfo uriInfo) {
    String uriString = uriInfo.getAbsolutePath().toString();
    if (uriString.charAt(uriString.length()-1) != '/') {
      uriString += '/';
    }
    UriRef nodeUri = new UriRef(uriString);
    final MGraph mGraph = cgProvider.getContentGraph();
    Triple typeTriple = new TripleImpl(nodeUri, RDF.type, HIERARCHY.Collection);
    if (mGraph.contains(typeTriple)) {
      return Response.status(405) // Method Not Allowed
          .entity("Collection \"" + nodeUri.getUnicodeString()
          + "\" already exists.").build();
    }
    new CollectionCreator(mGraph).createContainingCollections(nodeUri);
    return Response.created(uriInfo.getAbsolutePath()).build();
  }

  /**
   * Finds all properties of a hierarchy node and returns them in a
   * {@link DOMSource}
   *
   * @param uriInfo
   * @param headers {@link HttpHeaders}
   * @param body {@link DOMSource} containing the requested properties, can be null
   * @return
   * <ul>
   <li>207 "Multistatus" response if method succeeded
   *  <li>404 "Not Found" response if the hierarchy node was not found
   *  <li>400 "Bad Request" response if the body is malformed
   * </ul>
   */
  @PROPFIND
  @Consumes({"application/xml", "text/xml", "*/*"})
  @Produces({"application/xml", "text/xml", "*/*"})
  public Response propfind(@Context UriInfo uriInfo,
      @Context HttpHeaders headers, DOMSource body) {
    final UriRef nodeUri = new UriRef(uriInfo.getAbsolutePath().toString());
    if (!nodeAtUriExists(nodeUri)) {
      return resourceUnavailable(nodeUri, uriInfo);
    }
      Map<UriRef, PropertyMap> result;
    try {
      String depthHeader = WebDavUtils.getHeaderAsString(headers, "Depth");
      if (depthHeader == null) {
        depthHeader = WebDavUtils.infinite;
      }
      final MGraph mGraph = cgProvider.getContentGraph();
      GraphNode node = new GraphNode(nodeUri, mGraph);
      if (body != null) {
        Document requestDoc = WebDavUtils.sourceToDocument(body);
        Node propfindNode = WebDavUtils.getNode(requestDoc, WebDavUtils.propfind);
        Node requestNode = WebDavUtils.getFirstChild(propfindNode);
        String requestType = requestNode.getLocalName();
        if (requestType.equalsIgnoreCase(WebDavUtils.allprop)) {
          result = getAllProps(node, depthHeader);
        } else if (requestType.equalsIgnoreCase(WebDavUtils.prop)) {
          result = getPropsByName(requestNode, node, depthHeader);
        } else if (requestType.equalsIgnoreCase(WebDavUtils.propname)) {
          result = getPropNames(node, depthHeader);
        } else {
          return Response.status(Status.BAD_REQUEST).build();
        }
      } else {
        // returns all properties
        result = getAllProps(node, depthHeader);
      }
      Document responseDoc = WebDavUtils.createResponseDoc(result);
      return Response.status(207).entity(new DOMSource(responseDoc)).type(
          MediaType.APPLICATION_XML_TYPE).build();
    } catch (TransformerFactoryConfigurationError e) {
      return Response.status(Status.BAD_REQUEST).build();
    } catch (TransformerException e) {
      return Response.status(Status.BAD_REQUEST).build();
    } catch (ParserConfigurationException e) {
      throw new RuntimeException(e);
    }
  }

  Map<UriRef, PropertyMap> getPropNames(GraphNode node, String depthHeader) {
    Map<UriRef, PropertyMap> result = new HashMap<UriRef, PropertyMap>();
    WebDavUtils.addNodeProperties(result, null, null, node, false);
    return result;
  }

  Map<UriRef, PropertyMap> getPropsByName(Node requestNode, GraphNode node,
      String depthHeader) {
    Map<UriRef, PropertyMap> result;
    NodeList children = requestNode.getChildNodes();
    result = WebDavUtils.getPropsByName(children, node, "0", true);
    return result;
  }

  Map<UriRef, PropertyMap> getAllProps(GraphNode node, String depthHeader) {
    HashMap<UriRef, PropertyMap> result = new HashMap<UriRef, PropertyMap>();
    WebDavUtils.addNodeProperties(result, null, null, node, true);
    return result;
  }

  /**
   * @param uriInfo
   * @param body {@link DOMSource} containing properties which should be set
   * or deleted
   * @return
   * <ul>
   <li>207 "Multistatus" response if method succeeded
   *  <li>404 "Not Found" response if the hierarchy node was not found
   *  <li>400 "Bad Request" response if the body is malformed
   * </ul>
   */
  @PROPPATCH
  @Consumes({"application/xml", "text/xml", "*/*"})
  @Produces({"application/xml", "text/xml", "*/*"})
  public Response proppatch(@Context UriInfo uriInfo, DOMSource body) {
    UriRef nodeUri = new UriRef(uriInfo.getAbsolutePath().toString());
    if (!nodeAtUriExists(nodeUri)) {
      return resourceUnavailable(nodeUri, uriInfo);
    }
    try {
      Document requestDoc = WebDavUtils.sourceToDocument(body);
      final MGraph mGraph = cgProvider.getContentGraph();
      GraphNode node = new GraphNode(nodeUri, mGraph);
      NodeList propsToSet = null;
      NodeList propsToRemove = null;
      Node proppatchNode = WebDavUtils.getNode(requestDoc, WebDavUtils.proppatch);
      NodeList childNodes = proppatchNode.getChildNodes();
      for (int i = 0; i < childNodes.getLength(); i++) {
        Node child = childNodes.item(i);
        String localName = child.getLocalName();
        if (localName != null) {
          if (localName.equals(WebDavUtils.set)) {
            propsToSet = child.getFirstChild().getChildNodes();
          } else if (localName.equals(WebDavUtils.remove)) {
            propsToRemove = child.getFirstChild().getChildNodes();
          }
        }
      }
      Document responseDoc = WebDavUtils.modifyProperties(node, propsToSet, propsToRemove);
      return Response.status(207).entity(new DOMSource(responseDoc)).type(
          MediaType.APPLICATION_XML_TYPE).build();
    } catch (ParserConfigurationException ex) {
      throw new RuntimeException(ex);
    } catch (TransformerFactoryConfigurationError ex) {
      return Response.status(Status.BAD_REQUEST).build();
    } catch (TransformerException ex) {
      return Response.status(Status.BAD_REQUEST).build();
    }
  }

  /**
   * Moves or renames a hierarchy node
   *
   * @param uriInfo
   * @param headers
   * @return
   * <ul>
   <li>201 "Created" response if method succeeded
   *  <li>412 "Precondition Failed" response if the destination URL is already
   *  mapped to a resource
   *  <li>404 "Not Found" response if the hierarchy node was not found
   *  <li>403 "Forbidden" response if the source and destination resources are the same
   *  <li>400 "Bad Request" if no "Destination" header was found
   * </ul>
   */
  @MOVE
  public Response move(@Context UriInfo uriInfo, @Context HttpHeaders headers) {
    UriRef nodeUri = new UriRef(uriInfo.getAbsolutePath().toString());
    final LockableMGraph mGraph = cgProvider.getContentGraph();
    GraphNode node = new GraphNode(nodeUri, mGraph);
    String targetString = WebDavUtils.getHeaderAsString(headers,
          "Destination");
    UriRef targetUri = new UriRef(targetString);
    String overwriteHeader = WebDavUtils.getHeaderAsString(headers, "Overwrite");
    boolean overwriteTarget = "T".equalsIgnoreCase(overwriteHeader);
    if (nodeAtUriExists(targetUri)) {
      if (overwriteTarget) {
        new GraphNode(targetUri, mGraph).deleteNodeContext();
      } else {
        return Response.status(Status.PRECONDITION_FAILED).build();
      }
    }
    Lock l = mGraph.getLock().writeLock();
    l.lock();
    try {
      Iterator<Triple> oldParentTripleIter
          = mGraph.filter(nodeUri, HIERARCHY.parent, null);
      if (oldParentTripleIter.hasNext()) {
        oldParentTripleIter.next();
        oldParentTripleIter.remove();
      }
      while (oldParentTripleIter.hasNext()) {
        logger.error("more than one parent statement: "+oldParentTripleIter.next());
        oldParentTripleIter.remove();
      }
      Lock writeLock = node.writeLock();
      writeLock.lock();
      try {
        node.replaceWith(targetUri);
      } finally {
        writeLock.unlock();
      }
      new CollectionCreator(mGraph).createContainingCollections(targetUri);
      try {
        return Response.created(new java.net.URI(targetUri.getUnicodeString())).build();
      } catch (URISyntaxException ex) {
        throw new IllegalArgumentException(ex);
      }
    } finally {
      l.unlock();
    }
  }

  /**
   * Deletes a hierarchy node
   *
   * @param uriInfo
   * @return
   * <ul>
   <li>200 "OK" response if method succeeded
   *  <li>404 "Not Found" response if the hierarchy node was not found
   * </ul>
   */
  @DELETE
  public Response delete(@Context UriInfo uriInfo) {
    UriRef nodeUri = new UriRef(uriInfo.getAbsolutePath().toString());
    if (!nodeAtUriExists(nodeUri)) {
      return Response.status(Status.NOT_FOUND).entity(
          uriInfo.getAbsolutePath()).type(MediaType.TEXT_PLAIN).build();
    }
    final LockableMGraph mGraph = cgProvider.getContentGraph();
    GraphNode node = new GraphNode(nodeUri, mGraph);
    node.deleteNodeContext();
    return Response.ok().build();
  }

  /**
   * @param uriInfo
   * @return
   * <ul>
   <li>200 "OK" response with an "Allow" and a "DAV" header. The "Allow"
   * header contains all the possible HTTP methods that can be executed on the
   * resource and the "DAV" header shows if the resource is WebDAV enabled
   *  <li>404 "Not Found" response if the resource was not found
   * </ul>
   */
  @OPTIONS
  public Response options(@Context UriInfo uriInfo) {
    final UriRef nodeUri = new UriRef(uriInfo.getAbsolutePath().toString());
    if (!nodeAtUriExists(nodeUri)) {
      return resourceUnavailable(nodeUri, uriInfo);
    }
      Response.ResponseBuilder builder = Response.ok();
      builder.header(HeaderName.DAV.toString(), "1");
      Set<String> allow = new HashSet<String>();
      Method[] methods = this.getClass().getMethods();
      for (Method method : methods) {
        for (Annotation annotation : method.getAnnotations()) {
          HttpMethod httpMethod = annotation.annotationType().getAnnotation(HttpMethod.class);
          if (httpMethod != null) {
            allow.add(httpMethod.value());
          }
        }
      }
      if (allow.isEmpty()) {
        builder.header(HeaderName.ALLOW.toString(), "");
      } else {
        final Iterator<String> iterator = allow.iterator();
        final StringBuffer buffer = new StringBuffer(iterator.next());
        while (iterator.hasNext()) {
          buffer.append(", ");
          buffer.append(iterator.next());
        }
        builder.header(HeaderName.ALLOW.toString(), buffer.toString());
      }
    return builder.build();
  }

  protected void bindMetaDataGenerator(MetaDataGenerator generator) {
    metaDataGenerators.add(generator);
  }

  protected void unbindMetaDataGenerator(MetaDataGenerator generator) {
    metaDataGenerators.remove(generator);
  }

  @Override
  protected MGraph getMGraph() {
    return cgProvider.getContentGraph();
  }

  @Override
  protected Set<MetaDataGenerator> getMetaDataGenerators() {
    return metaDataGenerators;
  }

 

  private boolean nodeAtUriExists(UriRef nodeUri) {
    LockableMGraph mGraph = (LockableMGraph) getMGraph();
    Lock readLock = mGraph.getLock().readLock();
    readLock.lock();
    try {
      return mGraph.filter(nodeUri, null, null).hasNext()
          || mGraph.filter(null, null, nodeUri).hasNext();
    } finally {
      readLock.unlock();
    }
  }

  private Response resourceUnavailable(UriRef nodeUri,
      UriInfo uriInfo) {
    UriRef oppositUri = makeOppositeUriRef(nodeUri);
    if (nodeAtUriExists(oppositUri)) {
      return RedirectUtil.createSeeOtherResponse(
          oppositUri.getUnicodeString(), uriInfo);
    } else {
      return notFoundPageService.createResponse(uriInfo);
    }
    //return Response.status(Status.NOT_FOUND).build();
  }
  /**
   * add trailing slash if none present, remove otherwise
   *
   * @param uri
   * @return
   */
  private static UriRef makeOppositeUriRef(UriRef uri) {
    String uriString = uri.getUnicodeString();
    if (uriString.endsWith("/")) {
      return new UriRef(uriString.substring(0, uriString.length() - 1));
    } else {
      return new UriRef(uriString + "/");
    }
  }

  private UriRef createAnyHostUri(UriInfo uriInfo) {
    return new UriRef(Constants.ALL_HOSTS_URI_PREFIX+uriInfo.getPath());
  }
}
TOP

Related Classes of org.apache.clerezza.platform.content.DiscobitsTypeHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.