Package org.structr.rest.resource

Source Code of org.structr.rest.resource.Resource

/**
* Copyright (C) 2010-2014 Morgner UG (haftungsbeschränkt)
*
* This file is part of Structr <http://structr.org>.
*
* Structr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Structr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Structr.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.structr.rest.resource;

import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.structr.common.CaseHelper;
import org.structr.common.GraphObjectComparator;
import org.structr.common.Permission;
import org.structr.common.SecurityContext;
import org.structr.common.error.FrameworkException;
import org.structr.core.GraphObject;
import org.structr.core.Result;
import org.structr.core.Services;
import org.structr.core.Value;
import org.structr.core.app.App;
import org.structr.core.app.Query;
import org.structr.core.app.StructrApp;
import org.structr.core.entity.AbstractNode;
import org.structr.core.entity.AbstractRelationship;
import org.structr.core.graph.NodeFactory;
import org.structr.core.graph.search.SearchCommand;
import org.structr.core.property.PropertyKey;
import org.structr.core.property.PropertyMap;
import org.structr.rest.RestMethodResult;
import org.structr.rest.exception.IllegalPathException;
import org.structr.rest.exception.NoResultsException;
import org.structr.rest.exception.NotAllowedException;
import org.structr.rest.servlet.JsonRestServlet;
import org.structr.schema.ConfigurationProvider;

//~--- classes ----------------------------------------------------------------
/**
* Base class for all resource constraints. Constraints can be combined with succeeding constraints to avoid unneccesary evaluation.
*
*
* @author Christian Morgner
*/
public abstract class Resource {

  private static final Logger logger = Logger.getLogger(Resource.class.getName());

  protected SecurityContext securityContext = null;
  protected PropertyKey idProperty          = null;

  public abstract Resource tryCombineWith(Resource next) throws FrameworkException;

  /**
   * Check and configure this instance with the given values. Please note that you need to set the security context of your class in this method.
   *
   * @param part the uri part that matched this resource
   * @param securityContext the security context of the current request
   * @param request the current request
   * @return whether this resource accepts the given uri part
   * @throws FrameworkException
   */
  public abstract boolean checkAndConfigure(String part, SecurityContext securityContext, HttpServletRequest request) throws FrameworkException;
  public abstract String getUriPart();
  public abstract Class<? extends GraphObject> getEntityClass();
  public abstract String getResourceSignature();
  public abstract boolean isCollectionResource() throws FrameworkException;

  public abstract Result doGet(PropertyKey sortKey, boolean sortDescending, int pageSize, int page, String offsetId) throws FrameworkException;
  public abstract RestMethodResult doPost(final Map<String, Object> propertySet) throws FrameworkException;


  public RestMethodResult doHead() throws FrameworkException {
    Thread.dumpStack();
    throw new IllegalStateException("Resource.doHead() called, this should not happen.");
  }

  public RestMethodResult doOptions() throws FrameworkException {
    return new RestMethodResult(HttpServletResponse.SC_OK);
  }

  public RestMethodResult doDelete() throws FrameworkException {

    Iterable<? extends GraphObject> results = null;

    // catch 204, DELETE must return 200 if resource is empty
    try {
      results = doGet(null, false, NodeFactory.DEFAULT_PAGE_SIZE, NodeFactory.DEFAULT_PAGE, null).getResults();
    } catch (final NoResultsException nre) {
      results = null;
    }

    if (results != null) {

      final Iterable<? extends GraphObject> finalResults = results;
      final App app                                      = StructrApp.getInstance(securityContext);

      for (final GraphObject obj : finalResults) {

        if (obj instanceof AbstractRelationship) {

          app.delete((AbstractRelationship)obj);

        } else if (obj instanceof AbstractNode) {

          if (!securityContext.isAllowed((AbstractNode)obj, Permission.delete)) {

            logger.log(Level.WARNING, "Could not delete {0} because {1} has no delete permission", new Object[]{obj, securityContext.getUser(true)});
            throw new NotAllowedException();

          }

          // delete cascading
          app.delete((AbstractNode)obj);
        }

      }
    }

    return new RestMethodResult(HttpServletResponse.SC_OK);
  }

  public RestMethodResult doPut(final Map<String, Object> propertySet) throws FrameworkException {

    final Result<GraphObject> result = doGet(null, false, NodeFactory.DEFAULT_PAGE_SIZE, NodeFactory.DEFAULT_PAGE, null);
    final List<GraphObject> results  = result.getResults();
    final App app                    = StructrApp.getInstance(securityContext);

    if (results != null && !results.isEmpty()) {

      final Class type = results.get(0).getClass();

      PropertyMap properties = PropertyMap.inputTypeToJavaType(securityContext, type, propertySet);

      for (final GraphObject obj : results) {

        for (final Entry<PropertyKey, Object> attr : properties.entrySet()) {

          obj.setProperty(attr.getKey(), attr.getValue());

        }

      }

      return new RestMethodResult(HttpServletResponse.SC_OK);

    }

    throw new IllegalPathException();
  }

  /**
   *
   * @param propertyView
   */
  public void configurePropertyView(final Value<String> propertyView) {
  }

  public void configureIdProperty(PropertyKey idProperty) {
    this.idProperty = idProperty;
  }

  public void postProcessResultSet(final Result result) {
  }

  public boolean isPrimitiveArray() {
    return false;
  }

  public void setSecurityContext(final SecurityContext securityContext) {
    this.securityContext = securityContext;
  }

  /**
   * Override this method in your resource implementation and return false
   * to prevent the creation of an encosing transaction context in your
   * doPost() method. Default: true.
   *
   * @return whether to create transaction around the doPost() method
   */
  public boolean createPostTransaction() {
    return true;
  }

  // ----- protected methods -----
  protected PropertyKey findPropertyKey(final TypedIdResource typedIdResource, final TypeResource typeResource) {

    Class sourceNodeType = typedIdResource.getTypeResource().getEntityClass();
    String rawName = typeResource.getRawType();
    PropertyKey key = StructrApp.getConfiguration().getPropertyKeyForJSONName(sourceNodeType, rawName, false);

    if (key == null) {

      // try to convert raw name into lower-case variable name
      key = StructrApp.getConfiguration().getPropertyKeyForJSONName(sourceNodeType, CaseHelper.toLowerCamelCase(rawName), false);
    }

    return key;
  }

  protected String buildLocationHeader(final GraphObject newObject) {

    final StringBuilder uriBuilder = securityContext.getBaseURI();

    uriBuilder.append(getUriPart());
    uriBuilder.append("/");

    if (newObject != null) {

      // use configured id property
      if (idProperty == null) {

        uriBuilder.append(newObject.getUuid());

      } else {

        uriBuilder.append(newObject.getProperty(idProperty));

      }
    }

    return uriBuilder.toString();
  }

  protected void applyDefaultSorting(List<? extends GraphObject> list, PropertyKey sortKey, boolean sortDescending) {

    if (!list.isEmpty()) {

      String finalSortOrder = sortDescending ? "desc" : "asc";

      if (sortKey == null) {

        // Apply default sorting, if defined
        final GraphObject obj = list.get(0);

        final PropertyKey defaultSort = obj.getDefaultSortKey();

        if (defaultSort != null) {

          sortKey = defaultSort;
          finalSortOrder = obj.getDefaultSortOrder();
        }
      }

      if (sortKey != null) {
        Collections.sort(list, new GraphObjectComparator(sortKey, finalSortOrder));
      }
    }
  }

  protected void extractDistanceSearch(final HttpServletRequest request, final Query query) {

    if (request != null) {

      final String distance = request.getParameter(SearchCommand.DISTANCE_SEARCH_KEYWORD);

      if (!request.getParameterMap().isEmpty() && StringUtils.isNotBlank(distance)) {

        final Double dist = Double.parseDouble(distance);
        final String location = request.getParameter(SearchCommand.LOCATION_SEARCH_KEYWORD);

        String street = request.getParameter(SearchCommand.STREET_SEARCH_KEYWORD);
        String house = request.getParameter(SearchCommand.HOUSE_SEARCH_KEYWORD);
        String postalCode = request.getParameter(SearchCommand.POSTAL_CODE_SEARCH_KEYWORD);
        String city = request.getParameter(SearchCommand.CITY_SEARCH_KEYWORD);
        String state = request.getParameter(SearchCommand.STATE_SEARCH_KEYWORD);
        String country = request.getParameter(SearchCommand.COUNTRY_SEARCH_KEYWORD);

        // if location, use city and street, else use all fields that are there!
        if (location != null) {

          String[] parts = location.split("[,]+");
          switch (parts.length) {

            case 3:
              country = parts[2]// no break here intentionally

            case 2:
              city = parts[1]// no break here intentionally

            case 1:
              street = parts[0];
              break;

            default:
              break;
          }
        }

        query.location(street, house, postalCode, city, state, country, dist);
      }
    }
  }

  protected void extractSearchableAttributes(final SecurityContext securityContext, final Class type, final HttpServletRequest request, final Query query) throws FrameworkException {

    if (type != null && request != null && !request.getParameterMap().isEmpty()) {

      final ConfigurationProvider conf   = Services.getInstance().getConfigurationProvider();
      final List<PropertyKey> searchKeys = new LinkedList<>();

      for (final String name : request.getParameterMap().keySet()) {

        final PropertyKey key = conf.getPropertyKeyForJSONName(type, getFirstPartOfString(name));
        if (key != null) {

          if (key.isSearchable()) {

            // add to list of searchable keys
            searchKeys.add(key);

          } else if (!JsonRestServlet.commonRequestParameters.contains(name)) {

            throw new FrameworkException(400, "Search key " + name + " is not indexed.");
          }

        } else if (!JsonRestServlet.commonRequestParameters.contains(name)) {

          // exclude common request parameters here (should not throw exception)
          throw new FrameworkException(400, "Invalid search key " + name);
        }
      }

      // sort list of search keys according to their desired order
      // so that querying search attributes can use other attributes
      // to refine their partial results.
      Collections.sort(searchKeys, new PropertyKeyProcessingOrderComparator());

      for (final PropertyKey key : searchKeys) {

        // hand list of search attributes over to key
        key.extractSearchableAttribute(securityContext, request, query);
      }
    }
  }

  protected static int parseInteger(final Object source) {

    try {
      return Integer.parseInt(source.toString());
    } catch (final Throwable t) {
    }

    return -1;
  }

  // ----- private methods -----
  /**
   * Returns the first part of the given source string when it contains a "."
   *
   * @param parameter
   * @return source
   */
  private String getFirstPartOfString(final String source) {

    final int pos = source.indexOf(".");
    if (pos > -1) {

      return source.substring(0, pos);
    }

    return source;
  }

  // ----- nested classes -----
  private static class PropertyKeyProcessingOrderComparator implements Comparator<PropertyKey> {

    @Override
    public int compare(final PropertyKey key1, final PropertyKey key2) {
      return Integer.valueOf(key1.getProcessingOrderPosition()).compareTo(Integer.valueOf(key2.getProcessingOrderPosition()));
    }
  }
}
TOP

Related Classes of org.structr.rest.resource.Resource

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.