http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SecurityQuestionService.java ---------------------------------------------------------------------- diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SecurityQuestionService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SecurityQuestionService.java index b9df752..3eb45a4 100644 --- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SecurityQuestionService.java +++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SecurityQuestionService.java @@ -40,7 +40,6 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.SecurityQuestionTO; import org.apache.syncope.common.rest.api.RESTHeaders; @@ -60,7 +59,7 @@ public interface SecurityQuestionService extends JAXRSService { * @return list of all security questions */ @GET - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) List<SecurityQuestionTO> list(); /** @@ -71,7 +70,7 @@ public interface SecurityQuestionService extends JAXRSService { */ @GET @Path("{key}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) SecurityQuestionTO read(@NotNull @PathParam("key") String key); /** @@ -90,8 +89,8 @@ public interface SecurityQuestionService extends JAXRSService { @Schema(type = "string"), description = "URL of the entity created") })) @POST - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response create(@NotNull SecurityQuestionTO securityQuestionTO); /** @@ -105,8 +104,8 @@ public interface SecurityQuestionService extends JAXRSService { @ApiResponse(responseCode = "204", description = "Operation was successful")) @PUT @Path("{key}") - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) void update(@NotNull SecurityQuestionTO securityQuestionTO); /** @@ -118,7 +117,7 @@ public interface SecurityQuestionService extends JAXRSService { @ApiResponse(responseCode = "204", description = "Operation was successful")) @DELETE @Path("{key}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) void delete(@NotNull @PathParam("key") String key); /** @@ -129,7 +128,7 @@ public interface SecurityQuestionService extends JAXRSService { */ @GET @Path("byUser/{username}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) SecurityQuestionTO readByUser(@NotNull @PathParam("username") String username); }
http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SyncopeService.java ---------------------------------------------------------------------- diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SyncopeService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SyncopeService.java index c5e2a67..26332e8 100644 --- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SyncopeService.java +++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SyncopeService.java @@ -18,11 +18,19 @@ */ package org.apache.syncope.common.rest.api.service; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.security.SecurityRequirements; import io.swagger.v3.oas.annotations.tags.Tag; +import java.io.InputStream; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; +import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; @@ -30,14 +38,16 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; -import org.apache.syncope.common.lib.SyncopeConstants; +import javax.ws.rs.core.Response; import org.apache.syncope.common.lib.info.NumbersInfo; import org.apache.syncope.common.lib.info.SystemInfo; import org.apache.syncope.common.lib.info.PlatformInfo; import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.PagedResult; import org.apache.syncope.common.lib.to.TypeExtensionTO; +import org.apache.syncope.common.rest.api.RESTHeaders; /** * General info about this Apache Syncope deployment. @@ -58,7 +68,7 @@ public interface SyncopeService extends JAXRSService { */ @GET @Path("/platform") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) PlatformInfo platform(); /** @@ -68,20 +78,72 @@ public interface SyncopeService extends JAXRSService { */ @GET @Path("/system") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) SystemInfo system(); - /** * + /** * Provides some numbers about the managed entities (users, groups, any objects...). * * @return some numbers about the managed entities (users, groups, any objects...) */ @GET @Path("/numbers") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) NumbersInfo numbers(); /** + * Requests for batch execution. + * + * @param input batch request + * @return batch results returned as Response entity, in case no 'Prefer: respond-async' was specified + */ + @Parameter(name = RESTHeaders.PREFER, in = ParameterIn.HEADER, + description = "Allows client to specify a preference to process the batch request asynchronously", + allowEmptyValue = true, schema = + @Schema(defaultValue = "", allowableValues = { "respond-async" })) + @ApiResponses({ + @ApiResponse(responseCode = "200", + description = "Batch request processed, results returned as Response entity, " + + "in case no 'Prefer: respond-async' was specified"), + @ApiResponse(responseCode = "202", + description = "Batch accepted for asynchronous processing, " + + "in case 'Prefer: respond-async' was specified", headers = { + @Header(name = HttpHeaders.LOCATION, schema = + @Schema(type = "string"), + description = "URL to poll in order to get the results of the requested batch processing"), + @Header(name = RESTHeaders.PREFERENCE_APPLIED, schema = + @Schema(type = "string"), + description = "Allows the server to inform the " + + "client about the fact that a specified preference was applied") }) }) + @POST + @Path("/batch") + @Consumes(RESTHeaders.MULTIPART_MIXED) + @Produces(RESTHeaders.MULTIPART_MIXED) + Response batch(InputStream input); + + /** + * Gets batch results, in case asynchronous was requested. + * + * @return batch results returned as Response entity + */ + @GET + @ApiResponses({ + @ApiResponse(responseCode = "200", + description = "Batch results available, returned as Response entity"), + @ApiResponse(responseCode = "202", + description = "Batch results not yet available, retry later", headers = { + @Header(name = HttpHeaders.LOCATION, schema = + @Schema(type = "string"), + description = "URL to poll in order to get the results of the requested batch processing"), + @Header(name = HttpHeaders.RETRY_AFTER, schema = + @Schema(type = "integer"), + description = "seconds after which attempt again to get batch results") }), + @ApiResponse(responseCode = "404", description = "No batch process was found for the provided boundary") }) + @Path("/batch") + @Produces(RESTHeaders.MULTIPART_MIXED) + Response batch(); + + /** * Returns the list of Groups, according to provided paging instructions, assignable to Users and Any Objects of * the provided Realm. * @@ -94,7 +156,7 @@ public interface SyncopeService extends JAXRSService { */ @POST @Path("/assignableGroups/{realm:.*}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) PagedResult<GroupTO> searchAssignableGroups( @NotNull @PathParam("realm") String realm, @QueryParam("term") String term, @@ -109,6 +171,6 @@ public interface SyncopeService extends JAXRSService { */ @GET @Path("/userTypeExtension/{groupName}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) TypeExtensionTO readUserTypeExtension(@NotNull @PathParam("groupName") String groupName); } http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java ---------------------------------------------------------------------- diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java index b74ede1..70f5935 100644 --- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java +++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java @@ -42,7 +42,6 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.TaskTO; import org.apache.syncope.common.lib.to.BulkAction; import org.apache.syncope.common.lib.to.BulkActionResult; @@ -73,7 +72,7 @@ public interface TaskService extends ExecutableService { */ @GET @Path("{type}/{key}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) <T extends TaskTO> T read( @NotNull @PathParam("type") TaskType type, @NotNull @PathParam("key") String key, @@ -88,7 +87,7 @@ public interface TaskService extends ExecutableService { */ @GET @Path("{type}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) <T extends TaskTO> PagedResult<T> search(@BeanParam TaskQuery query); /** @@ -109,8 +108,8 @@ public interface TaskService extends ExecutableService { description = "URL of the entity created") })) @POST @Path("{type}") - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response create(@NotNull @PathParam("type") TaskType type, @NotNull SchedTaskTO taskTO); /** @@ -125,8 +124,8 @@ public interface TaskService extends ExecutableService { @ApiResponse(responseCode = "204", description = "Operation was successful")) @PUT @Path("{type}/{key}") - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) void update(@NotNull @PathParam("type") TaskType type, @NotNull SchedTaskTO taskTO); /** @@ -139,7 +138,7 @@ public interface TaskService extends ExecutableService { @ApiResponse(responseCode = "204", description = "Operation was successful")) @DELETE @Path("{type}/{key}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) void delete(@NotNull @PathParam("type") TaskType type, @NotNull @PathParam("key") String key); /** @@ -150,7 +149,7 @@ public interface TaskService extends ExecutableService { */ @POST @Path("bulk") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) BulkActionResult bulk(@NotNull BulkAction bulkAction); } http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java ---------------------------------------------------------------------- diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java index fd1665a..b655e8b 100644 --- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java +++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java @@ -42,7 +42,6 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.patch.StatusPatch; import org.apache.syncope.common.lib.patch.UserPatch; import org.apache.syncope.common.lib.to.ProvisioningResult; @@ -77,7 +76,7 @@ public interface UserSelfService extends JAXRSService { description = "List of entitlements owned by the calling user") })) @GET - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response read(); /** @@ -110,8 +109,8 @@ public interface UserSelfService extends JAXRSService { description = "Allows the server to inform the " + "client about the fact that a specified preference was applied") })) @POST - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response create(@NotNull UserTO userTO, @DefaultValue("true") @QueryParam("storePassword") boolean storePassword); @@ -144,8 +143,8 @@ public interface UserSelfService extends JAXRSService { + "client about the fact that a specified preference was applied")) }) @PATCH @Path("{key}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response update(@NotNull UserPatch patch); /** @@ -177,8 +176,8 @@ public interface UserSelfService extends JAXRSService { + "client about the fact that a specified preference was applied")) }) @PUT @Path("{key}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response update(@NotNull UserTO user); /** @@ -210,8 +209,8 @@ public interface UserSelfService extends JAXRSService { + "client about the fact that a specified preference was applied")) }) @POST @Path("{key}/status") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response status(@NotNull StatusPatch statusPatch); /** @@ -223,7 +222,7 @@ public interface UserSelfService extends JAXRSService { @SecurityRequirement(name = "BasicAuthentication"), @SecurityRequirement(name = "Bearer") }) @DELETE - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response delete(); /** @@ -238,7 +237,7 @@ public interface UserSelfService extends JAXRSService { @SecurityRequirement(name = "Bearer") }) @POST @Path("mustChangePassword") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response mustChangePassword(String password); /** @@ -253,7 +252,7 @@ public interface UserSelfService extends JAXRSService { @ApiResponse(responseCode = "204", description = "Operation was successful")) @POST @Path("requestPasswordReset") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) void requestPasswordReset(@NotNull @QueryParam("username") String username, String securityAnswer); /** @@ -269,6 +268,6 @@ public interface UserSelfService extends JAXRSService { @ApiResponse(responseCode = "204", description = "Operation was successful")) @POST @Path("confirmPasswordReset") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) void confirmPasswordReset(@NotNull @QueryParam("token") String token, String password); } http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java ---------------------------------------------------------------------- diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java index af33157..0274acd 100644 --- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java +++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java @@ -40,7 +40,6 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.patch.StatusPatch; import org.apache.syncope.common.lib.patch.UserPatch; import org.apache.syncope.common.lib.to.PagedResult; @@ -108,8 +107,8 @@ public interface UserService extends AnyService<UserTO> { description = "Allows the server to inform the " + "client about the fact that a specified preference was applied") })) @POST - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response create( @NotNull UserTO userTO, @DefaultValue("true") @QueryParam("storePassword") boolean storePassword); @@ -153,8 +152,8 @@ public interface UserService extends AnyService<UserTO> { + " date of the entity") }) @PATCH @Path("{key}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response update(@NotNull UserPatch userPatch); /** @@ -197,8 +196,8 @@ public interface UserService extends AnyService<UserTO> { + " date of the entity") }) @PUT @Path("{key}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response update(@NotNull UserTO userTO); /** @@ -241,7 +240,7 @@ public interface UserService extends AnyService<UserTO> { + " date of the entity") }) @POST @Path("{key}/status") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response status(@NotNull StatusPatch statusPatch); } http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java ---------------------------------------------------------------------- diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java index ee65b7e..c38746f 100644 --- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java +++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java @@ -31,11 +31,11 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.PagedResult; import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.to.WorkflowFormTO; import org.apache.syncope.common.lib.to.WorkflowTaskTO; +import org.apache.syncope.common.rest.api.RESTHeaders; import org.apache.syncope.common.rest.api.beans.WorkflowFormQuery; /** @@ -56,7 +56,7 @@ public interface UserWorkflowService extends JAXRSService { */ @GET @Path("forms") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) PagedResult<WorkflowFormTO> getForms(@BeanParam WorkflowFormQuery query); /** @@ -67,7 +67,7 @@ public interface UserWorkflowService extends JAXRSService { */ @GET @Path("forms/{userKey}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) WorkflowFormTO getFormForUser(@NotNull @PathParam("userKey") String userKey); /** @@ -78,7 +78,7 @@ public interface UserWorkflowService extends JAXRSService { */ @POST @Path("forms/{taskId}/claim") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) WorkflowFormTO claimForm(@NotNull @PathParam("taskId") String taskId); /** @@ -89,8 +89,8 @@ public interface UserWorkflowService extends JAXRSService { */ @POST @Path("forms") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) UserTO submitForm(@NotNull WorkflowFormTO form); /** @@ -112,7 +112,7 @@ public interface UserWorkflowService extends JAXRSService { */ @POST @Path("tasks/{taskId}/execute") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) UserTO executeTask(@NotNull @PathParam("taskId") String taskId, @NotNull UserTO userTO); } http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java ---------------------------------------------------------------------- diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java index 92d026f..25e3f7a 100644 --- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java +++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java @@ -34,7 +34,6 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.WorkflowDefinitionTO; import org.apache.syncope.common.rest.api.RESTHeaders; @@ -56,7 +55,7 @@ public interface WorkflowService extends JAXRSService { */ @GET @Path("{anyType}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) List<WorkflowDefinitionTO> list(@NotNull @PathParam("anyType") String anyType); /** @@ -68,7 +67,7 @@ public interface WorkflowService extends JAXRSService { */ @GET @Path("{anyType}/{key}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) Response get( @NotNull @PathParam("anyType") String anyType, @NotNull @PathParam("key") String key); @@ -98,8 +97,8 @@ public interface WorkflowService extends JAXRSService { @ApiResponse(responseCode = "204", description = "Operation was successful")) @PUT @Path("{anyType}/{key}") - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) void set( @NotNull @PathParam("anyType") String anyType, @NotNull @PathParam("key") String key, @@ -115,7 +114,7 @@ public interface WorkflowService extends JAXRSService { @ApiResponse(responseCode = "204", description = "Operation was successful")) @DELETE @Path("{anyType}/{key}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) void delete( @NotNull @PathParam("anyType") String anyType, @NotNull @PathParam("key") String key); http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/BatchDAO.java ---------------------------------------------------------------------- diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/BatchDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/BatchDAO.java new file mode 100644 index 0000000..b5e1166 --- /dev/null +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/BatchDAO.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.syncope.core.persistence.api.dao; + +import org.apache.syncope.core.persistence.api.entity.Batch; + +public interface BatchDAO extends DAO<Batch> { + + Batch find(String key); + + Batch save(Batch batch); + + void delete(String key); + + int deleteExpired(); +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Batch.java ---------------------------------------------------------------------- diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Batch.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Batch.java new file mode 100644 index 0000000..86fa647 --- /dev/null +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Batch.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.syncope.core.persistence.api.entity; + +import java.util.Date; + +public interface Batch extends ProvidedKeyEntity { + + Date getExpiryTime(); + + void setExpiryTime(Date expiryTime); + + String getResults(); + + void setResults(String results); +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPABatchDAO.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPABatchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPABatchDAO.java new file mode 100644 index 0000000..7470c5a --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPABatchDAO.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.syncope.core.persistence.jpa.dao; + +import java.util.Date; +import javax.persistence.Query; +import org.apache.syncope.core.persistence.api.dao.BatchDAO; +import org.apache.syncope.core.persistence.api.entity.Batch; +import org.apache.syncope.core.persistence.jpa.entity.JPABatch; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(rollbackFor = Throwable.class) +@Repository +public class JPABatchDAO extends AbstractDAO<Batch> implements BatchDAO { + + @Transactional(readOnly = true) + @Override + public Batch find(final String key) { + return entityManager().find(JPABatch.class, key); + } + + @Override + public Batch save(final Batch batch) { + return entityManager().merge(batch); + } + + @Override + public void delete(final String key) { + Batch batch = find(key); + if (batch == null) { + return; + } + + entityManager().remove(batch); + } + + @Override + public int deleteExpired() { + Query query = entityManager().createQuery( + "DELETE FROM " + JPABatch.class.getSimpleName() + " e " + + "WHERE e.expiryTime < :now"); + query.setParameter("now", new Date()); + return query.executeUpdate(); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPABatch.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPABatch.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPABatch.java new file mode 100644 index 0000000..26c068c --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPABatch.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.syncope.core.persistence.jpa.entity; + +import java.util.Date; +import javax.persistence.Entity; +import javax.persistence.Lob; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import org.apache.syncope.core.persistence.api.entity.Batch; + +@Entity +@Table(name = JPABatch.TABLE) +public class JPABatch extends AbstractProvidedKeyEntity implements Batch { + + private static final long serialVersionUID = 468423182798249255L; + + public static final String TABLE = "SyncopeBatch"; + + @Temporal(TemporalType.TIMESTAMP) + private Date expiryTime; + + @Lob + private String results; + + @Override + public Date getExpiryTime() { + return expiryTime == null + ? null + : new Date(expiryTime.getTime()); + } + + @Override + public void setExpiryTime(final Date expiryTime) { + if (expiryTime == null) { + this.expiryTime = null; + } else { + this.expiryTime = new Date(expiryTime.getTime()); + } + } + + @Override + public String getResults() { + return results; + } + + @Override + public void setResults(final String results) { + this.results = results; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java index 0f60dd7..1fa7787 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java @@ -31,6 +31,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyTemplateRealm; import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; import org.apache.syncope.core.persistence.api.entity.Application; +import org.apache.syncope.core.persistence.api.entity.Batch; import org.apache.syncope.core.persistence.api.entity.ConnInstance; import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf; import org.apache.syncope.core.persistence.api.entity.ConnPoolConf; @@ -295,6 +296,8 @@ public class JPAEntityFactory implements EntityFactory { result = (E) new JPAImplementation(); } else if (reference.equals(Remediation.class)) { result = (E) new JPARemediation(); + } else if (reference.equals(Batch.class)) { + result = (E) new JPABatch(); } else { throw new IllegalArgumentException("Could not find a JPA implementation of " + reference.getName()); } http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/persistence-jpa/src/main/resources/domains/MasterContent.xml ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml index 25c5b2a..123e500 100644 --- a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml +++ b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml @@ -194,6 +194,10 @@ under the License. body="org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup"/> <Task DTYPE="SchedTask" id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task" active="1" jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/> + <Implementation id="ExpiredBatchCleanup" type="TASKJOB_DELEGATE" engine="JAVA" + body="org.apache.syncope.core.provisioning.java.job.ExpiredBatchCleanup"/> + <Task DTYPE="SchedTask" id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Access Token Cleanup Task" active="1" + jobDelegate_id="ExpiredBatchCleanup" cronExpression="0 0/5 * * * ?"/> <!-- Password reset notifications --> <MailTemplate id="requestPasswordReset" http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java index 2f29e17..f505fbe 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java @@ -43,7 +43,7 @@ public class ImplementationTest extends AbstractTest { List<Implementation> implementations = implementationDAO.findAll(); assertFalse(implementations.isEmpty()); - assertEquals(18, implementations.size()); + assertEquals(19, implementations.size()); implementations = implementationDAO.find(ImplementationType.PULL_ACTIONS); assertEquals(1, implementations.size()); @@ -52,7 +52,7 @@ public class ImplementationTest extends AbstractTest { assertEquals(1, implementations.size()); implementations = implementationDAO.find(ImplementationType.TASKJOB_DELEGATE); - assertEquals(5, implementations.size()); + assertEquals(6, implementations.size()); implementations = implementationDAO.find(ImplementationType.REPORTLET); assertEquals(2, implementations.size()); http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java index 803984e..a912d42 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java @@ -97,7 +97,7 @@ public class TaskTest extends AbstractTest { public void findAll() { assertEquals(5, taskDAO.findAll(TaskType.PROPAGATION).size()); assertEquals(1, taskDAO.findAll(TaskType.NOTIFICATION).size()); - assertEquals(3, taskDAO.findAll(TaskType.SCHEDULED).size()); + assertEquals(4, taskDAO.findAll(TaskType.SCHEDULED).size()); assertEquals(10, taskDAO.findAll(TaskType.PULL).size()); assertEquals(11, taskDAO.findAll(TaskType.PUSH).size()); } http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/persistence-jpa/src/test/resources/domains/MasterContent.xml ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml index 6096d05..1f0fd44 100644 --- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml +++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml @@ -1333,6 +1333,10 @@ under the License. <Task DTYPE="PullTask" remediation="0" id="30cfd653-257b-495f-8665-281281dbcb3d" name="Scripted SQL" resource_id="resource-db-scripted" destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" performCreate="1" performUpdate="1" performDelete="0" syncStatus="0" pullMode="INCREMENTAL" unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" jobDelegate_id="PullJobDelegate"/> + <Implementation id="ExpiredBatchCleanup" type="TASKJOB_DELEGATE" engine="JAVA" + body="org.apache.syncope.core.provisioning.java.job.ExpiredBatchCleanup"/> + <Task DTYPE="SchedTask" id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Access Token Cleanup Task" active="1" + jobDelegate_id="ExpiredBatchCleanup" cronExpression="0 0/5 * * * ?"/> <MailTemplate id="requestPasswordReset" textTemplate="Hi, http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java index 327b396..351d21d 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java @@ -87,9 +87,9 @@ public class AccessTokenDataBinderImpl implements AccessTokenDataBinder { jwtClaims.setIssuer(jwtIssuer); jwtClaims.setExpiryTime(expiryTime); jwtClaims.setNotBefore(currentTime); - for (Map.Entry<String, Object> entry : claims.entrySet()) { - jwtClaims.setClaim(entry.getKey(), entry.getValue()); - } + claims.forEach((key, value) -> { + jwtClaims.setClaim(key, value); + }); JwsHeaders jwsHeaders = new JwsHeaders(JoseType.JWT, jwsSignatureProvider.getAlgorithm()); JwtToken token = new JwtToken(jwsHeaders, jwtClaims); http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredBatchCleanup.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredBatchCleanup.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredBatchCleanup.java new file mode 100644 index 0000000..9d8e7bc --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredBatchCleanup.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.syncope.core.provisioning.java.job; + +import org.apache.syncope.core.persistence.api.dao.BatchDAO; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; + +public class ExpiredBatchCleanup extends AbstractSchedTaskJobDelegate { + + @Autowired + private BatchDAO batchDAO; + + @Override + protected String doExecute(final boolean dryRun) throws JobExecutionException { + if (!dryRun) { + int deleted = batchDAO.deleteExpired(); + LOG.debug("Successfully deleted {} expired batch requests", deleted); + } + + return "SUCCESS"; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/rest-cxf/pom.xml ---------------------------------------------------------------------- diff --git a/core/rest-cxf/pom.xml b/core/rest-cxf/pom.xml index 23833d6..d56efe0 100644 --- a/core/rest-cxf/pom.xml +++ b/core/rest-cxf/pom.xml @@ -154,6 +154,17 @@ under the License. <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> </plugin> + + <plugin> + <groupId>org.gaul</groupId> + <artifactId>modernizer-maven-plugin</artifactId> + <configuration> + <exclusions> + <!-- required by HttpServletRequest's override in BatchItemRequest --> + java/lang/StringBuffer."<init>":(Ljava/lang/String;)V + </exclusions> + </configuration> + </plugin> </plugins> <resources> http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemRequest.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemRequest.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemRequest.java new file mode 100644 index 0000000..cc1cd22 --- /dev/null +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemRequest.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.syncope.core.rest.cxf.batch; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.stream.Collectors; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.ws.rs.core.HttpHeaders; +import org.apache.syncope.common.rest.api.batch.BatchRequestItem; +import org.springframework.http.MediaType; + +public class BatchItemRequest extends HttpServletRequestWrapper { + + private final String basePath; + + private final BatchRequestItem batchItem; + + private final ServletInputStream inputStream; + + public BatchItemRequest( + final String basePath, + final HttpServletRequest request, + final BatchRequestItem batchItem) { + + super(request); + this.basePath = basePath; + this.batchItem = batchItem; + this.inputStream = new ServletInputStream() { + + private final ByteArrayInputStream bais = new ByteArrayInputStream(batchItem.getContent().getBytes()); + + private boolean isFinished = false; + + private boolean isReady = true; + + @Override + public boolean isFinished() { + return isFinished; + } + + @Override + public boolean isReady() { + return isReady; + } + + @Override + public void setReadListener(final ReadListener readListener) { + // nope + } + + @Override + public int read() { + isFinished = true; + isReady = false; + return bais.read(); + } + }; + } + + @Override + public String getMethod() { + return batchItem.getMethod(); + } + + @Override + public StringBuffer getRequestURL() { + return new StringBuffer(basePath).append(getRequestURI()); + } + + @Override + public String getRequestURI() { + return batchItem.getRequestURI(); + } + + @Override + public String getQueryString() { + return batchItem.getQueryString(); + } + + @Override + public String getContentType() { + return batchItem.getHeaders().containsKey(HttpHeaders.CONTENT_TYPE) + ? batchItem.getHeaders().get(HttpHeaders.CONTENT_TYPE).get(0).toString() + : MediaType.ALL_VALUE; + } + + @Override + public int getContentLength() { + return batchItem.getHeaders().containsKey(HttpHeaders.CONTENT_LENGTH) + ? Integer.valueOf(batchItem.getHeaders().get(HttpHeaders.CONTENT_LENGTH).get(0).toString()) + : 0; + } + + @Override + public long getContentLengthLong() { + return getContentLength(); + } + + @Override + public String getHeader(final String name) { + return batchItem.getHeaders().containsKey(name) + ? batchItem.getHeaders().get(name).get(0).toString() + : HttpHeaders.CONTENT_TYPE.equals(name) || HttpHeaders.ACCEPT.equals(name) + ? MediaType.ALL_VALUE + : super.getHeader(name); + } + + @Override + public Enumeration<String> getHeaders(final String name) { + return batchItem.getHeaders().containsKey(name) + ? Collections.enumeration( + batchItem.getHeaders().get(name).stream().map(Object::toString).collect(Collectors.toList())) + : HttpHeaders.CONTENT_TYPE.equals(name) || HttpHeaders.ACCEPT.equals(name) + ? Collections.enumeration(Arrays.asList(MediaType.ALL_VALUE)) + : super.getHeaders(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return inputStream; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemResponse.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemResponse.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemResponse.java new file mode 100644 index 0000000..e16fed9 --- /dev/null +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemResponse.java @@ -0,0 +1,310 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.syncope.core.rest.cxf.batch; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.HttpHeaders; + +public class BatchItemResponse implements HttpServletResponse { + + private final Set<Cookie> cookies = new HashSet<>(); + + private final Map<String, List<Object>> headers = new HashMap<>(); + + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + private final ServletOutputStream servletOuputStream = new ServletOutputStream() { + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setWriteListener(final WriteListener writeListener) { + // nope + } + + @Override + public void write(final int b) throws IOException { + baos.write(b); + } + }; + + private final PrintWriter writer = new PrintWriter(baos); + + private int status; + + private Locale locale; + + public Set<Cookie> getCookies() { + return cookies; + } + + public Map<String, List<Object>> getHeaders() { + return headers; + } + + @Override + public void addCookie(final Cookie cookie) { + this.cookies.add(cookie); + } + + @Override + public boolean containsHeader(final String name) { + return headers.containsKey(name); + } + + @Override + public void setDateHeader(final String name, final long date) { + List<Object> values = headers.get(name); + if (values == null) { + values = new ArrayList<>(); + headers.put(name, values); + } else { + values.clear(); + } + values.add(date); + } + + @Override + public void addDateHeader(final String name, final long date) { + List<Object> values = headers.get(name); + if (values == null) { + values = new ArrayList<>(); + headers.put(name, values); + } + values.add(date); + } + + @Override + public void setHeader(final String name, final String value) { + List<Object> values = headers.get(name); + if (values == null) { + values = new ArrayList<>(); + headers.put(name, values); + } else { + values.clear(); + } + values.add(value); + } + + @Override + public void addHeader(final String name, final String value) { + List<Object> values = headers.get(name); + if (values == null) { + values = new ArrayList<>(); + headers.put(name, values); + } + values.add(value); + } + + @Override + public void setIntHeader(final String name, final int value) { + List<Object> values = headers.get(name); + if (values == null) { + values = new ArrayList<>(); + headers.put(name, values); + } else { + values.clear(); + } + values.add(value); + } + + @Override + public void addIntHeader(final String name, final int value) { + List<Object> values = headers.get(name); + if (values == null) { + values = new ArrayList<>(); + headers.put(name, values); + } + values.add(value); + } + + @Override + public String getHeader(final String name) { + return headers.containsKey(name) ? headers.get(name).get(0).toString() : null; + } + + @Override + public Collection<String> getHeaders(final String name) { + return headers.containsKey(name) + ? headers.get(name).stream().map(Object::toString).collect(Collectors.toList()) + : Collections.emptyList(); + } + + @Override + public Collection<String> getHeaderNames() { + return headers.keySet(); + } + + @Override + public String encodeURL(final String url) { + return url; + } + + @Override + public String encodeRedirectURL(final String url) { + return url; + } + + @Override + @SuppressWarnings("deprecation") + public String encodeUrl(final String url) { + return encodeURL(url); + } + + @Override + @SuppressWarnings("deprecation") + public String encodeRedirectUrl(final String url) { + return encodeRedirectURL(url); + } + + @Override + public void sendError(final int sc, final String msg) throws IOException { + setStatus(sc); + } + + @Override + public void sendError(final int sc) throws IOException { + setStatus(sc); + } + + @Override + public void sendRedirect(final String location) throws IOException { + setStatus(SC_MOVED_TEMPORARILY); + setHeader(HttpHeaders.LOCATION, location); + } + + @Override + public void setStatus(final int sc) { + this.status = sc; + } + + @Override + @SuppressWarnings("deprecation") + public void setStatus(final int sc, final String sm) { + setStatus(sc); + } + + @Override + public int getStatus() { + return status; + } + + @Override + public String getCharacterEncoding() { + throw new UnsupportedOperationException(); + } + + @Override + public String getContentType() { + throw new UnsupportedOperationException(); + } + + public ByteArrayOutputStream getUnderlyingOutputStream() { + return baos; + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + return servletOuputStream; + } + + @Override + public PrintWriter getWriter() throws IOException { + return writer; + } + + @Override + public void setCharacterEncoding(final String charset) { + throw new UnsupportedOperationException(); + } + + @Override + public void setContentLength(final int len) { + setIntHeader(HttpHeaders.CONTENT_LENGTH, len); + } + + @Override + public void setContentLengthLong(final long len) { + setContentLength((int) len); + } + + @Override + public void setContentType(final String type) { + setHeader(HttpHeaders.CONTENT_TYPE, type); + } + + @Override + public void setBufferSize(final int size) { + throw new UnsupportedOperationException(); + } + + @Override + public int getBufferSize() { + throw new UnsupportedOperationException(); + } + + @Override + public void flushBuffer() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void resetBuffer() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isCommitted() { + throw new UnsupportedOperationException(); + } + + @Override + public void reset() { + throw new UnsupportedOperationException(); + } + + @Override + public void setLocale(final Locale loc) { + this.locale = loc; + } + + @Override + public Locale getLocale() { + return locale; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchProcess.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchProcess.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchProcess.java new file mode 100644 index 0000000..a76d418 --- /dev/null +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchProcess.java @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.syncope.core.rest.cxf.batch; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.ServletConfig; +import javax.servlet.http.HttpServletRequest; +import org.apache.cxf.transport.http.AbstractHTTPDestination; +import org.apache.cxf.transport.http.DestinationRegistry; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.rest.api.batch.BatchPayloadGenerator; +import org.apache.syncope.common.rest.api.batch.BatchRequestItem; +import org.apache.syncope.common.rest.api.batch.BatchResponseItem; +import org.apache.syncope.core.persistence.api.dao.BatchDAO; +import org.apache.syncope.core.persistence.api.entity.Batch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +public class BatchProcess implements Runnable { + + private static final Logger LOG = LoggerFactory.getLogger(BatchProcess.class); + + @Autowired + private BatchDAO batchDAO; + + private String boundary; + + private String basePath; + + private List<BatchRequestItem> batchRequestItems; + + private DestinationRegistry destinationRegistry; + + private ServletConfig servletConfig; + + private HttpServletRequest servletRequest; + + private Authentication authentication; + + public void setBoundary(final String boundary) { + this.boundary = boundary; + } + + public void setBasePath(final String basePath) { + this.basePath = basePath; + } + + public void setBatchRequestItems(final List<BatchRequestItem> batchRequestItems) { + this.batchRequestItems = batchRequestItems; + } + + public void setDestinationRegistry(final DestinationRegistry destinationRegistry) { + this.destinationRegistry = destinationRegistry; + } + + public void setServletConfig(final ServletConfig servletConfig) { + this.servletConfig = servletConfig; + } + + public void setServletRequest(final HttpServletRequest servletRequest) { + this.servletRequest = servletRequest; + } + + public void setAuthentication(final Authentication authentication) { + this.authentication = authentication; + } + + @Override + public void run() { + SecurityContextHolder.getContext().setAuthentication(authentication); + + List<BatchResponseItem> batchResponseItems = new ArrayList<>(batchRequestItems.size()); + + batchRequestItems.forEach((BatchRequestItem reqItem) -> { + LOG.debug("Batch item:\n{}", reqItem); + + AbstractHTTPDestination dest = destinationRegistry.getDestinationForPath(reqItem.getRequestURI(), true); + if (dest == null) { + dest = destinationRegistry.checkRestfulRequest(reqItem.getRequestURI()); + } + LOG.debug("Destination found for {}: {}", reqItem.getRequestURI(), dest); + + if (dest == null) { + BatchResponseItem resItem = new BatchResponseItem(); + resItem.setStatus(404); + batchResponseItems.add(resItem); + } else { + BatchItemRequest request = new BatchItemRequest(basePath, servletRequest, reqItem); + BatchItemResponse response = new BatchItemResponse(); + try { + dest.invoke(servletConfig, servletConfig.getServletContext(), request, response); + LOG.debug("Returned:\nstatus: {}\nheaders: {}\nbody:\n{}", response.getStatus(), + response.getHeaders(), new String(response.getUnderlyingOutputStream().toByteArray())); + + BatchResponseItem resItem = new BatchResponseItem(); + resItem.setStatus(response.getStatus()); + resItem.setHeaders(response.getHeaders()); + String output = new String(response.getUnderlyingOutputStream().toByteArray()); + if (output.length() > 0) { + resItem.setContent(output); + } + batchResponseItems.add(resItem); + } catch (IOException e) { + LOG.error("Invocation of {} failed", dest.getPath(), e); + + BatchResponseItem resItem = new BatchResponseItem(); + resItem.setStatus(404); + batchResponseItems.add(resItem); + } + } + }); + + String results = BatchPayloadGenerator.generate(batchResponseItems, SyncopeConstants.DOUBLE_DASH + boundary); + + Batch batch = batchDAO.find(boundary); + if (batch == null) { + LOG.error("Could not find batch {}, cannot save results hence reporting here:\n{}", boundary, results); + } else { + batch.setResults(results); + batchDAO.save(batch); + } + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java index caef5d8..2e9a0c0 100644 --- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java @@ -80,7 +80,8 @@ abstract class AbstractServiceImpl implements JAXRSService { } protected boolean isNullPriorityAsync() { - return BooleanUtils.toBoolean(messageContext.getHttpHeaders().getHeaderString(RESTHeaders.NULL_PRIORITY_ASYNC)); + return BooleanUtils.toBoolean( + messageContext.getHttpServletRequest().getHeader(RESTHeaders.NULL_PRIORITY_ASYNC)); } /** @@ -89,8 +90,8 @@ abstract class AbstractServiceImpl implements JAXRSService { * @return a {@code Preference} instance matching the passed {@code Prefer} header, * or {@code Preference.NONE} if missing. */ - private Preference getPreference() { - return Preference.fromString(messageContext.getHttpHeaders().getHeaderString(RESTHeaders.PREFER)); + protected Preference getPreference() { + return Preference.fromString(messageContext.getHttpServletRequest().getHeader(RESTHeaders.PREFER)); } protected Response.ResponseBuilder applyPreference( http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SyncopeServiceImpl.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SyncopeServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SyncopeServiceImpl.java index 2f84efc..1deaa37 100644 --- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SyncopeServiceImpl.java +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SyncopeServiceImpl.java @@ -18,9 +18,23 @@ */ package org.apache.syncope.core.rest.cxf.service; +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; import java.util.List; +import javax.annotation.Resource; +import javax.ws.rs.InternalServerErrorException; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import org.apache.cxf.Bus; +import org.apache.cxf.transport.DestinationFactoryManager; +import org.apache.cxf.transport.http.DestinationRegistry; +import org.apache.cxf.transport.http.HTTPTransportFactory; +import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.info.NumbersInfo; import org.apache.syncope.common.lib.info.SystemInfo; @@ -28,17 +42,41 @@ import org.apache.syncope.common.lib.info.PlatformInfo; import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.PagedResult; import org.apache.syncope.common.lib.to.TypeExtensionTO; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.rest.api.Preference; +import org.apache.syncope.common.rest.api.RESTHeaders; +import org.apache.syncope.common.rest.api.batch.BatchPayloadParser; +import org.apache.syncope.common.rest.api.batch.BatchRequestItem; import org.apache.syncope.common.rest.api.service.SyncopeService; import org.apache.syncope.core.logic.SyncopeLogic; +import org.apache.syncope.core.rest.cxf.batch.BatchProcess; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.apache.syncope.core.persistence.api.dao.BatchDAO; +import org.apache.syncope.core.persistence.api.entity.Batch; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.security.core.context.SecurityContextHolder; @Service public class SyncopeServiceImpl extends AbstractServiceImpl implements SyncopeService { + @Resource(name = "batchExecutor") + private ThreadPoolTaskExecutor batchExecutor; + @Autowired private SyncopeLogic logic; + @Autowired + private Bus bus; + + @Autowired + private BatchDAO batchDAO; + + @Autowired + private EntityFactory entityFactory; + @Override public PlatformInfo platform() { return logic.platform(); @@ -68,4 +106,95 @@ public class SyncopeServiceImpl extends AbstractServiceImpl implements SyncopeSe return logic.readTypeExtension(groupName); } + private DestinationRegistry getDestinationRegistryFromBusOrDefault() { + DestinationFactoryManager dfm = bus.getExtension(DestinationFactoryManager.class); + try { + HTTPTransportFactory df = (HTTPTransportFactory) dfm. + getDestinationFactory("http://cxf.apache.org/transports/http/configuration"); + return df.getRegistry(); + } catch (Exception e) { + throw new InternalServerErrorException("Could not find CXF's DestinationRegistry", e); + } + } + + @Override + public Response batch(final InputStream input) { + // parse Content-Type, expect appropriate boundary + MediaType mediaType = MediaType.valueOf(messageContext.getHttpServletRequest().getContentType()); + String boundary = mediaType.getParameters().get(RESTHeaders.BOUNDARY_PARAMETER); + + if (batchDAO.find(boundary) != null) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.EntityExists); + sce.getElements().add("Batch with boundary " + boundary + " already processing"); + throw sce; + } + + // parse batch request + List<BatchRequestItem> batchRequestItems; + try { + batchRequestItems = BatchPayloadParser.parse(input, mediaType, new BatchRequestItem()); + } catch (IOException e) { + LOG.error("Could not parse batch request with boundary {}", boundary, e); + + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidEntity); + sce.getElements().add("Batch request with boundary " + boundary); + throw sce; + } + + // prepare for batch processing + Batch batch = entityFactory.newEntity(Batch.class); + batch.setKey(boundary); + batch.setExpiryTime(new Date(System.currentTimeMillis() + 5 * 60 * 1000)); + batchDAO.save(batch); + + BatchProcess batchProcess = ApplicationContextProvider.getBeanFactory().createBean(BatchProcess.class); + batchProcess.setBoundary(boundary); + batchProcess.setBasePath(uriInfo.getBaseUri().toASCIIString()); + batchProcess.setBatchRequestItems(batchRequestItems); + batchProcess.setDestinationRegistry(getDestinationRegistryFromBusOrDefault()); + batchProcess.setServletConfig(messageContext.getServletConfig()); + batchProcess.setServletRequest(messageContext.getHttpServletRequest()); + batchProcess.setAuthentication(SecurityContextHolder.getContext().getAuthentication()); + + // manage synchronous Vs asynchronous batch processing + if (getPreference() == Preference.RESPOND_ASYNC) { + batchExecutor.execute(batchProcess); + + return Response.accepted(). + header(RESTHeaders.PREFERENCE_APPLIED, getPreference().toString()). + header(HttpHeaders.LOCATION, uriInfo.getAbsolutePathBuilder().build()). + type(RESTHeaders.multipartMixedWith(boundary)). + build(); + } else { + batchProcess.run(); + return batch(); + } + } + + @Override + public Response batch() { + MediaType mediaType = MediaType.valueOf(messageContext.getHttpServletRequest().getContentType()); + String boundary = mediaType.getParameters().get(RESTHeaders.BOUNDARY_PARAMETER); + + Batch batch = batchDAO.find(boundary); + if (batch == null) { + throw new NotFoundException("Batch " + boundary); + } + + if (batch.getResults() == null) { + return Response.accepted(). + type(RESTHeaders.multipartMixedWith(boundary)). + header(HttpHeaders.RETRY_AFTER, 5). + header(HttpHeaders.LOCATION, uriInfo.getAbsolutePathBuilder().build()). + build(); + } + + Response response = Response.ok(batch.getResults()). + type(RESTHeaders.multipartMixedWith(boundary)). + build(); + + batchDAO.delete(boundary); + + return response; + } } http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/core/rest-cxf/src/main/resources/restCXFContext.xml ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/resources/restCXFContext.xml b/core/rest-cxf/src/main/resources/restCXFContext.xml index 571add9..dd3ff18 100644 --- a/core/rest-cxf/src/main/resources/restCXFContext.xml +++ b/core/rest-cxf/src/main/resources/restCXFContext.xml @@ -21,18 +21,23 @@ under the License. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:context="http://www.springframework.org/schema/context" + xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://www.springframework.org/schema/context - http://www.springframework.org/schema/context/spring-context.xsd"> + http://www.springframework.org/schema/context/spring-context.xsd + http://www.springframework.org/schema/task + http://www.springframework.org/schema/task/spring-task.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <context:component-scan base-package="org.apache.syncope.core.rest.cxf.service"/> + <task:executor id="batchExecutor" pool-size="10"/> + <bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider"> <property name="namespacePrefixes"> <map> http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/ext/camel/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/CamelRouteService.java ---------------------------------------------------------------------- diff --git a/ext/camel/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/CamelRouteService.java b/ext/camel/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/CamelRouteService.java index 0760567..020b4a2 100644 --- a/ext/camel/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/CamelRouteService.java +++ b/ext/camel/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/CamelRouteService.java @@ -36,10 +36,10 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.CamelMetrics; import org.apache.syncope.common.lib.to.CamelRouteTO; import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.rest.api.RESTHeaders; /** * REST operations for Camel routes. @@ -59,7 +59,7 @@ public interface CamelRouteService extends JAXRSService { */ @GET @Path("{anyTypeKind}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) List<CamelRouteTO> list(@NotNull @PathParam("anyTypeKind") AnyTypeKind anyTypeKind); /** @@ -71,7 +71,7 @@ public interface CamelRouteService extends JAXRSService { */ @GET @Path("{anyTypeKind}/{key}") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) CamelRouteTO read( @NotNull @PathParam("anyTypeKind") AnyTypeKind anyTypeKind, @NotNull @PathParam("key") String key); @@ -88,8 +88,8 @@ public interface CamelRouteService extends JAXRSService { @ApiResponse(responseCode = "204", description = "Operation was successful")) @PUT @Path("{anyTypeKind}/{key}") - @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) void update(@NotNull @PathParam("anyTypeKind") AnyTypeKind anyTypeKind, @NotNull CamelRouteTO route); /** @@ -99,7 +99,7 @@ public interface CamelRouteService extends JAXRSService { @ApiResponse(responseCode = "204", description = "Operation was successful")) @POST @Path("restartContext") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) void restartContext(); /** @@ -109,6 +109,6 @@ public interface CamelRouteService extends JAXRSService { */ @GET @Path("metrics") - @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) CamelMetrics metrics(); }