/**
 * Provides services for the space gadget to display a user's spaces and pending spaces.
 *
 * @anchor SpacesRestService
 */
@Path("{portalName}/social/spaces")
public class SpacesRestService implements ResourceContainer {
  private SpaceService _spaceService;
  private IdentityManager _identityManager;
  /** Confirmed Status information */
  private static final String CONFIRMED_STATUS = "confirmed";
  /** Pending Status information */
  private static final String PENDING_STATUS = "pending";
  /** Incoming Status information */
  private static final String INCOMING_STATUS = "incoming";
  /** Public Status information */
  private static final String ALL_SPACES_STATUS = "all_spaces";

  private String portalContainerName;

  /**
   * Qualified name path for rendering url.
   *
   * @since 1.2.2
   */
  private static final QualifiedName PATH = QualifiedName.create("gtn", "path");

  /**
   * Qualified name path for rendering url.
   *
   * @since 1.2.9
   */
  private static final QualifiedName LANG = QualifiedName.create("gtn", "lang");

  /**
   * Qualified name site type for rendering url.
   *
   * @since 1.2.2
   */
  private static final QualifiedName REQUEST_SITE_TYPE = QualifiedName.create("gtn", "sitetype");

  /**
   * Qualified name handler for rendering url.
   *
   * @since 1.2.2
   */
  private static final QualifiedName REQUEST_HANDLER = QualifiedName.create("gtn", "handler");

  /**
   * Qualified name site name for rendering url.
   *
   * @since 1.2.2
   */
  private static final QualifiedName REQUEST_SITE_NAME = QualifiedName.create("gtn", "sitename");

  private static final String ALL_SPACES = "all-spaces";

  /** constructor */
  public SpacesRestService() {}

  /**
   * Gets the current user's spaces and pending spaces.
   *
   * @param uriInfo The requested URI information.
   * @param portalName The name of the current container.
   * @param format The format of the returned result.
   * @anchor SpacesRestService.showMySpaceList
   * @return response
   * @throws Exception @LevelAPI Platform
   * @deprecated Deprecated from 4.3.x. Replaced by a new API {@link
   *     UserRestResourcesV1#getSpacesOfUser(org.exoplatform.social.rest.impl.user.UriInfo, String,
   *     int, int, boolean, String)}
   */
  @GET
  @Path("mySpaces/show.{format}")
  public Response showMySpaceList(
      @Context UriInfo uriInfo,
      @PathParam("portalName") String portalName,
      @PathParam("format") String format)
      throws Exception {
    MediaType mediaType = Util.getMediaType(format);
    ConversationState state = ConversationState.getCurrent();
    portalContainerName = portalName;

    String userId = null;
    if (state != null) {
      userId = state.getIdentity().getUserId();
    }

    Identity identity =
        getIdentityManager().getOrCreateIdentity(OrganizationIdentityProvider.NAME, userId, false);
    if (identity == null) {
      userId = Util.getViewerId(uriInfo);
    }

    SpaceList mySpaceList = showMySpaceList(userId);

    this.fillUrlAllSpaces(mySpaceList, portalName);
    return Util.getResponse(mySpaceList, uriInfo, mediaType, Response.Status.OK);
  }

  /**
   * Provides a way to get the latest spaces ordered by last access and to be able to filter spaces,
   * based on the application Id in the spaces.
   *
   * @param uriInfo The requested URI information.
   * @param portalName The portal container name.
   * @param format The format of the returned result, for example, JSON, or XML.
   * @param offset Specifies the staring point of the returned results. It must be greater than or
   *     equal to 0.
   * @param limit Specifies the ending point of the returned results. It must be less than or equal
   *     to 10.
   * @param appId The application Id which is contained in spaces to filter, such as, Wiki,
   *     Discussion, Documents, Agenda and more.
   * @authentication
   * @request GET:
   *     http://localhost:8080/rest/private/social/spaces/lastVisitedSpace/list.json?appId=Wiki&offset=0&limit=10
   * @response { "spaces":[
   *     {"groupId":"/spaces/space_2","spaceUrl":null,"name":"space_2","displayName":"space
   *     2","url":"space_2"},
   *     {"groupId":"/spaces/space_1","spaceUrl":null,"name":"space_1","displayName":"space
   *     1","url":"space_1"} ], "moreSpacesUrl":null }
   * @return the response @LevelAPI Platform
   * @anchor SpacesRestService.getLastVisitedSpace
   */
  @GET
  @Path("lastVisitedSpace/list.{format}")
  public Response getLastVisitedSpace(
      @Context UriInfo uriInfo,
      @PathParam("portalName") String portalName,
      @PathParam("format") String format,
      @QueryParam("appId") String appId,
      @QueryParam("offset") int offset,
      @QueryParam("limit") int limit)
      throws Exception {
    checkAuthenticatedRequest();

    MediaType mediaType = Util.getMediaType(format, new String[] {format});
    ConversationState state = ConversationState.getCurrent();
    portalContainerName = portalName;

    String userId = null;
    if (state != null) {
      userId = state.getIdentity().getUserId();
    }

    Identity identity =
        getIdentityManager().getOrCreateIdentity(OrganizationIdentityProvider.NAME, userId, false);
    if (identity == null) {
      userId = Util.getViewerId(uriInfo);
    }

    //
    int newLimit = Math.min(limit, 100);
    int newOffset = 0;
    if (offset > 0) {
      newOffset = Math.min(offset, newLimit);
    } else {
      newOffset = 0;
    }

    //
    String newAppId = null;
    if (appId != null && appId.trim().length() > 0) {
      newAppId = appId;
    }

    SpaceList mySpaceList = getLastVisitedSpace(userId, newAppId, newOffset, newLimit);
    return Util.getResponse(mySpaceList, uriInfo, mediaType, Response.Status.OK);
  }

  /**
   * Gets a user's pending spaces.
   *
   * @param uriInfo The requested URI information.
   * @param portalName The portal container name.
   * @param format The format of the returned result, for example, JSON, or XML.
   * @anchor SpacesRestService.showPendingSpaceList
   * @return response
   * @throws Exception @LevelAPI Platform
   */
  @GET
  @Path("pendingSpaces/show.{format}")
  public Response showPendingSpaceList(
      @Context UriInfo uriInfo,
      @PathParam("portalName") String portalName,
      @PathParam("format") String format)
      throws Exception {
    MediaType mediaType = Util.getMediaType(format);
    String userId = ConversationState.getCurrent().getIdentity().getUserId();
    portalContainerName = portalName;
    if (!userId.equals(Util.getViewerId(uriInfo))) {
      return null;
    }
    SpaceList pendingSpaceList = showPendingSpaceList(userId);
    return Util.getResponse(pendingSpaceList, uriInfo, mediaType, Response.Status.OK);
  }

  /**
   * Suggests the space's name for searching.
   *
   * @param uriInfo The requested URI information.
   * @param portalName The name of portal.
   * @param conditionToSearch The input information to search.
   * @param typeOfRelation The type of relationship of the user and the space.
   * @param userId The Id of current user.
   * @param format The format of the returned result, for example, JSON, or XML.
   * @return
   * @throws Exception @LevelAPI Platform
   * @anchor SpacesRestService.suggestSpacenames
   * @deprecated Deprecated from 4.3.x. Replaced by a new API {@link
   *     SpaceRestResourcesV1#getSpaces(org.exoplatform.social.rest.impl.space.UriInfo, String, int,
   *     int, boolean, String)}
   */
  @GET
  @Path("suggest.{format}")
  public Response suggestSpacenames(
      @Context UriInfo uriInfo,
      @PathParam("portalName") String portalName,
      @QueryParam("conditionToSearch") String conditionToSearch,
      @QueryParam("typeOfRelation") String typeOfRelation,
      @QueryParam("currentUser") String userId,
      @PathParam("format") String format)
      throws Exception {

    MediaType mediaType = Util.getMediaType(format);
    SpaceNameList nameList = new SpaceNameList();
    portalContainerName = portalName;
    SpaceService spaceSrv = getSpaceService();

    SpaceListAccess listAccess =
        spaceSrv.getVisibleSpacesWithListAccess(userId, new SpaceFilter(conditionToSearch));
    List<Space> spaces = Arrays.asList(listAccess.load(0, 10));

    for (Space space : spaces) {
      if (ALL_SPACES_STATUS.equals(typeOfRelation)) {
        nameList.addName(space.getDisplayName());
      } else {
        if (PENDING_STATUS.equals(typeOfRelation) && (spaceSrv.isPending(space, userId))) {
          nameList.addName(space.getDisplayName());
          continue;
        } else if (INCOMING_STATUS.equals(typeOfRelation) && (spaceSrv.isInvited(space, userId))) {
          nameList.addName(space.getDisplayName());
          continue;
        } else if (CONFIRMED_STATUS.equals(typeOfRelation) && (spaceSrv.isMember(space, userId))) {
          nameList.addName(space.getDisplayName());
          continue;
        }
      }
    }

    return Util.getResponse(nameList, uriInfo, mediaType, Response.Status.OK);
  }

  /**
   * List that contains space from space service.<br>
   * Need this class for converter from rest service.
   */
  @XmlRootElement
  public static class SpaceList {
    private String moreSpacesUrl;

    private List<SpaceRest> _spaces;

    /**
     * sets space list
     *
     * @param spaces space list
     */
    public void setSpaces(List<SpaceRest> spaces) {
      _spaces = spaces;
    }

    /**
     * gets space list
     *
     * @return space list
     */
    public List<SpaceRest> getSpaces() {
      return _spaces;
    }

    /**
     * adds space to space list
     *
     * @param space
     * @see Space
     */
    public void addSpace(SpaceRest space) {
      if (_spaces == null) {
        _spaces = new LinkedList<SpaceRest>();
      }
      _spaces.add(space);
    }

    /**
     * Get the url of all spaces.
     *
     * @return
     * @since 1.2.9
     */
    public String getMoreSpacesUrl() {
      return moreSpacesUrl;
    }

    /**
     * Set the url of all spaces.
     *
     * @param allSpacesUrl
     * @since 1.2.9
     */
    public void setMoreSpacesUrl(String allSpacesUrl) {
      moreSpacesUrl = allSpacesUrl;
    }
  }

  @XmlRootElement
  public static class SpaceNameList {
    private List<String> _names;

    /**
     * Sets space name list
     *
     * @param space name list
     */
    public void setNames(List<String> names) {
      this._names = names;
    }

    /**
     * Gets space name list
     *
     * @return space name list
     */
    public List<String> getNames() {
      return _names;
    }

    /**
     * Add name to space name list
     *
     * @param space name
     */
    public void addName(String name) {
      if (_names == null) {
        _names = new ArrayList<String>();
      }
      _names.add(name);
    }
  }

  /**
   * shows my spaceList by userId
   *
   * @param userId
   * @return spaceList
   * @see SpaceList
   */
  private SpaceList showMySpaceList(String userId) {
    SpaceList spaceList = new SpaceList();
    _spaceService = getSpaceService();
    List<Space> mySpaces = null;
    List<SpaceRest> mySpacesRest = new ArrayList<SpaceRest>();
    try {
      mySpaces = _spaceService.getSpaces(userId);

      for (Space space : mySpaces) {
        SpaceRest spaceRest = new SpaceRest(space);
        mySpacesRest.add(spaceRest);
      }

      // fix for issue SOC-2039, sets the space url with new navigation controller
      Router router = this.getRouter(this.getConfigurationPath());

      this.fillSpacesURI(mySpacesRest, router);
    } catch (SpaceException e) {
      throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
    } catch (Exception e) {
      throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
    }

    spaceList.setSpaces(mySpacesRest);
    return spaceList;
  }

  /**
   * get my spaceList by userId which user is last visited
   *
   * @param userId
   * @param appId
   * @param limit
   * @return
   */
  private SpaceList getLastVisitedSpace(String userId, String appId, int offset, int limit) {
    SpaceList spaceList = new SpaceList();
    _spaceService = getSpaceService();
    List<Space> mySpaces = null;

    try {
      mySpaces = _spaceService.getLastAccessedSpace(userId, appId, offset, limit);
      SpaceRest spaceRest;
      for (Space space : mySpaces) {
        spaceRest = new SpaceRest(space);
        spaceList.addSpace(spaceRest);
      }
    } catch (SpaceException e) {
      throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
    } catch (Exception e) {
      throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
    }
    return spaceList;
  }

  /**
   * shows pending spaceList by userId
   *
   * @param userId
   * @return spaceList
   * @see SpaceList
   */
  private SpaceList showPendingSpaceList(String userId) {
    SpaceList spaceList = new SpaceList();
    _spaceService = getSpaceService();
    List<Space> pendingSpaces;
    List<SpaceRest> pendingSpacesRest = new ArrayList<SpaceRest>();
    try {
      pendingSpaces = _spaceService.getPendingSpaces(userId);
      for (Space space : pendingSpaces) {
        SpaceRest spaceRest = new SpaceRest(space);
        pendingSpacesRest.add(spaceRest);
      }
    } catch (SpaceException e) {
      throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
    }
    spaceList.setSpaces(pendingSpacesRest);
    return spaceList;
  }

  /**
   * Fill url for more spaces.
   *
   * @param spaceList
   * @param portalOwner
   * @since 1.2.9
   */
  private void fillUrlAllSpaces(SpaceList spaceList, String portalOwner) {
    try {
      Router router = this.getRouter(this.getConfigurationPath());

      Map<QualifiedName, String> qualifiedName = new HashedMap();
      qualifiedName.put(REQUEST_HANDLER, "portal");
      qualifiedName.put(REQUEST_SITE_TYPE, "portal");
      qualifiedName.put(LANG, "");

      StringBuilder urlBuilder = new StringBuilder();
      qualifiedName.put(REQUEST_SITE_NAME, portalOwner);
      qualifiedName.put(PATH, ALL_SPACES);
      router.render(qualifiedName, new URIWriter(urlBuilder));
      spaceList.setMoreSpacesUrl(urlBuilder.toString());
    } catch (Exception e) {
      throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
    }
  }

  /**
   * gets spaceService
   *
   * @return spaceService
   * @see SpaceService
   */
  private SpaceService getSpaceService() {
    return (SpaceService) getPortalContainer().getComponentInstanceOfType(SpaceService.class);
  }

  /**
   * gets identityManager
   *
   * @return
   */
  private IdentityManager getIdentityManager() {
    if (_identityManager == null) {
      _identityManager =
          (IdentityManager) getPortalContainer().getComponentInstanceOfType(IdentityManager.class);
    }
    return _identityManager;
  }

  private PortalContainer getPortalContainer() {
    return (PortalContainer) ExoContainerContext.getContainerByName(portalContainerName);
  }

  /**
   * Fills the spaces uri.
   *
   * @param mySpaces
   * @param router
   * @since 1.2.2
   */
  @SuppressWarnings("unchecked")
  private void fillSpacesURI(List<SpaceRest> mySpaces, Router router) {
    try {
      Map<QualifiedName, String> qualifiedName = new HashedMap();
      qualifiedName.put(REQUEST_HANDLER, "portal");
      qualifiedName.put(REQUEST_SITE_TYPE, "group");

      for (SpaceRest space : mySpaces) {
        StringBuilder urlBuilder = new StringBuilder();
        qualifiedName.put(REQUEST_SITE_NAME, space.getGroupId());
        qualifiedName.put(PATH, space.getUrl());
        router.render(qualifiedName, new URIWriter(urlBuilder));
        space.setSpaceUrl(urlBuilder.toString());
      }
    } catch (Exception e) {
      throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
    }
  }

  /**
   * Gets the configuration path of file controller.xml
   *
   * @return
   * @since 1.2.2
   */
  private String getConfigurationPath() {
    PortalContainer portalContainer = this.getPortalContainer();
    WebAppController webAppController =
        (WebAppController) portalContainer.getComponentInstanceOfType(WebAppController.class);
    return webAppController.getConfigurationPath();
  }

  /**
   * Gets the router from path of file controller.xml
   *
   * @param path
   * @return
   * @throws IOException
   * @throws RouterConfigException
   * @since 1.2.2
   */
  private Router getRouter(String path) throws IOException, RouterConfigException {
    File f = new File(path);
    if (!f.exists()) {
      throw new MalformedURLException("Could not resolve path " + path);
    }
    if (!f.isFile()) {
      throw new MalformedURLException("Could not resolve path " + path + " to a valid file");
    }
    return this.getRouter(f.toURI().toURL());
  }

  /**
   * Gets the router from url.
   *
   * @param url
   * @return
   * @throws RouterConfigException
   * @throws IOException
   * @since 1.2.2
   */
  private Router getRouter(URL url) throws RouterConfigException, IOException {
    InputStream in = url.openStream();
    try {
      ControllerDescriptor routerDesc = new DescriptorBuilder().build(in);
      return new Router(routerDesc);
    } finally {
      Safe.close(in);
    }
  }
}
Example #2
0
  /**
   * Append a path, creates the necessary routes and returns the last route added.
   *
   * @param pathParamDescriptors the path param descriptors
   * @param path the path to append
   * @return the last route added
   */
  private Route append(Map<QualifiedName, PathParamDescriptor> pathParamDescriptors, String path)
      throws MalformedRouteException {
    if (path.length() == 0 || path.charAt(0) != '/') {
      throw new MalformedRouteException();
    }

    //
    int pos = path.length();
    int level = 0;
    List<Integer> start = new ArrayList<Integer>();
    List<Integer> end = new ArrayList<Integer>();
    for (int i = 1; i < path.length(); i++) {
      char c = path.charAt(i);
      if (c == '{') {
        if (level++ == 0) {
          start.add(i);
        }
      } else if (c == '}') {
        if (--level == 0) {
          end.add(i);
        }
      } else if (c == '/') {
        if (level == 0) {
          pos = i;
          break;
        }
      }
    }

    //
    Route next;
    if (start.isEmpty()) {
      String segment = path.substring(1, pos);
      SegmentRoute route = new SegmentRoute(router, segment);
      add(route);
      next = route;
    } else {
      if (start.size() == end.size()) {
        PatternBuilder builder = new PatternBuilder();
        builder.expr("^").expr('/');
        List<String> chunks = new ArrayList<String>();
        List<PathParam> parameterPatterns = new ArrayList<PathParam>();

        //
        int previous = 1;
        for (int i = 0; i < start.size(); i++) {
          builder.litteral(path, previous, start.get(i));
          chunks.add(path.substring(previous, start.get(i)));
          String parameterName = path.substring(start.get(i) + 1, end.get(i));

          //
          QualifiedName parameterQName = QualifiedName.parse(parameterName);

          // Now get path param metadata
          PathParamDescriptor parameterDescriptor = pathParamDescriptors.get(parameterQName);

          //
          PathParam param;
          if (parameterDescriptor != null) {
            param = PathParam.create(parameterDescriptor, router);
          } else {
            param = PathParam.create(parameterQName, router);
          }

          // Append routing regex to the route regex surrounded by a non capturing regex
          // to isolate routingRegex like a|b or a(.)b
          builder.expr("(?:").expr(param.routingRegex).expr(")");

          // Add the path param with the rendering regex
          parameterPatterns.add(param);
          previous = end.get(i) + 1;
        }

        //
        builder.litteral(path, previous, pos);

        // We want to satisfy one of the following conditions
        // - the next char after the matched expression is '/'
        // - the expression matched until the end
        // - the match expression is the '/' expression
        builder.expr("(?:(?<=^/)|(?=/)|$)");

        //
        chunks.add(path.substring(previous, pos));
        PatternRoute route =
            new PatternRoute(router, router.compile(builder.build()), parameterPatterns, chunks);

        // Wire
        add(route);

        //
        next = route;
      } else {
        throw new UnsupportedOperationException("Report error");
      }
    }

    //
    if (pos < path.length()) {
      return next.append(pathParamDescriptors, path.substring(pos));
    } else {
      return next;
    }
  }
/** @author <a href="mailto:[email protected]">Julien Viet</a> */
public class ResourceRequestHandler extends WebRequestHandler {

  public static final String IF_MODIFIED_SINCE = "If-Modified-Since";

  public static final String LAST_MODIFIED = "Last-Modified";

  /** . */
  private static String PATH =
      "META-INF/maven/org.exoplatform.portal/exo.portal.component.web.resources/pom.properties";

  /** . */
  private static final Logger log = LoggerFactory.getLogger(ResourceRequestHandler.class);

  /** . */
  public static final String VERSION;

  static {
    // Detecting version from maven properties
    // empty value is ok
    String version = "";
    URL url = ResourceRequestHandler.class.getClassLoader().getResource(PATH);
    if (url != null) {
      log.debug("Loading resource serving version from " + url);
      InputStream in = null;
      try {
        in = url.openStream();
        Properties props = new Properties();
        props.load(in);
        version = props.getProperty("version");
      } catch (IOException e) {
        log.error("Could not read properties from " + url, e);
      } finally {
        IOTools.safeClose(in);
      }
    }

    //
    log.info("Use version \"" + version + "\" for resource serving");
    VERSION = version;
  }

  /** . */
  public static final QualifiedName VERSION_QN = QualifiedName.create("gtn", "version");

  /** . */
  public static final QualifiedName RESOURCE_QN = QualifiedName.create("gtn", "resource");

  /** . */
  public static final QualifiedName SCOPE_QN = QualifiedName.create("gtn", "scope");

  /** . */
  public static final QualifiedName MODULE_QN = QualifiedName.create("gtn", "module");

  /** . */
  public static final QualifiedName COMPRESS_QN = QualifiedName.create("gtn", "compress");

  /** . */
  public static final QualifiedName LANG_QN = QualifiedName.create("gtn", "lang");

  /** . */
  private final FutureMap<ScriptKey, ScriptResult, ControllerContext> cache;

  public ResourceRequestHandler() {
    this.cache = new FutureMap<ScriptKey, ScriptResult, ControllerContext>(new ScriptLoader());
  }

  @Override
  public String getHandlerName() {
    return "script";
  }

  @Override
  public boolean execute(ControllerContext context) throws Exception {
    String resourceParam = context.getParameter(RESOURCE_QN);
    String scopeParam = context.getParameter(SCOPE_QN);

    //
    if (scopeParam != null && resourceParam != null) {
      String compressParam = context.getParameter(COMPRESS_QN);
      String lang = context.getParameter(LANG_QN);
      String moduleParam = context.getParameter(MODULE_QN);

      //
      Locale locale = null;
      if (lang != null && lang.length() > 0) {
        locale = I18N.parseTagIdentifier(lang);
      }

      //
      ResourceScope scope;
      try {
        scope = ResourceScope.valueOf(ResourceScope.class, scopeParam);
      } catch (IllegalArgumentException e) {
        HttpServletResponse response = context.getResponse();
        String msg = "Unrecognized scope " + scopeParam;
        log.error(msg);
        response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        return true;
      }

      //
      ResourceId resource = new ResourceId(scope, resourceParam);

      ScriptKey key = new ScriptKey(resource, moduleParam, "min".equals(compressParam), locale);

      //
      ScriptResult result = cache.get(context, key);
      HttpServletResponse response = context.getResponse();
      HttpServletRequest request = context.getRequest();

      //
      if (result instanceof ScriptResult.Resolved) {
        ScriptResult.Resolved resolved = (ScriptResult.Resolved) result;

        // Content type + charset
        response.setContentType("text/javascript");
        response.setCharacterEncoding("UTF-8");

        // One hour caching
        // make this configurable later
        response.setHeader("Cache-Control", "max-age:3600");
        response.setDateHeader("Expires", System.currentTimeMillis() + 3600 * 1000);

        // Set content length
        response.setContentLength(resolved.bytes.length);

        long ifModifiedSince = request.getDateHeader(IF_MODIFIED_SINCE);
        if (resolved.isModified(ifModifiedSince)) {
          response.setDateHeader(ResourceRequestFilter.LAST_MODIFIED, resolved.lastModified);
          // Send bytes
          ServletOutputStream out = response.getOutputStream();
          try {
            out.write(resolved.bytes);
          } finally {
            Safe.close(out);
          }
        } else {
          response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
        }
      } else if (result instanceof ScriptResult.Error) {
        ScriptResult.Error error = (ScriptResult.Error) result;
        log.error("Could not render script " + key + "\n:" + error.message);
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      } else {
        String msg = "Resource " + key + " cannot be found";
        log.error(msg);
        response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
      }
    } else {
      HttpServletResponse response = context.getResponse();
      String msg = "Missing scope or resource param";
      log.error(msg);
      response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    }

    //
    return true;
  }

  @Override
  protected boolean getRequiresLifeCycle() {
    return false;
  }
}