/**
   * @param path
   * @param strings
   * @param strings2
   * @param strings3
   * @throws JCRNodeFactoryServiceException
   * @throws RepositoryException
   * @throws IOException
   * @throws UnsupportedEncodingException
   */
  private Map<String, Object> saveProperties(String path, MapParams params)
      throws RepositoryException, JCRNodeFactoryServiceException, UnsupportedEncodingException,
          IOException {
    InputStream in = null;

    try {
      Node n = jcrNodeFactoryService.getNode(path);
      Map<String, Object> map = null;
      if (n != null) {
        in = jcrNodeFactoryService.getInputStream(path);
        String content = IOUtils.readFully(in, "UTF-8");
        try {
          in.close();
        } catch (IOException ex) {
        }
        map = beanConverter.convertToObject(content, Map.class);
      } else {
        map = new HashMap<String, Object>();
      }
      for (int i = 0; i < params.names.length; i++) {
        if (REMOVE_ACTION.equals(params.actions[i])) {
          map.remove(params.names[i]);
        } else {
          map.put(params.names[i], params.values[i]);
        }
      }
      String result = beanConverter.convertToString(map);
      in = new ByteArrayInputStream(result.getBytes("UTF-8"));
      n = jcrNodeFactoryService.setInputStream(path, in, RestProvider.CONTENT_TYPE);

      // deal with indexed properties.
      for (int i = 0; i < params.names.length; i++) {
        boolean index = false;
        if (params.indexes != null && "1".equals(params.indexes[i])) {
          index = true;
        }
        if (n.hasProperty("sakai:" + params.names[i])) {
          // if remove, remove it, else update
          if (REMOVE_ACTION.equals(params.actions[i])) {
            n.getProperty("sakai:" + params.names[i]).remove();
          } else {
            n.setProperty("sakai:" + params.names[i], params.values[i]);
          }
        } else if (index) {
          // add it
          n.setProperty("sakai:" + params.names[i], params.values[i]);
        }
      }
      n.getSession().save(); // verify changes
      Map<String, Object> outputMap = new HashMap<String, Object>();
      outputMap.put("response", "OK");
      return outputMap;
    } finally {
      try {
        in.close();
      } catch (Exception ex) {
      }
    }
  }
  /**
   * {@inheritDoc}
   *
   * @see org.sakaiproject.kernel.api.rest.RestProvider#dispatch(java.lang.String[],
   *     javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
   */
  public void dispatch(
      String[] elements, HttpServletRequest request, HttpServletResponse response) {
    try {
      FriendsParams params =
          new FriendsParams(
              elements, request, sessionManagerService, userEnvironmentResolverService);
      Map<String, Object> map = Maps.newHashMap();
      switch (params.major) {
        case connect:
          if (!"POST".equals(request.getMethod())) {
            throw new RestServiceFaultException(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
          }
          switch (params.minor) {
            case request:
              map = doRequestConnect(params, request, response);
              break;
            case accept:
              map = doAcceptConnect(params, request, response);
              break;
            case cancel:
              map = doCancelConnect(params, request, response);
              break;
            case reject:
              map = doRejectConnect(params, request, response);
              break;
            case ignore:
              map = doIgnoreConnect(params, request, response);
              break;
            case remove:
              map = doRemoveConnect(params, request, response);
              break;
            default:
              doRequestError();
              break;
          }
          break;
        case status:
          if (!"GET".equals(request.getMethod())) {
            throw new RestServiceFaultException(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
          }
          map = doStatus(params, request, response);
          break;
        default:
          doRequestError();
          break;
      }

      if (map != null) {
        String responseBody = beanConverter.convertToString(map);
        response.setContentType(RestProvider.CONTENT_TYPE);
        response.getOutputStream().print(responseBody);
      }
    } catch (SecurityException ex) {
      throw ex;
    } catch (RestServiceFaultException ex) {
      throw ex;
    } catch (Exception ex) {
      throw new RestServiceFaultException(ex.getMessage(), ex);
    }
  }
  /**
   * {@inheritDoc}
   *
   * @see org.sakaiproject.kernel.api.rest.RestProvider#dispatch(java.lang.String[],
   *     javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
   */
  public void dispatch(
      String[] elements, HttpServletRequest request, HttpServletResponse response) {
    try {
      if (elements.length >= 1) {
        // the URL is the path to the resource.

        Map<String, Object> map = null;
        if (PRIVATE.equals(elements[1]) && "POST".equals(request.getMethod())) {
          map = doPatchPrivate(elements, request, response);
        } else if (SHARED.equals(elements[1]) && "POST".equals(request.getMethod())) {
          map = doPatchShared(elements, request, response);
        } else {
          throw new RestServiceFaultException(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
        }
        if (map != null) {
          String responseBody = beanConverter.convertToString(map);
          response.setContentType(RestProvider.CONTENT_TYPE);
          response.getOutputStream().print(responseBody);
        }
      }
    } catch (AccessDeniedException ex) {
      throw new RestServiceFaultException(HttpServletResponse.SC_FORBIDDEN, ex.getMessage(), ex);
    } catch (SecurityException ex) {
      throw ex;
    } catch (RestServiceFaultException ex) {
      throw ex;
    } catch (Exception ex) {
      throw new RestServiceFaultException(ex.getMessage(), ex);
    }
  }