@PUT
  @Path("/{id}")
  @Produces({MediaType.APPLICATION_JSON, "text/uri-list"})
  @ApiOperation(
      value = "Places a new Feature at a particular URI",
      notes =
          "Creates a new Feature entry at the specified URI. If a Feature already exists at this URI,"
              + "it will be replaced. If, instead, no Feature is stored under the specified URI, a new "
              + "Feature entry will be created. Notice that authentication, authorization and accounting (quota) "
              + "restrictions may apply.",
      response = Feature.class,
      position = 4)
  @Consumes(MediaType.APPLICATION_JSON)
  @ApiResponses(
      value = {
        @ApiResponse(code = 200, message = "Feature entry was created successfully."),
        @ApiResponse(
            code = 400,
            message = "Feature entry was not created because the request was malformed"),
        @ApiResponse(
            code = 401,
            message = "You are not authorized to create a feature on the server"),
        @ApiResponse(
            code = 403,
            message = "This request is forbidden (e.g., no authentication token is provided)"),
        @ApiResponse(code = 500, message = "Internal server error - this request cannot be served.")
      })
  public Response putFeature(
      @ApiParam(value = "ID of the Feature.", required = true) @PathParam("id") String id,
      @ApiParam(value = "Feature in JSON", defaultValue = DEFAULT_FEATURE, required = true)
          Feature feature,
      @ApiParam("Clients need to authenticate in order to create resources on the server")
          @HeaderParam("subjectid")
          String subjectId) {
    if (feature == null) {
      ErrorReport report =
          ErrorReportFactory.badRequest(
              "No feature provided; check out the API specs",
              "Clients MUST provide a Feature document in JSON to perform this request");
      return Response.ok(report).status(Response.Status.BAD_REQUEST).build();
    }
    feature.setId(id);

    Feature foundFeature = featureHandler.find(id);
    if (foundFeature != null) {
      featureHandler.edit(feature);
    } else {
      featureHandler.create(feature);
    }

    return Response.ok(feature)
        .status(Response.Status.CREATED)
        .header("Location", uriInfo.getBaseUri().toString() + "feature/" + feature.getId())
        .build();
  }
 @DELETE
 @Path("/{id}")
 @Produces({MediaType.APPLICATION_JSON, "text/uri-list"})
 @ApiOperation(
     value = "Deletes a particular Feature resource.",
     notes =
         "Deletes a Feature of a given ID. The method is idempondent, that is, it can be used more than once without "
             + "triggering an exception/error. If the Feature does not exist, the method will return without errors. "
             + "Authentication and authorization requirements apply, so clients that are not authenticated with a "
             + "valid token or do not have sufficient priviledges will not be able to delete a Feature using this method.")
 @ApiResponses(
     value = {
       @ApiResponse(code = 200, message = "Feature entry was deleted successfully."),
       @ApiResponse(code = 401, message = "You are not authorized to delete this resource"),
       @ApiResponse(
           code = 403,
           message = "This request is forbidden (e.g., no authentication token is provided)"),
       @ApiResponse(code = 500, message = "Internal server error - this request cannot be served.")
     })
 public Response deleteFeature(
     @ApiParam("Clients need to authenticate in order to create resources on the server")
         @HeaderParam("subjectid")
         String subjectId,
     @ApiParam(value = "ID of the Model.", required = true) @PathParam("id") String id) {
   featureHandler.remove(new Feature(id));
   return Response.ok().build();
 }
 @GET
 @Produces({MediaType.APPLICATION_JSON, "text/uri-list"})
 @Path("/{id}")
 @ApiOperation(
     value = "Finds Feature by ID",
     notes = "Finds specified Feature (by ID)",
     response = Feature.class)
 public Response getFeature(@PathParam("id") String id) {
   Feature feature = featureHandler.find(id);
   if (feature == null) {
     throw new NotFoundException("Could not find Dataset with id:" + id);
   }
   return Response.ok(feature).build();
 }
 @GET
 @Produces({MediaType.APPLICATION_JSON, "text/uri-list"})
 @ApiOperation(
     value = "Lists features",
     notes =
         "Lists Feature entries in the DB of Jaqpot and returns them in a list. "
             + "Results can be obtained "
             + "either in the form of a URI list or as a JSON list as specified by the Accept HTTP header. "
             + "In the latter case, a list will be returned containing only the IDs of the features, their metadata "
             + "and their ontological classes. The parameter max, which specifies the maximum number of IDs to be "
             + "listed is limited to 500; if the client specifies a larger value, an HTTP Warning Header will be "
             + "returned (RFC 2616) with code P670.",
     response = Feature.class,
     responseContainer = "List")
 @ApiResponses(
     value = {
       @ApiResponse(
           code = 200,
           message = "Feature entries found and are listed in the response body"),
       @ApiResponse(code = 401, message = "You are not authorized to access this user"),
       @ApiResponse(
           code = 403,
           message = "This request is forbidden (e.g., no authentication token is provided)"),
       @ApiResponse(code = 500, message = "Internal server error - this request cannot be served.")
     })
 public Response listFeatures(
     @ApiParam("Creator of the feature") @QueryParam("creator") String creator,
     @ApiParam("Generic query") @QueryParam("query") String query,
     @ApiParam(value = "start", defaultValue = "0") @QueryParam("start") Integer start,
     @ApiParam(
             value = "max - the server imposes an upper limit of 500 on this " + "parameter.",
             defaultValue = "10")
         @QueryParam("max")
         Integer max) {
   // TODO Support querying at GET /feature
   boolean doWarnMax = false;
   if (max == null || max > 500) {
     max = 500;
     doWarnMax = true;
   }
   Response.ResponseBuilder responseBuilder =
       Response.ok(featureHandler.listOnlyIDs(start != null ? start : 0, max))
           .status(Response.Status.OK);
   if (doWarnMax) {
     responseBuilder.header("Warning", "P670 Parameter max has been limited to 500");
   }
   return responseBuilder.build();
 }
  @POST
  @Produces({MediaType.APPLICATION_JSON, "text/uri-list"})
  @Consumes(MediaType.APPLICATION_JSON)
  @ApiOperation(
      value = "Creates a new Feature",
      notes =
          "Creates a new feature which is assigned a random unique ID. When creating a new feature, clients must wary not only for "
              + "its syntactic correctness, but also for its semantic completeness. It is strongly recommended to add a comprehensive and "
              + "identifying title to your feature using the <code>meta.titles</code> field, to add a description in <code>meta.descriptions</code> "
              + "and also to add a list of tags in <code>meta.subjects</code> that will facilitate the discoverability of your features later. "
              + "Additionally, all features should be annotated with appropriate ontological classes (from the OpenTox ontology), such as "
              + "<code>ot:Feature</code>, <code>ot:NumericFeature</code> and <code>ot:NominalFeature</code>. Features that are created as "
              + "prediction features for a model or are descriptors that can be calculated using a descriptor calculation web "
              + "service should be linked to this/these service(s) using <code>meta.hasSources</code>. Finally, nominal features should define their "
              + "admissible values in <code>admissibleValues</code>. Malformed feature documents will not be accepted by the server and an "
              + "error report will be generated and returned to the client. Notice also that authentication, authorization and accounting "
              + "restrictions may apply.",
      response = Feature.class)
  @ApiResponses(
      value = {
        @ApiResponse(code = 200, message = "Feature was created successfully."),
        @ApiResponse(code = 400, message = "Bad request: malformed feature"),
        @ApiResponse(code = 401, message = "You are not authorized to access this resource"),
        @ApiResponse(
            code = 403,
            message = "This request is forbidden (e.g., no authentication token is provided)"),
        @ApiResponse(code = 500, message = "Internal server error - this request cannot be served.")
      })
  @Authorize
  public Response createFeature(
      @ApiParam(value = "Clients need to authenticate in order to create resources on the server")
          @HeaderParam("subjectid")
          String subjectId,
      @ApiParam(
              value =
                  "Feature in JSON representation compliant with the Feature specifications. "
                      + "Malformed Feature entries with missing fields will not be accepted.",
              required = true,
              defaultValue = DEFAULT_FEATURE)
          Feature feature)
      throws JaqpotNotAuthorizedException {
    if (feature == null) {
      ErrorReport report =
          ErrorReportFactory.badRequest(
              "No feature provided; check out the API specs",
              "Clients MUST provide a Feature document in JSON to perform this request");
      return Response.ok(report).status(Response.Status.BAD_REQUEST).build();
    }
    if (feature.getMeta() == null) {
      feature.setMeta(new MetaInfo());
    }
    feature.getMeta().setDate(new Date());
    ROG rog = new ROG(true);
    if (feature.getId() == null) {
      feature.setId(rog.nextString(10));
    }
    feature.setCreatedBy(securityContext.getUserPrincipal().getName());

    featureHandler.create(feature);
    return Response.ok(feature)
        .status(Response.Status.OK)
        .header("Location", uriInfo.getBaseUri().toString() + "feature/" + feature.getId())
        .build();
  }