Java tutorial
/** * This file is part of Graylog. * * Graylog 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. * * Graylog 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 Graylog. If not, see <http://www.gnu.org/licenses/>. */ package org.graylog.plugins.collector.configurations.rest.resources; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.hash.Hashing; import io.swagger.annotations.*; import org.apache.shiro.authz.annotation.RequiresAuthentication; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.graylog.plugins.collector.audit.CollectorAuditEventTypes; import org.graylog.plugins.collector.configurations.CollectorConfigurationService; import org.graylog.plugins.collector.configurations.rest.models.*; import org.graylog.plugins.collector.configurations.rest.responses.CollectorConfigurationListResponse; import org.graylog.plugins.collector.permissions.CollectorRestPermissions; import org.graylog2.audit.jersey.AuditEvent; import org.graylog2.database.NotFoundException; import org.graylog2.plugin.rest.PluginRestResource; import org.graylog2.shared.rest.resources.RestResource; import org.hibernate.validator.constraints.NotEmpty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.validation.Valid; import javax.validation.constraints.NotNull; import javax.ws.rs.*; import javax.ws.rs.core.*; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @Api(value = "CollectorConfiguration", description = "Manage collector configurations") @Path("/") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public class CollectorConfigurationResource extends RestResource implements PluginRestResource { private static final Logger LOG = LoggerFactory.getLogger(CollectorConfigurationResource.class); private final ObjectMapper mapper = new ObjectMapper(); private final CollectorConfigurationService collectorConfigurationService; private static List<String> validEtags = Collections.synchronizedList(new ArrayList<String>()); @Inject public CollectorConfigurationResource(CollectorConfigurationService collectorConfigurationService) { this.collectorConfigurationService = collectorConfigurationService; } @GET @Path("/{collectorId}") @Produces(MediaType.APPLICATION_JSON) @ApiOperation(value = "Get a single collector configuration") @ApiResponses(value = { @ApiResponse(code = 404, message = "Collector not found."), @ApiResponse(code = 400, message = "Invalid ObjectId."), @ApiResponse(code = 304, message = "Configuration didn't update.") }) public Response getConfiguration(@Context Request request, @Context HttpHeaders httpHeaders, @ApiParam(name = "collectorId", required = true) @PathParam("collectorId") String collectorId, @ApiParam(name = "tags") @QueryParam("tags") String queryTags) throws NotFoundException { List tags = parseQueryTags(queryTags); CollectorConfiguration collectorConfiguration; String ifNoneMatch = httpHeaders.getHeaderString("If-None-Match"); Boolean etagCached = false; Response.ResponseBuilder builder = Response.noContent(); // check if client is up to date with a known valid etag if (ifNoneMatch != null) { EntityTag etag = new EntityTag(ifNoneMatch.replaceAll("\"", "")); etagCached = validEtags.contains(etag.toString()); if (etagCached) { etagCached = true; builder = Response.notModified(); builder.tag(etag); } } // fetch configuration from database if client is outdated if (tags != null && !etagCached) { List<CollectorConfiguration> collectorConfigurationList = collectorConfigurationService .findByTags(tags); collectorConfiguration = collectorConfigurationService.merge(collectorConfigurationList); String hashCode; if (collectorConfiguration != null) { collectorConfiguration.tags().addAll(tags); // add new etag to cache hashCode = Hashing.md5().hashInt(collectorConfiguration.hashCode()) // avoid negative values .toString(); EntityTag collectorConfigurationEtag = new EntityTag(hashCode); builder = Response.ok(collectorConfiguration); builder.tag(collectorConfigurationEtag); if (!validEtags.contains(collectorConfigurationEtag.toString())) { validEtags.add(collectorConfigurationEtag.toString()); } } } // set cache control CacheControl cacheControl = new CacheControl(); cacheControl.setNoTransform(true); cacheControl.setPrivate(true); builder.cacheControl(cacheControl); return builder.build(); } @GET @Path("/configurations") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_READ) @Produces(MediaType.APPLICATION_JSON) @ApiOperation(value = "List all collector configurations") public CollectorConfigurationListResponse listConfigurations() { final List<CollectorConfigurationSummary> result = this.collectorConfigurationService.loadAll().stream() .map(collectorConfiguration -> getCollectorConfigurationSummary(collectorConfiguration)) .collect(Collectors.toList()); return CollectorConfigurationListResponse.create(result.size(), result); } @GET @Path("/configurations/tags") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_READ) @Produces(MediaType.APPLICATION_JSON) @ApiOperation(value = "List all used tags") public List<String> getTags() { return collectorConfigurationService.loadAllTags(); } @GET @Path("/configurations/{id}") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_READ) @Produces(MediaType.APPLICATION_JSON) @ApiOperation(value = "Show collector configuration details") public CollectorConfiguration getConfigurations( @ApiParam(name = "id", required = true) @PathParam("id") @NotEmpty String id) { return this.collectorConfigurationService.findById(id); } @PUT @Path("/configurations/{id}/tags") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_UPDATE) @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @AuditEvent(type = CollectorAuditEventTypes.TAGS_UPDATE) public CollectorConfiguration updateTags(@ApiParam(name = "id", required = true) @PathParam("id") String id, @ApiParam(name = "JSON body", required = true) List<String> tags) { validEtags.clear(); final CollectorConfiguration collectorConfiguration = collectorConfigurationService.withTagsFromRequest(id, tags); collectorConfigurationService.save(collectorConfiguration); return collectorConfiguration; } @PUT @Path("/configurations/{id}/outputs/{output_id}") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_UPDATE) @ApiOperation(value = "Update a configuration output", notes = "This is a stateless method which updates a collector output") @ApiResponses(value = { @ApiResponse(code = 400, message = "The supplied request is not valid.") }) @AuditEvent(type = CollectorAuditEventTypes.OUTPUT_UPDATE) public Response updateOutput(@ApiParam(name = "id", required = true) @PathParam("id") @NotEmpty String id, @ApiParam(name = "output_id", required = true) @PathParam("output_id") @NotEmpty String outputId, @ApiParam(name = "JSON body", required = true) @Valid @NotNull CollectorOutput request) { validEtags.clear(); final CollectorConfiguration collectorConfiguration = collectorConfigurationService .updateOutputFromRequest(id, outputId, request); collectorConfigurationService.save(collectorConfiguration); return Response.accepted().build(); } @PUT @Path("/configurations/{id}/inputs/{input_id}") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_UPDATE) @ApiOperation(value = "Update a configuration input", notes = "This is a stateless method which updates a collector input") @ApiResponses(value = { @ApiResponse(code = 400, message = "The supplied request is not valid.") }) @AuditEvent(type = CollectorAuditEventTypes.INPUT_UPDATE) public Response updateInput(@ApiParam(name = "id", required = true) @PathParam("id") @NotEmpty String id, @ApiParam(name = "input_id", required = true) @PathParam("input_id") @NotEmpty String inputId, @ApiParam(name = "JSON body", required = true) @Valid @NotNull CollectorInput request) { validEtags.clear(); final CollectorConfiguration collectorConfiguration = collectorConfigurationService .updateInputFromRequest(id, inputId, request); collectorConfigurationService.save(collectorConfiguration); return Response.accepted().build(); } @PUT @Path("/configurations/{id}/snippets/{snippet_id}") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_UPDATE) @ApiOperation(value = "Update a configuration snippet", notes = "This is a stateless method which updates a collector snippet") @ApiResponses(value = { @ApiResponse(code = 400, message = "The supplied request is not valid.") }) @AuditEvent(type = CollectorAuditEventTypes.SNIPPET_UPDATE) public Response updateSnippet(@ApiParam(name = "id", required = true) @PathParam("id") @NotEmpty String id, @ApiParam(name = "snippet_id", required = true) @PathParam("snippet_id") @NotEmpty String snippetId, @ApiParam(name = "JSON body", required = true) @Valid @NotNull CollectorConfigurationSnippet request) { validEtags.clear(); final CollectorConfiguration collectorConfiguration = collectorConfigurationService .updateSnippetFromRequest(id, snippetId, request); collectorConfigurationService.save(collectorConfiguration); return Response.accepted().build(); } @POST @Path("/configurations") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_CREATE) @Produces(MediaType.APPLICATION_JSON) @ApiOperation(value = "Create new collector configuration") @AuditEvent(type = CollectorAuditEventTypes.CONFIGURATION_CREATE) public CollectorConfiguration createConfiguration( @ApiParam(name = "createDefaults") @QueryParam("createDefaults") RestBoolean createDefaults, @ApiParam(name = "JSON body", required = true) @Valid @NotNull CollectorConfiguration request) { validEtags.clear(); CollectorConfiguration collectorConfiguration; if (createDefaults != null && createDefaults.getValue()) { collectorConfiguration = collectorConfigurationService.fromRequestWithDefaultSnippets(request); } else { collectorConfiguration = collectorConfigurationService.fromRequest(request); } collectorConfigurationService.save(collectorConfiguration); return collectorConfiguration; } @PUT @Path("/configurations/{id}/name") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_UPDATE) @Produces(MediaType.APPLICATION_JSON) @ApiOperation(value = "Updates a collector configuration name") @AuditEvent(type = CollectorAuditEventTypes.CONFIGURATION_UPDATE) public CollectorConfiguration updateConfigurationName(@ApiParam(name = "id") @PathParam("id") String id, @ApiParam(name = "JSON body", required = true) @Valid @NotNull CollectorConfiguration request) { validEtags.clear(); final CollectorConfiguration persistedConfiguration = collectorConfigurationService.findById(id); final CollectorConfiguration newConfiguration = collectorConfigurationService.fromRequest(request); final CollectorConfiguration updatedConfiguration = persistedConfiguration.toBuilder() .name(newConfiguration.name()).build(); collectorConfigurationService.save(updatedConfiguration); return updatedConfiguration; } @POST @Path("/configurations/{id}/outputs") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_CREATE) @ApiOperation(value = "Create a configuration output", notes = "This is a stateless method which inserts a collector output") @ApiResponses(value = { @ApiResponse(code = 400, message = "The supplied request is not valid.") }) @AuditEvent(type = CollectorAuditEventTypes.OUTPUT_CREATE) public Response createOutput(@ApiParam(name = "id", required = true) @PathParam("id") @NotEmpty String id, @ApiParam(name = "JSON body", required = true) @Valid @NotNull CollectorOutput request) { validEtags.clear(); final CollectorConfiguration collectorConfiguration = collectorConfigurationService .withOutputFromRequest(id, request); collectorConfigurationService.save(collectorConfiguration); return Response.accepted().build(); } @POST @Path("/configurations/{id}/inputs") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_CREATE) @ApiOperation(value = "Create a configuration input", notes = "This is a stateless method which inserts a collector input") @ApiResponses(value = { @ApiResponse(code = 400, message = "The supplied request is not valid.") }) @AuditEvent(type = CollectorAuditEventTypes.INPUT_CREATE) public Response createInput(@ApiParam(name = "id", required = true) @PathParam("id") @NotEmpty String id, @ApiParam(name = "JSON body", required = true) @Valid @NotNull CollectorInput request) { validEtags.clear(); final CollectorConfiguration collectorConfiguration = collectorConfigurationService.withInputFromRequest(id, request); collectorConfigurationService.save(collectorConfiguration); return Response.accepted().build(); } @POST @Path("/configurations/{id}/snippets") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_CREATE) @ApiOperation(value = "Create a configuration snippet", notes = "This is a stateless method which inserts a collector configuration snippet") @ApiResponses(value = { @ApiResponse(code = 400, message = "The supplied request is not valid.") }) @AuditEvent(type = CollectorAuditEventTypes.SNIPPET_CREATE) public Response createSnippet(@ApiParam(name = "id", required = true) @PathParam("id") @NotEmpty String id, @ApiParam(name = "JSON body", required = true) @Valid @NotNull CollectorConfigurationSnippet request) { validEtags.clear(); final CollectorConfiguration collectorConfiguration = collectorConfigurationService .withSnippetFromRequest(id, request); collectorConfigurationService.save(collectorConfiguration); return Response.accepted().build(); } @POST @Path("/configurations/{id}/{name}") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_CREATE) @ApiOperation(value = "Copy a configuration", notes = "This is a stateless method which copies a collector configuration to one with another name") @ApiResponses(value = { @ApiResponse(code = 404, message = "Configuration not found."), @ApiResponse(code = 400, message = "Invalid ObjectId.") }) @AuditEvent(type = CollectorAuditEventTypes.CONFIGURATION_CLONE) public Response copyConfiguration(@ApiParam(name = "id", required = true) @PathParam("id") String id, @PathParam("name") String name) throws NotFoundException { validEtags.clear(); final CollectorConfiguration collectorConfiguration = collectorConfigurationService.copyConfiguration(id, name); collectorConfigurationService.save(collectorConfiguration); return Response.accepted().build(); } @POST @Path("/configurations/{id}/outputs/{outputId}/{name}") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_CREATE) @ApiOperation(value = "Copy a configuration output", notes = "This is a stateless method which copies a collector output to one with another name") @ApiResponses(value = { @ApiResponse(code = 404, message = "Configuration or Output not found."), @ApiResponse(code = 400, message = "Invalid ObjectId.") }) @AuditEvent(type = CollectorAuditEventTypes.OUTPUT_CLONE) public Response copyOutput(@ApiParam(name = "id", required = true) @PathParam("id") String id, @PathParam("outputId") String outputId, @PathParam("name") String name) throws NotFoundException { validEtags.clear(); final CollectorConfiguration collectorConfiguration = collectorConfigurationService.copyOutput(id, outputId, name); collectorConfigurationService.save(collectorConfiguration); return Response.accepted().build(); } @POST @Path("/configurations/{id}/inputs/{inputId}/{name}") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_CREATE) @ApiOperation(value = "Copy a configuration input", notes = "This is a stateless method which copies a collector input to one with another name") @ApiResponses(value = { @ApiResponse(code = 404, message = "Configuration or Input not found."), @ApiResponse(code = 400, message = "Invalid ObjectId.") }) @AuditEvent(type = CollectorAuditEventTypes.INPUT_CLONE) public Response copyInput(@ApiParam(name = "id", required = true) @PathParam("id") String id, @PathParam("inputId") String inputId, @PathParam("name") String name) throws NotFoundException { validEtags.clear(); final CollectorConfiguration collectorConfiguration = collectorConfigurationService.copyInput(id, inputId, name); collectorConfigurationService.save(collectorConfiguration); return Response.accepted().build(); } @POST @Path("/configurations/{id}/snippets/{snippetId}/{name}") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_CREATE) @ApiOperation(value = "Copy a configuration snippet", notes = "This is a stateless method which copies a collector snippet to one with another name") @ApiResponses(value = { @ApiResponse(code = 404, message = "Configuration or Snippet not found."), @ApiResponse(code = 400, message = "Invalid ObjectId.") }) @AuditEvent(type = CollectorAuditEventTypes.SNIPPET_CLONE) public Response copySnippet(@ApiParam(name = "id", required = true) @PathParam("id") String id, @PathParam("snippetId") String snippetId, @PathParam("name") String name) throws NotFoundException { validEtags.clear(); final CollectorConfiguration collectorConfiguration = collectorConfigurationService.copySnippet(id, snippetId, name); collectorConfigurationService.save(collectorConfiguration); return Response.accepted().build(); } @DELETE @Path("/configurations/{id}") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_DELETE) @ApiOperation(value = "Delete a collector configuration") @ApiResponses(value = { @ApiResponse(code = 404, message = "Configuration not found."), @ApiResponse(code = 400, message = "Invalid ObjectId.") }) @AuditEvent(type = CollectorAuditEventTypes.CONFIGURATION_DELETE) public void deleteConfiguration(@ApiParam(name = "id", required = true) @PathParam("id") String id) throws NotFoundException { validEtags.clear(); collectorConfigurationService.delete(id); } @DELETE @Path("/configurations/{id}/outputs/{outputId}") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_DELETE) @ApiOperation(value = "Delete output from configuration") @ApiResponses(value = { @ApiResponse(code = 404, message = "Configuration or Output not found."), @ApiResponse(code = 400, message = "Invalid ObjectId."), @ApiResponse(code = 412, message = "Still inputs assigned to output") }) @AuditEvent(type = CollectorAuditEventTypes.OUTPUT_DELETE) public Response deleteOutput(@ApiParam(name = "id", required = true) @PathParam("id") String id, @PathParam("outputId") String outputId) throws NotFoundException { validEtags.clear(); int deleted = collectorConfigurationService.deleteOutput(id, outputId); switch (deleted) { case 0: return Response.status(Response.Status.NOT_FOUND).build(); case -1: return Response.status(Response.Status.PRECONDITION_FAILED).build(); } return Response.ok().build(); } @DELETE @Path("/configurations/{id}/inputs/{inputId}") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_DELETE) @ApiOperation(value = "Delete input form configuration") @ApiResponses(value = { @ApiResponse(code = 404, message = "Configuration or Input not found."), @ApiResponse(code = 400, message = "Invalid ObjectId.") }) @AuditEvent(type = CollectorAuditEventTypes.INPUT_DELETE) public void deleteInput(@ApiParam(name = "id", required = true) @PathParam("id") String id, @PathParam("inputId") String inputId) throws NotFoundException { validEtags.clear(); collectorConfigurationService.deleteInput(id, inputId); } @DELETE @Path("/configurations/{id}/snippets/{snippetId}") @RequiresAuthentication @RequiresPermissions(CollectorRestPermissions.COLLECTORS_DELETE) @ApiOperation(value = "Delete snippet from configuration") @ApiResponses(value = { @ApiResponse(code = 404, message = "Configuration or Snippet not found."), @ApiResponse(code = 400, message = "Invalid ObjectId.") }) @AuditEvent(type = CollectorAuditEventTypes.SNIPPET_DELETE) public void deleteSnippet(@ApiParam(name = "id", required = true) @PathParam("id") String id, @PathParam("snippetId") String snippetId) throws NotFoundException { validEtags.clear(); collectorConfigurationService.deleteSnippet(id, snippetId); } private CollectorConfigurationSummary getCollectorConfigurationSummary( CollectorConfiguration collectorConfiguration) { return CollectorConfigurationSummary.create(collectorConfiguration.id(), collectorConfiguration.name(), collectorConfiguration.tags()); } private List<String> parseQueryTags(String queryTags) { List tags = null; if (queryTags != null) { try { tags = mapper.readValue(queryTags, List.class); } catch (IOException e) { LOG.error("Can not parse provided collector tags: {}", queryTags); tags = null; } } return tags; } public static class RestBoolean { private static final RestBoolean FALSE = new RestBoolean(false); private static final RestBoolean TRUE = new RestBoolean(true); private boolean value; private RestBoolean(boolean value) { this.value = value; } public boolean getValue() { return this.value; } public static RestBoolean valueOf(String value) { switch (value.toLowerCase()) { case "true": case "yes": case "y": { return RestBoolean.TRUE; } default: { return RestBoolean.FALSE; } } } } }