All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.internet2.middleware.grouper.ws.rest.GrouperRestServlet Maven / Gradle / Ivy

There is a newer version: 5.13.5
Show newest version
/*******************************************************************************
 * Copyright 2012 Internet2
 * 
 * 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.
 ******************************************************************************/
/*
 * @author mchyzer $Id: GrouperRestServlet.java,v 1.13 2009-11-20 07:15:38 mchyzer Exp $
 */
package edu.internet2.middleware.grouper.ws.rest;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.internet2.middleware.grouper.instrumentation.InstrumentationDataBuiltinTypes;
import edu.internet2.middleware.grouper.instrumentation.InstrumentationThread;
import edu.internet2.middleware.grouper.j2ee.status.GrouperStatusServlet;
import edu.internet2.middleware.grouper.misc.GrouperStartup;
import edu.internet2.middleware.grouper.misc.GrouperVersion;
import edu.internet2.middleware.grouper.util.GrouperUtil;
import edu.internet2.middleware.grouper.ws.GrouperServiceJ2ee;
import edu.internet2.middleware.grouper.ws.GrouperWsConfig;
import edu.internet2.middleware.grouper.ws.coresoap.WsResultMeta;
import edu.internet2.middleware.grouper.ws.rest.contentType.WsRestRequestContentType;
import edu.internet2.middleware.grouper.ws.rest.contentType.WsRestResponseContentType;
import edu.internet2.middleware.grouper.ws.rest.method.GrouperRestHttpMethod;
import edu.internet2.middleware.grouper.ws.util.GrouperServiceUtils;
import edu.internet2.middleware.grouper.ws.util.GrouperWsVersionUtils;

/**
 * servlet for rest web services
 */
public class GrouperRestServlet extends HttpServlet {

  static {
    GrouperStatusServlet.registerStartup();
  }

  /** keep track of if this is a rest request vs soap */
  private static ThreadLocal restRequest = new ThreadLocal();

  /**
   * return if this is a rest request
   * @return true if rest request
   */
  public static boolean isRestRequest() {
    Boolean isRestRequest = restRequest.get();
    return isRestRequest != null && isRestRequest;
  }
  
  /**
   * response header for if this is a success or not T or F
   */
  public static final String X_GROUPER_SUCCESS = "X-Grouper-success";

  /**
   * response header for the grouper response code
   */
  public static final String X_GROUPER_RESULT_CODE = "X-Grouper-resultCode";

  /**
   * response header for the grouper response code
   */
  public static final String X_GROUPER_RESULT_CODE2 = "X-Grouper-resultCode2";

  /** logger */
  private static final Log LOG = GrouperUtil.getLog(GrouperRestServlet.class);

  /**
   * id
   */
  private static final long serialVersionUID = 1L;

  /**
   * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
   */
  @SuppressWarnings("unchecked")
  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    GrouperStartup.startup();

    GrouperStatusServlet.incrementNumberOfRequest();
    InstrumentationThread.addCount(InstrumentationDataBuiltinTypes.WS_REQUESTS.name());

    GrouperServiceJ2ee.assignHttpServlet(this);
    restRequest.set(true);
    List urlStrings = null;
    StringBuilder warnings = new StringBuilder();
    WsResponseBean wsResponseBean = null;
    //we need something here if errors, so default to xhtml
    WsRestResponseContentType wsRestResponseContentType = WsRestResponseContentType.json;

    Map parameterMap = null;

    try {
      
      parameterMap = request.getParameterMap();

      //default to xhtml, or whatever is in the config file
      String configResponseType = GrouperWsConfig.retrieveConfig().propertyValueString(GrouperWsConfig.WS_REST_DEFAULT_RESPONSE_CONTENT_TYPE);
      wsRestResponseContentType = StringUtils.isBlank(configResponseType) ? wsRestResponseContentType
          : WsRestResponseContentType.valueOfIgnoreCase(configResponseType, true);
      
      urlStrings = extractUrlStrings(request);
      int urlStringsLength = GrouperUtil.length(urlStrings);

      //this is in content-type
      String contentType = request.getContentType();
      
      //get the body and convert to an object
      String body = IOUtils.toString(request.getReader());

      //get the enum
      WsRestRequestContentType wsRestRequestContentType = WsRestRequestContentType
        .findByContentType(contentType, body);
      
      //if there is a recommendation based on request content type, use that, else the config or default
      wsRestResponseContentType = GrouperUtil.defaultIfNull(
          wsRestRequestContentType.calculateResponseContentType(), wsRestResponseContentType);
      
      GrouperVersion clientVersion = null;

      if (urlStringsLength > 0) {
        boolean firstIsVersion = false;
        try {
          //first see if version
          GrouperVersion.valueOfIgnoreCase(urlStrings.get(0), true);
          firstIsVersion = true;
        } catch (Exception e) {
          //ignore
        }

        if (!firstIsVersion && urlStringsLength > 1) {
          
          //see if second is version (it better be at this point)
          GrouperVersion.valueOfIgnoreCase(urlStrings.get(1), true);
          
          //if so, then the first must be the content type
          String wsRestResponseContentTypeString = urlStrings.get(0);
          wsRestResponseContentType = WsRestResponseContentType.valueOfIgnoreCase(
              wsRestResponseContentTypeString, false);
          if (wsRestResponseContentType != null ) {
            //pop this off
            urlStrings.remove(0);
          }
        } 
      }
      //will get enum and validate
      String clientVersionString = null;
      if (urlStringsLength > 0) {
        clientVersionString = GrouperServiceUtils.popUrlString(urlStrings);
      }
      //will get enum and validate
      clientVersion = GrouperVersion.valueOfIgnoreCase(clientVersionString, true);

      GrouperWsVersionUtils.assignCurrentClientVersion(clientVersion, warnings);
      
      WsRequestBean requestObject = null;

      if (!StringUtils.isBlank(body)) {
        requestObject = (WsRequestBean) wsRestRequestContentType.parseString(body,
            warnings);
      }

      //might be in params (which might not be in body
      if (requestObject == null) {
        //might be in http params...
        requestObject = (WsRequestBean) GrouperServiceUtils.marshalHttpParamsToObject(
            parameterMap, request, warnings);

      }
      
      //get the method and validate (either from object, or HTTP method
      GrouperRestHttpMethod grouperRestHttpMethod = requestObject == null ? GrouperRestHttpMethod
          .valueOfIgnoreCase(request.getMethod(), true) : requestObject.retrieveRestHttpMethod();

      wsResponseBean = grouperRestHttpMethod.service(clientVersion, urlStrings, requestObject);

      //set this again, since it was probably just removed
      GrouperWsVersionUtils.assignCurrentClientVersion(clientVersion, warnings);
      
    } catch (GrouperRestInvalidRequest glir) {

      wsResponseBean = new WsRestResultProblem();
      WsResultMeta wsResultMeta = wsResponseBean.getResultMetadata();
      String error = glir.getMessage() + ", " + requestDebugInfo(request);

      //this is a user error, but an error nonetheless
      LOG.error(error, glir);

      wsResultMeta.appendResultMessageError(error);
      wsResultMeta.assignHttpStatusCode(400);
      wsResultMeta.assignResultCode("INVALID_QUERY");
      wsResultMeta.assignSuccess("F");

    } catch (RuntimeException e) {

      //this is not a user error, is a big problem

      wsResponseBean = new WsRestResultProblem();
      LOG.error("Problem with request: " + requestDebugInfo(request), e);
      WsResultMeta wsResultMeta = wsResponseBean.getResultMetadata();
      wsResultMeta.appendResultMessageError("Problem with request: "
          + requestDebugInfo(request));
      wsResultMeta.appendResultMessageError(e);
      wsResultMeta.assignSuccess("F");
      wsResultMeta.assignResultCode("EXCEPTION");
      wsResultMeta.assignHttpStatusCode(500);

    }

    //set response headers (they should be set at this point, but make sure)
    GrouperServiceUtils.addResponseHeaders(wsResponseBean.getResultMetadata(), false);
    
    //set http status code, content type, and write the response
    try {
      if (warnings.length() > 0) {
        wsResponseBean.getResponseMetadata().appendResultWarning(warnings.toString());
      }
      
      //headers should be there by now
      //set the status code
      response.setStatus(wsResponseBean.getResultMetadata().retrieveHttpStatusCode());

      String restCharset = GrouperWsConfig.retrieveConfig().propertyValueString("ws.restHttpContentTypeCharset");
      String responseContentType = wsRestResponseContentType.getContentType();
      
      if (!StringUtils.isBlank(restCharset)) {
        responseContentType += "; charset=" + restCharset;
      }
      
      response.setContentType(responseContentType);
      
      //init millis for rest since doesnt call getters
      wsResponseBean.getResponseMetadata().getMillis();
      
      boolean wrapJsonResponse = false;

      //maybe response writer
      if(wsRestResponseContentType == WsRestResponseContentType.json && GrouperWsConfig.retrieveConfig().propertyValueBoolean("ws.allowJsonWrapper", true)) {
        
        String grouperJsonResponseWrapper = GrouperServiceJ2ee.parameterValue(parameterMap, request, "grouperJsonResponseWrapper"); 
        if (!StringUtils.isBlank(grouperJsonResponseWrapper)) {
          response.getWriter().print(grouperJsonResponseWrapper);
          response.getWriter().print("(");
          wrapJsonResponse = true;
        }
        
      }
      
      wsRestResponseContentType.writeString(wsResponseBean, response.getWriter());
      
      if (wrapJsonResponse) {
        response.getWriter().print(")");
      }
      
    } catch (RuntimeException re) {
      //problem!
      LOG.error("Problem with request: " + requestDebugInfo(request), re);
    } finally {

      IOUtils.closeQuietly(response.getWriter());
      GrouperWsVersionUtils.removeCurrentClientVersion();
      restRequest.set(null);
    }
    
    HttpSession httpSession = request.getSession(false);
    if (httpSession != null) {
      httpSession.invalidate();
    }

  }

  /**
   * for error messages, get a detailed report of the request
   * @param request
   * @return the string of descriptive result
   */
  public static String requestDebugInfo(HttpServletRequest request) {
    StringBuilder result = new StringBuilder();
    result.append(" uri: ").append(request.getRequestURI());
    result.append(", method: ").append(request.getMethod());
    result.append(", decoded url strings: ");
    List urlStrings = extractUrlStrings(request);
    int urlStringsLength = GrouperUtil.length(urlStrings);
    if (urlStringsLength == 0) {
      result.append("[none]");
    } else {
      for (int i = 0; i < urlStringsLength; i++) {
        result.append(i).append(": '").append(urlStrings.get(i)).append("'");
        if (i != urlStringsLength - 1) {
          result.append(", ");
        }
      }
    }
    return result.toString();
  }

  /**
   * take a request and get the list of url strings for the rest web service
   * @see #extractUrlStrings(String)
   * @param request is the request to get the url strings out of
   * @return the list of url strings
   */
  private static List extractUrlStrings(HttpServletRequest request) {
    String requestResourceFull = request.getRequestURI();
    return extractUrlStrings(requestResourceFull);
  }

  /**
   * 
   * take a request uri and break up the url strings not including the app name or servlet
   * this does not include the url params (if applicable)
   * if the input is: grouper-ws/servicesRest/xhtml/v1_3_000/groups/members
   * then the result is a list of size 2: {"group", "members"}
   * 
   * 
* @param requestResourceFull * @return the url strings */ private static List extractUrlStrings(String requestResourceFull) { String[] requestResources = StringUtils.split(requestResourceFull, '/'); List urlStrings = new ArrayList(); //loop through and decode int index = 0; for (String requestResource : requestResources) { //skip the app name and lite servlet if (index++ < 2) { continue; } //unescape the url encoding urlStrings.add(GrouperUtil.escapeUrlDecode(requestResource)); } return urlStrings; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy