syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ilgro...@apache.org
Subject [5/7] syncope git commit: [SYNCOPE-620] server-rest-cxf
Date Mon, 12 Jan 2015 17:41:08 GMT
http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
new file mode 100644
index 0000000..f0bbf08
--- /dev/null
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
@@ -0,0 +1,313 @@
+/*
+ * 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.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.jaxrs.model.wadl.Description;
+import org.apache.cxf.jaxrs.model.wadl.Descriptions;
+import org.apache.cxf.jaxrs.model.wadl.DocTarget;
+import org.apache.syncope.common.lib.mod.RoleMod;
+import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.types.ResourceAssociationActionType;
+import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
+import org.apache.syncope.common.lib.wrap.ResourceName;
+
+/**
+ * REST operations for roles.
+ */
+@Path("roles")
+public interface RoleService extends JAXRSService {
+
+    /**
+     * Returns children roles of given role.
+     *
+     * @param roleKey key of role to get children from
+     * @return children roles of given role
+     */
+    @GET
+    @Path("{roleKey}/children")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    List<RoleTO> children(@NotNull @PathParam("roleKey") Long roleKey);
+
+    /**
+     * Returns parent role of the given role (or null if no parent exists).
+     *
+     * @param roleKey key of role to get parent role from
+     * @return parent role of the given role (or null if no parent exists)
+     */
+    @GET
+    @Path("{roleKey}/parent")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    RoleTO parent(@NotNull @PathParam("roleKey") Long roleKey);
+
+    /**
+     * Reads the role matching the provided roleKey.
+     *
+     * @param roleKey key of role to be read
+     * @return role with matching id
+     */
+    @GET
+    @Path("{roleKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    RoleTO read(@NotNull @PathParam("roleKey") Long roleKey);
+
+    /**
+     * This method is similar to {@link #read(Long)}, but uses different authentication handling to ensure that a user
+     * can read his own roles.
+     *
+     * @param roleKey key of role to be read
+     * @return role with matching id
+     */
+    @Descriptions({
+        @Description(target = DocTarget.METHOD,
+                value = "This method is similar to <tt>read()</tt>, but uses different authentication handling to "
+                + "ensure that a user can read his own roles.")
+    })
+    @GET
+    @Path("{roleKey}/own")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    RoleTO readSelf(@NotNull @PathParam("roleKey") Long roleKey);
+
+    /**
+     * Returns a paged list of existing roles.
+     *
+     * @return paged list of all existing roles
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<RoleTO> list();
+
+    /**
+     * Returns a paged list of existing roles.
+     *
+     * @param orderBy list of ordering clauses, separated by comma
+     * @return paged list of all existing roles
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<RoleTO> list(@QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Returns a paged list of existing roles matching page/size conditions.
+     *
+     * @param page result page number
+     * @param size number of entries per page
+     * @return paged list of existing roles matching page/size conditions
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<RoleTO> list(
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size);
+
+    /**
+     * Returns a paged list of existing roles matching page/size conditions.
+     *
+     * @param page result page number
+     * @param size number of entries per page
+     * @param orderBy list of ordering clauses, separated by comma
+     * @return paged list of existing roles matching page/size conditions
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<RoleTO> list(
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
+            @QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Returns a paged list of roles matching the provided FIQL search condition.
+     *
+     * @param fiql FIQL search expression
+     * @return paged list of roles matching the provided FIQL search condition
+     */
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<RoleTO> search(@NotNull @QueryParam(PARAM_FIQL) String fiql);
+
+    /**
+     * Returns a paged list of roles matching the provided FIQL search condition.
+     *
+     * @param fiql FIQL search expression
+     * @param orderBy list of ordering clauses, separated by comma
+     * @return paged list of roles matching the provided FIQL search condition
+     */
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<RoleTO> search(
+            @NotNull @QueryParam(PARAM_FIQL) String fiql, @QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Returns a paged list of roles matching the provided FIQL search condition.
+     *
+     * @param fiql FIQL search expression
+     * @param page result page number
+     * @param size number of entries per page
+     * @return paged list of roles matching the provided FIQL search condition
+     */
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<RoleTO> search(@QueryParam(PARAM_FIQL) String fiql,
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size);
+
+    /**
+     * Returns a paged list of roles matching the provided FIQL search condition.
+     *
+     * @param fiql FIQL search expression
+     * @param page result page number
+     * @param size number of entries per page
+     * @param orderBy list of ordering clauses, separated by comma
+     * @return paged list of roles matching the provided FIQL search condition
+     */
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<RoleTO> search(@QueryParam(PARAM_FIQL) String fiql,
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
+            @QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Creates a new role.
+     *
+     * @param roleTO role to be created
+     * @return <tt>Response</tt> object featuring <tt>Location</tt> header of created role as well as the role itself
+     * enriched with propagation status information - {@link RoleTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring <tt>Location</tt> header of created role as well as the "
+                + "role itself enriched with propagation status information - <tt>RoleTO</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response create(@NotNull RoleTO roleTO);
+
+    /**
+     * Updates role matching the provided roleKey.
+     *
+     * @param roleKey key of role to be updated
+     * @param roleMod modification to be applied to role matching the provided roleKey
+     * @return <tt>Response</tt> object featuring the updated role enriched with propagation status information
+     * - {@link RoleTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring the updated role enriched with propagation status information - "
+                + "<tt>RoleTO</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Path("{roleKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response update(@NotNull @PathParam("roleKey") Long roleKey, @NotNull RoleMod roleMod);
+
+    /**
+     * Deletes role matching provided roleKey.
+     *
+     * @param roleKey key of role to be deleted
+     * @return <tt>Response</tt> object featuring the deleted role enriched with propagation status information
+     * - {@link RoleTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring the deleted role enriched with propagation status information - "
+                + "<tt>RoleTO</tt> as <tt>Entity</tt>")
+    })
+    @DELETE
+    @Path("{roleKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response delete(@NotNull @PathParam("roleKey") Long roleKey);
+
+    /**
+     * Executes resource-related operations on given role.
+     *
+     * @param roleKey role id.
+     * @param type resource association action type
+     * @param resourceNames external resources to be used for propagation-related operations
+     * @return <tt>Response</tt> object featuring
+     * {@link org.apache.syncope.common.reqres.BulkActionResult} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring <tt>BulkActionResult</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Path("{roleKey}/deassociate/{type}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response bulkDeassociation(@NotNull @PathParam("roleKey") Long roleKey,
+            @NotNull @PathParam("type") ResourceDeassociationActionType type,
+            @NotNull List<ResourceName> resourceNames);
+
+    /**
+     * Executes resource-related operations on given role.
+     *
+     * @param roleKey role id.
+     * @param type resource association action type
+     * @param resourceNames external resources to be used for propagation-related operations
+     * @return <tt>Response</tt> object featuring {@link org.apache.syncope.common.reqres.BulkActionResult}
+     * as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring <tt>BulkActionResult</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Path("{roleKey}/associate/{type}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response bulkAssociation(@NotNull @PathParam("roleKey") Long roleKey,
+            @NotNull @PathParam("type") ResourceAssociationActionType type,
+            @NotNull List<ResourceName> resourceNames);
+
+    /**
+     * Executes the provided bulk action.
+     *
+     * @param bulkAction list of role ids against which the bulk action will be performed.
+     * @return Bulk action result
+     */
+    @POST
+    @Path("bulk")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    BulkActionResult bulk(@NotNull BulkAction bulkAction);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SchemaService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SchemaService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SchemaService.java
new file mode 100644
index 0000000..d3f850b
--- /dev/null
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SchemaService.java
@@ -0,0 +1,119 @@
+/*
+ * 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.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+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.cxf.jaxrs.model.wadl.Description;
+import org.apache.cxf.jaxrs.model.wadl.Descriptions;
+import org.apache.cxf.jaxrs.model.wadl.DocTarget;
+import org.apache.syncope.common.lib.to.AbstractSchemaTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.SchemaType;
+
+/**
+ * REST operations for attribute schemas.
+ */
+@Path("schemas/{kind}/{type}")
+public interface SchemaService extends JAXRSService {
+
+    /**
+     * Returns schema matching the given kind, type and name.
+     *
+     * @param <T> actual SchemaTO
+     * @param attrType kind for schemas to be read
+     * @param schemaType type for schemas to be read
+     * @param schemaKey name of schema to be read
+     * @return schema matching the given kind, type and name
+     */
+    @GET
+    @Path("{key}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    <T extends AbstractSchemaTO> T read(@NotNull @PathParam("kind") AttributableType attrType,
+            @NotNull @PathParam("type") SchemaType schemaType, @NotNull @PathParam("key") String schemaKey);
+
+    /**
+     * Returns a list of schemas with matching kind and type.
+     *
+     * @param <T> actual SchemaTO
+     * @param attrType kind for schemas to be listed
+     * @param schemaType type for schemas to be listed
+     * @return list of schemas with matching kind and type
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    <T extends AbstractSchemaTO> List<T> list(
+            @NotNull @PathParam("kind") AttributableType attrType, @NotNull @PathParam("type") SchemaType schemaType);
+
+    /**
+     * Creates a new schema.
+     *
+     * @param <T> actual SchemaTO
+     * @param attrType kind for schema to be created
+     * @param schemaType type for schema to be created
+     * @param schemaTO schema to be created
+     * @return <tt>Response</tt> object featuring <tt>Location</tt> header of created schema
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE, value = "Featuring <tt>Location</tt> header of created schema")
+    })
+    @POST
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    <T extends AbstractSchemaTO> Response create(@NotNull @PathParam("kind") AttributableType attrType,
+            @NotNull @PathParam("type") SchemaType schemaType, @NotNull T schemaTO);
+
+    /**
+     * Updates the schema matching the given kind, type and name.
+     *
+     * @param <T> actual SchemaTO
+     * @param attrType kind for schemas to be updated
+     * @param schemaType type for schemas to be updated
+     * @param schemaKey name of schema to be updated
+     * @param schemaTO updated schema to be stored
+     */
+    @PUT
+    @Path("{key}")
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    <T extends AbstractSchemaTO> void update(@NotNull @PathParam("kind") AttributableType attrType,
+            @NotNull @PathParam("type") SchemaType schemaType,
+            @NotNull @PathParam("key") String schemaKey, @NotNull T schemaTO);
+
+    /**
+     * Deletes the schema matching the given kind, type and name.
+     *
+     * @param attrType kind for schema to be deleted
+     * @param schemaType type for schema to be deleted
+     * @param schemaKey name of schema to be deleted
+     */
+    @DELETE
+    @Path("{key}")
+    void delete(@NotNull @PathParam("kind") AttributableType attrType,
+            @NotNull @PathParam("type") SchemaType schemaType,
+            @NotNull @PathParam("key") String schemaKey);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SecurityQuestionService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SecurityQuestionService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SecurityQuestionService.java
new file mode 100644
index 0000000..12a73cf
--- /dev/null
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/SecurityQuestionService.java
@@ -0,0 +1,110 @@
+/*
+ * 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.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+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.cxf.jaxrs.model.wadl.Description;
+import org.apache.cxf.jaxrs.model.wadl.Descriptions;
+import org.apache.cxf.jaxrs.model.wadl.DocTarget;
+import org.apache.syncope.common.lib.to.SecurityQuestionTO;
+
+/**
+ * REST operations for configuration.
+ */
+@Path("securityQuestions")
+public interface SecurityQuestionService extends JAXRSService {
+
+    /**
+     * Returns a list of all security questions.
+     *
+     * @return list of all security questions
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    List<SecurityQuestionTO> list();
+
+    /**
+     * Returns security question with matching id.
+     *
+     * @param securityQuestionId security question id to be read
+     * @return security question with matching id
+     */
+    @GET
+    @Path("{securityQuestionId}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    SecurityQuestionTO read(@NotNull @PathParam("securityQuestionId") Long securityQuestionId);
+
+    /**
+     * Creates a new security question.
+     *
+     * @param securityQuestionTO security question to be created
+     * @return <tt>Response</tt> object featuring <tt>Location</tt> header of created security question
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring <tt>Location</tt> header of created security question")
+    })
+    @POST
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response create(@NotNull SecurityQuestionTO securityQuestionTO);
+
+    /**
+     * Updates the security question matching the provided id.
+     *
+     * @param securityQuestionId security question id to be updated
+     * @param securityQuestionTO security question to be stored
+     */
+    @PUT
+    @Path("{securityQuestionId}")
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    void update(@NotNull @PathParam("securityQuestionId") Long securityQuestionId,
+            @NotNull SecurityQuestionTO securityQuestionTO);
+
+    /**
+     * Deletes the security question matching the provided id.
+     *
+     * @param securityQuestionId security question id to be deleted
+     */
+    @DELETE
+    @Path("{securityQuestionId}")
+    void delete(@NotNull @PathParam("securityQuestionId") Long securityQuestionId);
+
+    /**
+     * Ask for security question configured for the user matching the given username, if any.
+     *
+     * @param username username for which the security question is requested
+     * @return security question, if configured for the user matching the given username
+     */
+    @GET
+    @Path("byUser/{username}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    SecurityQuestionTO readByUser(@NotNull @PathParam("username") String username);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
new file mode 100644
index 0000000..c3296dd
--- /dev/null
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
@@ -0,0 +1,245 @@
+/*
+ * 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.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.MatrixParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+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.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.jaxrs.model.wadl.Description;
+import org.apache.cxf.jaxrs.model.wadl.Descriptions;
+import org.apache.cxf.jaxrs.model.wadl.DocTarget;
+import org.apache.syncope.common.lib.to.AbstractTaskTO;
+import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.ReportExecTO;
+import org.apache.syncope.common.lib.to.SchedTaskTO;
+import org.apache.syncope.common.lib.to.TaskExecTO;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.lib.wrap.JobClass;
+import org.apache.syncope.common.lib.wrap.PushActionClass;
+import org.apache.syncope.common.lib.wrap.SyncActionClass;
+
+/**
+ * REST operations for tasks.
+ */
+@Path("tasks")
+public interface TaskService extends JAXRSService {
+
+    /**
+     * Returns a list of classes to be used for jobs.
+     *
+     * @return list of classes to be used for jobs
+     */
+    @GET
+    @Path("jobClasses")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    List<JobClass> getJobClasses();
+
+    /**
+     * Returns a list of classes to be used as synchronization actions.
+     *
+     * @return list of classes to be used as synchronization actions
+     */
+    @GET
+    @Path("syncActionsClasses")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    List<SyncActionClass> getSyncActionsClasses();
+
+    /**
+     * Returns a list of classes to be used as push actions.
+     *
+     * @return list of classes to be used as push actions
+     */
+    @GET
+    @Path("pushActionsClasses")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    List<PushActionClass> getPushActionsClasses();
+
+    /**
+     * Returns the task matching the given id.
+     *
+     * @param taskKey key of task to be read
+     * @param <T> type of taskTO
+     * @return task with matching id
+     */
+    @GET
+    @Path("{taskKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    <T extends AbstractTaskTO> T read(@NotNull @PathParam("taskKey") Long taskKey);
+
+    /**
+     * Returns the task execution with the given id.
+     *
+     * @param executionKey key of task execution to be read
+     * @return task execution with matching Id
+     */
+    @GET
+    @Path("executions/{executionKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    TaskExecTO readExecution(@NotNull @PathParam("executionKey") Long executionKey);
+
+    /**
+     * Returns a list of tasks with matching type.
+     *
+     * @param taskType type of tasks to be listed
+     * @param <T> type of taskTO
+     * @return list of tasks with matching type
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    <T extends AbstractTaskTO> PagedResult<T> list(@NotNull @MatrixParam("type") TaskType taskType);
+
+    /**
+     * Returns a list of tasks with matching type.
+     *
+     * @param taskType type of tasks to be listed
+     * @param orderBy list of ordering clauses, separated by comma
+     * @param <T> type of taskTO
+     * @return list of tasks with matching type
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    <T extends AbstractTaskTO> PagedResult<T> list(@NotNull @MatrixParam("type") TaskType taskType,
+            @QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Returns a paged list of existing tasks matching type and page/size conditions.
+     *
+     * @param taskType type of tasks to be listed
+     * @param page page number of tasks in relation to page size
+     * @param size number of tasks listed per page
+     * @param orderBy list of ordering clauses, separated by comma
+     * @param <T> type of taskTO
+     * @return paged list of existing tasks matching type and page/size conditions
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    <T extends AbstractTaskTO> PagedResult<T> list(@NotNull @MatrixParam("type") TaskType taskType,
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
+            @QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Returns a paged list of existing tasks matching type and page/size conditions.
+     *
+     * @param taskType type of tasks to be listed
+     * @param page page number of tasks in relation to page size
+     * @param size number of tasks listed per page
+     * @param <T> type of taskTO
+     * @return paged list of existing tasks matching type and page/size conditions
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    <T extends AbstractTaskTO> PagedResult<T> list(@MatrixParam("type") TaskType taskType,
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size);
+
+    /**
+     * Creates a new task.
+     *
+     * @param taskTO task to be created
+     * @param <T> type of taskTO
+     * @return <tt>Response</tt> object featuring <tt>Location</tt> header of created task
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE, value = "Featuring <tt>Location</tt> header of created task")
+    })
+    @POST
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    <T extends SchedTaskTO> Response create(@NotNull T taskTO);
+
+    /**
+     * Updates the task matching the provided key.
+     *
+     * @param taskKey key of task to be updated
+     * @param taskTO updated task to be stored
+     */
+    @PUT
+    @Path("{taskKey}")
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    void update(@NotNull @PathParam("taskKey") Long taskKey, @NotNull AbstractTaskTO taskTO);
+
+    /**
+     * Deletes the task matching the provided key.
+     *
+     * @param taskKey key of task to be deleted
+     */
+    @DELETE
+    @Path("{taskKey}")
+    void delete(@NotNull @PathParam("taskKey") Long taskKey);
+
+    /**
+     * Deletes the task execution matching the provided key.
+     *
+     * @param executionKey key of task execution to be deleted
+     */
+    @DELETE
+    @Path("executions/{executionKey}")
+    void deleteExecution(@NotNull @PathParam("executionKey") Long executionKey);
+
+    /**
+     * Executes the task matching the given id.
+     *
+     * @param taskKey key of task to be executed
+     * @param dryRun if true, task will only be simulated
+     * @return execution report for the task matching the given id
+     */
+    @POST
+    @Path("{taskKey}/execute")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    TaskExecTO execute(@NotNull @PathParam("taskKey") Long taskKey,
+            @QueryParam("dryRun") @DefaultValue("false") boolean dryRun);
+
+    /**
+     * Reports task execution result.
+     *
+     * @param executionKey key of task execution being reported
+     * @param reportExec execution being reported
+     */
+    @POST
+    @Path("executions/{executionKey}/report")
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    void report(@NotNull @PathParam("executionKey") Long executionKey, @NotNull ReportExecTO reportExec);
+
+    /**
+     * Executes the provided bulk action.
+     *
+     * @param bulkAction list of task ids against which the bulk action will be performed.
+     * @return Bulk action result
+     */
+    @POST
+    @Path("bulk")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    BulkActionResult bulk(@NotNull BulkAction bulkAction);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java
new file mode 100644
index 0000000..cd3a302
--- /dev/null
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.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.common.rest.api.service;
+
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+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.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.jaxrs.model.wadl.Description;
+import org.apache.cxf.jaxrs.model.wadl.Descriptions;
+import org.apache.cxf.jaxrs.model.wadl.DocTarget;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.UserTO;
+
+/**
+ * REST operations for user self-management.
+ */
+@Path("users/self")
+public interface UserSelfService extends JAXRSService {
+
+    /**
+     * Checks whether self-registration is allowed.
+     *
+     * @return <tt>Response</tt> contains special Syncope HTTP header indicating if user self registration and / or
+     * password reset is allowed
+     * @see org.apache.syncope.common.types.RESTHeaders#SELFREG_ALLOWED
+     * @see org.apache.syncope.common.types.RESTHeaders#PWDRESET_ALLOWED
+     * @see org.apache.syncope.common.types.RESTHeaders#PWDRESET_NEEDS_SECURITYQUESTIONS
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Contains special Syncope HTTP header indicating if user self registration "
+                + "and / or password reset is allowed")
+    })
+    @OPTIONS
+    Response getOptions();
+
+    /**
+     * Returns the user making the service call.
+     *
+     * @return calling user data
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    UserTO read();
+
+    /**
+     * Self-registration for new user.
+     *
+     * @param userTO user to be created
+     * @param storePassword whether password shall be stored internally
+     * @return <tt>Response</tt> object featuring <tt>Location</tt> header of self-registered user as well as the user
+     * itself - {@link UserTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring <tt>Location</tt> header of self-registered user as well "
+                + "as the user itself - {@link UserTO} as <tt>Entity</tt>")
+    })
+    @POST
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response create(@NotNull UserTO userTO,
+            @DefaultValue("true") @QueryParam("storePassword") boolean storePassword);
+
+    /**
+     * Self-updates user.
+     *
+     * @param userKey id of user to be updated
+     * @param userMod modification to be applied to user matching the provided userKey
+     * @return <tt>Response</tt> object featuring the updated user - {@link UserTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring the updated user - <tt>UserTO</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Path("{userKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response update(@NotNull @PathParam("userKey") Long userKey, @NotNull UserMod userMod);
+
+    /**
+     * Self-deletes user.
+     *
+     * @return <tt>Response</tt> object featuring the deleted user - {@link UserTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring the deleted user - <tt>UserTO</tt> as <tt>Entity</tt>")
+    })
+    @DELETE
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response delete();
+
+    /**
+     * Provides answer for the security question configured for user matching the given username, if any.
+     * If provided anwser matches the one stored for that user, a password reset token is internally generated,
+     * otherwise an error is returned.
+     *
+     * @param username username for which the security answer is provided
+     * @param securityAnswer actual answer text
+     */
+    @POST
+    @Path("requestPasswordReset")
+    void requestPasswordReset(@NotNull @QueryParam("username") String username, String securityAnswer);
+
+    /**
+     * Reset the password value for the user matching the provided token, if available and still valid.
+     * If the token actually matches one of users, and if it is still valid at the time of submission, the matching
+     * user's password value is set as provided. The new password value will need anyway to comply with all relevant
+     * password policies.
+     *
+     * @param token password reset token
+     * @param password new password to be set
+     */
+    @POST
+    @Path("confirmPasswordReset")
+    void confirmPasswordReset(@NotNull @QueryParam("token") String token, String password);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
new file mode 100644
index 0000000..ea1d197
--- /dev/null
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
@@ -0,0 +1,321 @@
+/*
+ * 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.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+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.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.jaxrs.model.wadl.Description;
+import org.apache.cxf.jaxrs.model.wadl.Descriptions;
+import org.apache.cxf.jaxrs.model.wadl.DocTarget;
+import org.apache.syncope.common.lib.mod.ResourceAssociationMod;
+import org.apache.syncope.common.lib.mod.StatusMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.ResourceAssociationActionType;
+import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
+import org.apache.syncope.common.lib.wrap.ResourceName;
+
+/**
+ * REST operations for users.
+ */
+@Path("users")
+public interface UserService extends JAXRSService {
+
+    /**
+     * Gives the username for the provided user key.
+     *
+     * @param userKey user key
+     * @return <tt>Response</tt> object featuring HTTP header with username matching the given userKey
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring HTTP header with username matching the given userKey")
+    })
+    @OPTIONS
+    @Path("{userKey}/username")
+    Response getUsername(@NotNull @PathParam("userKey") Long userKey);
+
+    /**
+     * Gives the user key for the provided username.
+     *
+     * @param username username
+     * @return <tt>Response</tt> object featuring HTTP header with userKey matching the given username
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring HTTP header with userKey matching the given username")
+    })
+    @OPTIONS
+    @Path("{username}/userKey")
+    Response getUserId(@NotNull @PathParam("username") String username);
+
+    /**
+     * Reads the user matching the provided userKey.
+     *
+     * @param userKey id of user to be read
+     * @return User matching the provided userKey
+     */
+    @GET
+    @Path("{userKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    UserTO read(@NotNull @PathParam("userKey") Long userKey);
+
+    /**
+     * Returns a paged list of existing users.
+     *
+     * @return paged list of all existing users
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<UserTO> list();
+
+    /**
+     * Returns a paged list of existing users.
+     *
+     * @param orderBy list of ordering clauses, separated by comma
+     * @return paged list of all existing users
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<UserTO> list(@QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Returns a paged list of existing users matching page/size conditions.
+     *
+     * @param page result page number
+     * @param size number of entries per page
+     * @return paged list of existing users matching page/size conditions
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<UserTO> list(
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size);
+
+    /**
+     * Returns a paged list of existing users matching page/size conditions.
+     *
+     * @param page result page number
+     * @param size number of entries per page
+     * @param orderBy list of ordering clauses, separated by comma
+     * @return paged list of existing users matching page/size conditions
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<UserTO> list(
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
+            @QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Returns a paged list of users matching the provided FIQL search condition.
+     *
+     * @param fiql FIQL search expression
+     * @return paged list of users matching the provided FIQL search condition
+     */
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<UserTO> search(@NotNull @QueryParam(PARAM_FIQL) String fiql);
+
+    /**
+     * Returns a paged list of users matching the provided FIQL search condition.
+     *
+     * @param fiql FIQL search expression
+     * @param orderBy list of ordering clauses, separated by comma
+     * @return paged list of users matching the provided FIQL search condition
+     */
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<UserTO> search(@NotNull @QueryParam(PARAM_FIQL) String fiql, @QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Returns a paged list of users matching the provided FIQL search condition.
+     *
+     * @param fiql FIQL search expression
+     * @param page result page number
+     * @param size number of entries per page
+     * @return paged list of users matching the provided FIQL search condition
+     */
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<UserTO> search(@QueryParam(PARAM_FIQL) String fiql,
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size);
+
+    /**
+     * Returns a paged list of users matching the provided FIQL search condition.
+     *
+     * @param fiql FIQL search expression
+     * @param page result page number
+     * @param size number of entries per page
+     * @param orderBy list of ordering clauses, separated by comma
+     * @return paged list of users matching the provided FIQL search condition
+     */
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<UserTO> search(@QueryParam(PARAM_FIQL) String fiql,
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
+            @QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Creates a new user.
+     *
+     * @param userTO user to be created
+     * @param storePassword whether password shall be stored internally
+     * @return <tt>Response</tt> object featuring <tt>Location</tt> header of created user as well as the user itself
+     * enriched with propagation status information - {@link UserTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring <tt>Location</tt> header of created user as well as the "
+                + "user itself enriched with propagation status information - <tt>UserTO</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response create(@NotNull UserTO userTO,
+            @DefaultValue("true") @QueryParam("storePassword") boolean storePassword);
+
+    /**
+     * Updates user matching the provided userKey.
+     *
+     * @param userKey id of user to be updated
+     * @param userMod modification to be applied to user matching the provided userKey
+     * @return <tt>Response</tt> object featuring the updated user enriched with propagation status information
+     * - {@link UserTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring the updated user enriched with propagation status information - "
+                + "<tt>UserTO</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Path("{userKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response update(@NotNull @PathParam("userKey") Long userKey, @NotNull UserMod userMod);
+
+    /**
+     * Performs a status update on user matching provided userKey.
+     *
+     * @param userKey id of user to be subjected to status update
+     * @param statusMod status update details
+     * @return <tt>Response</tt> object featuring the updated user enriched with propagation status information
+     * - {@link UserTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring the updated user enriched with propagation status information - "
+                + "<tt>UserTO</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Path("{userKey}/status")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response status(@NotNull @PathParam("userKey") Long userKey, @NotNull StatusMod statusMod);
+
+    /**
+     * Deletes user matching provided userKey.
+     *
+     * @param userKey id of user to be deleted
+     * @return <tt>Response</tt> object featuring the deleted user enriched with propagation status information
+     * - {@link UserTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring the deleted user enriched with propagation status information - "
+                + "<tt>UserTO</tt> as <tt>Entity</tt>")
+    })
+    @DELETE
+    @Path("{userKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response delete(@NotNull @PathParam("userKey") Long userKey);
+
+    /**
+     * Executes resource-related operations on given user.
+     *
+     * @param userKey user key
+     * @param type resource de-association action type
+     * @param resourceNames external resources to be used for propagation-related operations
+     * @return <tt>Response</tt> object featuring {@link BulkActionResult} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring <tt>BulkActionResult</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Path("{userKey}/bulkDeassociation/{type}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response bulkDeassociation(@NotNull @PathParam("userKey") Long userKey,
+            @NotNull @PathParam("type") ResourceDeassociationActionType type,
+            @NotNull List<ResourceName> resourceNames);
+
+    /**
+     * Executes resource-related operations on given user.
+     *
+     * @param userKey user key.
+     * @param type resource association action type
+     * @param associationMod external resources to be used for propagation-related operations
+     * @return <tt>Response</tt> object featuring {@link BulkActionResult} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE, value = "Featuring <tt>BulkActionResult</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Path("{userKey}/bulkAssociation/{type}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response bulkAssociation(@NotNull @PathParam("userKey") Long userKey,
+            @NotNull @PathParam("type") ResourceAssociationActionType type,
+            @NotNull ResourceAssociationMod associationMod);
+
+    /**
+     * Executes the provided bulk action.
+     *
+     * @param bulkAction list of user keys against which the bulk action will be performed.
+     * @return Bulk action result
+     */
+    @POST
+    @Path("bulk")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    BulkActionResult bulk(@NotNull BulkAction bulkAction);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java
new file mode 100644
index 0000000..256317d
--- /dev/null
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserWorkflowService.java
@@ -0,0 +1,108 @@
+/*
+ * 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.common.rest.api.service;
+
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.to.UserTO;
+import org.apache.syncope.common.lib.to.WorkflowFormTO;
+
+/**
+ * REST operations related to user workflow.
+ */
+@Path("userworkflow")
+public interface UserWorkflowService extends JAXRSService {
+
+    /**
+     * Returns a list of all available workflow forms.
+     *
+     * @return list of all available workflow forms
+     */
+    @GET
+    @Path("forms")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    List<WorkflowFormTO> getForms();
+
+    /**
+     * Returns a list of all available workflow forms with matching name, for the given user key.
+     *
+     * @param userKey user key
+     * @param name form name
+     * @return list of all available workflow forms with matching name, fir the given user key.
+     */
+    @GET
+    @Path("forms/{userKey}/{name}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    List<WorkflowFormTO> getFormsByName(
+            @NotNull @PathParam("userKey") final Long userKey, @NotNull @PathParam("name") final String name);
+
+    /**
+     * Returns a list of available forms for the given user key.
+     *
+     * @param userKey user key
+     * @return list of available forms for the given user key
+     */
+    @GET
+    @Path("forms/{userKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    WorkflowFormTO getFormForUser(@NotNull @PathParam("userKey") Long userKey);
+
+    /**
+     * Claims the form for the given task id.
+     *
+     * @param taskId workflow task id
+     * @return the workflow form for the given task id
+     */
+    @POST
+    @Path("forms/{taskId}/claim")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    WorkflowFormTO claimForm(@NotNull @PathParam("taskId") String taskId);
+
+    /**
+     * Submits a workflow form.
+     *
+     * @param form workflow form.
+     * @return updated user
+     */
+    @POST
+    @Path("forms")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    UserTO submitForm(@NotNull WorkflowFormTO form);
+
+    /**
+     * Executes workflow task for matching id.
+     *
+     * @param taskId workflow task id
+     * @param userTO argument to be passed to workflow task
+     * @return updated user
+     */
+    @POST
+    @Path("tasks/{taskId}/execute")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    UserTO executeTask(@NotNull @PathParam("taskId") String taskId, @NotNull UserTO userTO);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java
----------------------------------------------------------------------
diff --git a/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java
new file mode 100644
index 0000000..709ab37
--- /dev/null
+++ b/syncope620/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java
@@ -0,0 +1,90 @@
+/*
+ * 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.common.rest.api.service;
+
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+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.cxf.jaxrs.model.wadl.Description;
+import org.apache.cxf.jaxrs.model.wadl.Descriptions;
+import org.apache.cxf.jaxrs.model.wadl.DocTarget;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+
+/**
+ * REST operations for workflow definition management.
+ */
+@Path("workflows/{kind}")
+public interface WorkflowService extends JAXRSService {
+
+    /**
+     * Checks whether Activiti is enabled (for users or roles).
+     *
+     * @param kind user or role
+     * @return <tt>Response</tt> contains special syncope HTTP header indicating if Activiti is enabled for
+     * users / roles
+     * @see org.apache.syncope.common.types.RESTHeaders#ACTIVITI_USER_ENABLED
+     * @see org.apache.syncope.common.types.RESTHeaders#ACTIVITI_ROLE_ENABLED
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Contains special syncope HTTP header indicating if Activiti is enabled for users / roles")
+    })
+    @OPTIONS
+    Response getOptions(@NotNull @PathParam("kind") SubjectType kind);
+
+    /**
+     * Exports workflow definition for matching kind.
+     *
+     * @param kind user or role
+     * @return workflow definition for matching kind
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response exportDefinition(@NotNull @PathParam("kind") SubjectType kind);
+
+    /**
+     * Exports workflow diagram representation.
+     *
+     * @param kind user or role
+     * @return workflow diagram representation
+     */
+    @GET
+    @Path("diagram.png")
+    @Produces({ RESTHeaders.MEDIATYPE_IMAGE_PNG })
+    Response exportDiagram(@NotNull @PathParam("kind") SubjectType kind);
+
+    /**
+     * Imports workflow definition for matching kind.
+     *
+     * @param kind user or role
+     * @param definition workflow definition for matching kind
+     */
+    @PUT
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    void importDefinition(@NotNull @PathParam("kind") SubjectType kind, @NotNull String definition);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/WorkflowLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/WorkflowLogic.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/WorkflowLogic.java
index 2e5ae74..f7a11fd 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/WorkflowLogic.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/WorkflowLogic.java
@@ -93,6 +93,7 @@ public class WorkflowLogic extends AbstractTransactionalLogic<AbstractBaseBean>
 
     private void importDefinition(
             final WorkflowAdapter adapter, final WorkflowDefinitionFormat format, final String definition) {
+
         adapter.importDefinition(format, definition);
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/server/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/pom.xml b/syncope620/server/pom.xml
index 9db6358..240c8c3 100644
--- a/syncope620/server/pom.xml
+++ b/syncope620/server/pom.xml
@@ -42,6 +42,7 @@ under the License.
     <module>workflow-api</module>
     <module>workflow-java</module>
     <module>logic</module>
+    <module>rest-cxf</module>
   </modules>
 
 </project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/server/rest-cxf/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/rest-cxf/pom.xml b/syncope620/server/rest-cxf/pom.xml
new file mode 100644
index 0000000..c8e020c
--- /dev/null
+++ b/syncope620/server/rest-cxf/pom.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope</groupId>
+    <artifactId>syncope-server</artifactId>
+    <version>2.0.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Server REST CXF</name>
+  <description>Apache Syncope Server REST CXF</description>
+  <groupId>org.apache.syncope.server</groupId>
+  <artifactId>syncope-server-rest-cxf</artifactId>
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jpa_2.0_spec</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-orm</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.fasterxml.jackson.jaxrs</groupId>
+      <artifactId>jackson-jaxrs-json-provider</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.module</groupId>
+      <artifactId>jackson-module-afterburner</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-rs-extension-providers</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-rs-extension-search</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-frontend-jaxws</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-rs-service-description</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-rs-client</artifactId>
+    </dependency>  
+    
+    <dependency>
+      <groupId>org.apache.syncope.server</groupId>
+      <artifactId>syncope-server-logic</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.common</groupId>
+      <artifactId>syncope-common-rest-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/QueryResourceInfoComparator.java
----------------------------------------------------------------------
diff --git a/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/QueryResourceInfoComparator.java b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/QueryResourceInfoComparator.java
new file mode 100644
index 0000000..7c879c8
--- /dev/null
+++ b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/QueryResourceInfoComparator.java
@@ -0,0 +1,113 @@
+/*
+ * 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.server.rest.cxf;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.cxf.jaxrs.ext.ResourceComparator;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.jaxrs.model.OperationResourceInfo;
+import org.apache.cxf.jaxrs.model.OperationResourceInfoComparator;
+import org.apache.cxf.jaxrs.model.Parameter;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
+
+public class QueryResourceInfoComparator extends OperationResourceInfoComparator implements ResourceComparator {
+
+    public QueryResourceInfoComparator() {
+        super(null, null);
+    }
+
+    @Override
+    public int compare(final ClassResourceInfo cri1, final ClassResourceInfo cri2, final Message message) {
+        // Leave Class selection to CXF
+        return 0;
+    }
+
+    @Override
+    public int compare(final OperationResourceInfo oper1, final OperationResourceInfo oper2, final Message message) {
+        // Check if CXF can make a decision
+        int cxfResult = super.compare(oper1, oper2);
+        if (cxfResult != 0) {
+            return cxfResult;
+        }
+
+        int op1Counter = getMatchingRate(oper1, message);
+        int op2Counter = getMatchingRate(oper2, message);
+
+        return op1Counter == op2Counter
+                ? 0
+                : op1Counter < op2Counter
+                        ? 1
+                        : -1;
+    }
+
+    /**
+     * This method calculates a number indicating a good or bad match between values provided within the request and
+     * expected method parameters. A higher number means a better match.
+     *
+     * @param operation The operation to be rated, based on contained parameterInfo values.
+     * @param message A message containing query and header values from user request
+     * @return A positive or negative number, indicating a good match between query and method
+     */
+    protected int getMatchingRate(final OperationResourceInfo operation, final Message message) {
+        List<Parameter> params = operation.getParameters();
+        if (params == null || params.isEmpty()) {
+            return 0;
+        }
+
+        // Get Request QueryParams
+        String query = (String) message.get(Message.QUERY_STRING);
+        String path = (String) message.get(Message.REQUEST_URI);
+        Map<String, List<String>> qParams = JAXRSUtils.getStructuredParams(query, "&", true, false);
+        Map<String, List<String>> mParams = JAXRSUtils.getMatrixParams(path, true);
+        // Get Request Headers
+        Map<?, ?> qHeader = (java.util.Map<?, ?>) message.get(Message.PROTOCOL_HEADERS);
+
+        int rate = 0;
+        for (Parameter p : params) {
+            switch (p.getType()) {
+                case QUERY:
+                    if (qParams.containsKey(p.getName())) {
+                        rate += 2;
+                    } else if (p.getDefaultValue() == null) {
+                        rate -= 1;
+                    }
+                    break;
+                case MATRIX:
+                    if (mParams.containsKey(p.getName())) {
+                        rate += 2;
+                    } else if (p.getDefaultValue() == null) {
+                        rate -= 1;
+                    }
+                    break;
+                case HEADER:
+                    if (qHeader.containsKey(p.getName())) {
+                        rate += 2;
+                    } else if (p.getDefaultValue() == null) {
+                        rate -= 1;
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+        return rate;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AbstractServiceImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AbstractServiceImpl.java b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AbstractServiceImpl.java
new file mode 100644
index 0000000..9168ab1
--- /dev/null
+++ b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AbstractServiceImpl.java
@@ -0,0 +1,227 @@
+/*
+ * 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.server.rest.cxf.service;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.jaxrs.ext.MessageContext;
+import org.apache.cxf.jaxrs.ext.search.SearchBean;
+import org.apache.cxf.jaxrs.ext.search.SearchCondition;
+import org.apache.cxf.jaxrs.ext.search.SearchContext;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.rest.api.service.JAXRSService;
+import org.apache.syncope.common.rest.api.Preference;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.server.misc.search.SearchCondVisitor;
+import org.apache.syncope.server.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.server.persistence.api.dao.search.SearchCond;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractServiceImpl implements JAXRSService {
+
+    /**
+     * Logger.
+     */
+    protected static final Logger LOG = LoggerFactory.getLogger(AbstractServiceImpl.class);
+
+    protected static final String OPTIONS_ALLOW = "GET,POST,OPTIONS,HEAD";
+
+    @Context
+    protected UriInfo uriInfo;
+
+    @Context
+    protected MessageContext messageContext;
+
+    @Context
+    protected SearchContext searchContext;
+
+    /**
+     * Reads <tt>Prefer</tt> header from request and parses into a <tt>Preference</tt> instance.
+     *
+     * @return a <tt>Preference</tt> instance matching the passed <tt>Prefer</tt> header,
+     * or <tt>Preference.NONE</tt> if missing.
+     */
+    protected Preference getPreference() {
+        return Preference.fromString(messageContext.getHttpHeaders().getHeaderString(RESTHeaders.PREFER));
+    }
+
+    /**
+     * Builds response to successful <tt>create</tt> request, taking into account any <tt>Prefer</tt> header.
+     *
+     * @param id identifier of the created entity
+     * @param entity the entity just created
+     * @return response to successful <tt>create</tt> request
+     */
+    protected Response createResponse(final Object id, final Object entity) {
+        URI location = uriInfo.getAbsolutePathBuilder().path(String.valueOf(id)).build();
+
+        Response.ResponseBuilder builder = Response.
+                created(location).
+                header(RESTHeaders.RESOURCE_ID, id);
+
+        switch (getPreference()) {
+            case RETURN_NO_CONTENT:
+                break;
+
+            case RETURN_CONTENT:
+            case NONE:
+            default:
+                builder = builder.entity(entity);
+                break;
+
+        }
+        if (getPreference() == Preference.RETURN_CONTENT || getPreference() == Preference.RETURN_NO_CONTENT) {
+            builder = builder.header(RESTHeaders.PREFERENCE_APPLIED, getPreference().toString());
+        }
+
+        return builder.build();
+    }
+
+    /**
+     * Builds response to successful modification request, taking into account any <tt>Prefer</tt> header.
+     *
+     * @param entity the entity just modified
+     * @return response to successful modification request
+     */
+    protected Response modificationResponse(final Object entity) {
+        Response.ResponseBuilder builder;
+        switch (getPreference()) {
+            case RETURN_NO_CONTENT:
+                builder = Response.noContent();
+                break;
+
+            case RETURN_CONTENT:
+            case NONE:
+            default:
+                builder = Response.ok(entity);
+                break;
+        }
+        if (getPreference() == Preference.RETURN_CONTENT || getPreference() == Preference.RETURN_NO_CONTENT) {
+            builder = builder.header(RESTHeaders.PREFERENCE_APPLIED, getPreference().toString());
+        }
+
+        return builder.build();
+    }
+
+    protected void checkETag(final String etag) {
+        Response.ResponseBuilder builder = messageContext.getRequest().evaluatePreconditions(new EntityTag(etag));
+        if (builder != null) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.ConcurrentModification);
+            sce.getElements().add("Mismatching ETag value");
+            throw sce;
+        }
+    }
+
+    protected SearchCond getSearchCond(final String fiql) {
+        try {
+            SearchCondVisitor visitor = new SearchCondVisitor();
+            SearchCondition<SearchBean> sc = searchContext.getCondition(fiql, SearchBean.class);
+            sc.accept(visitor);
+
+            return visitor.getQuery();
+        } catch (Exception e) {
+            LOG.error("Invalid FIQL expression: {}", fiql, e);
+
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression);
+            sce.getElements().add(fiql);
+            throw sce;
+        }
+    }
+
+    protected List<OrderByClause> getOrderByClauses(final String orderBy) {
+        if (StringUtils.isBlank(orderBy)) {
+            return Collections.<OrderByClause>emptyList();
+        }
+
+        List<OrderByClause> result = new ArrayList<OrderByClause>();
+
+        for (String clause : orderBy.split(",")) {
+            String[] elems = clause.split(" ");
+
+            if (elems.length > 0 && StringUtils.isNotBlank(elems[0])) {
+                OrderByClause obc = new OrderByClause();
+                obc.setField(elems[0].trim());
+                if (elems.length > 1 && StringUtils.isNotBlank(elems[1])) {
+                    obc.setDirection(elems[1].trim().equalsIgnoreCase(OrderByClause.Direction.ASC.name())
+                            ? OrderByClause.Direction.ASC : OrderByClause.Direction.DESC);
+                }
+                result.add(obc);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Builds a paged result out of a list of items and additional information.
+     *
+     * @param <T> result type
+     * @param list bare list of items to be returned
+     * @param page current page
+     * @param size requested size
+     * @param totalCount total result size (not considering pagination)
+     * @return paged result
+     */
+    protected <T extends AbstractBaseBean> PagedResult<T> buildPagedResult(
+            final List<T> list, final int page, final int size, final int totalCount) {
+
+        PagedResult<T> result = new PagedResult<T>();
+        result.getResult().addAll(list);
+
+        result.setPage(page);
+        result.setSize(result.getResult().size());
+        result.setTotalCount(totalCount);
+
+        UriBuilder builder = uriInfo.getAbsolutePathBuilder();
+        MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
+        for (Map.Entry<String, List<String>> queryParam : queryParams.entrySet()) {
+            builder = builder.queryParam(queryParam.getKey(), queryParam.getValue().toArray());
+        }
+
+        if (result.getPage() > 1) {
+            result.setPrev(builder.
+                    replaceQueryParam(PARAM_PAGE, result.getPage() - 1).
+                    replaceQueryParam(PARAM_SIZE, size).
+                    build());
+        }
+        if ((result.getPage() - 1) * size + result.getSize() < totalCount) {
+            result.setNext(builder.
+                    replaceQueryParam(PARAM_PAGE, result.getPage() + 1).
+                    replaceQueryParam(PARAM_SIZE, size).
+                    build());
+        }
+
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AddETagFilter.java
----------------------------------------------------------------------
diff --git a/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AddETagFilter.java b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AddETagFilter.java
new file mode 100644
index 0000000..164e1be
--- /dev/null
+++ b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/AddETagFilter.java
@@ -0,0 +1,50 @@
+/*
+ * 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.server.rest.cxf.service;
+
+import java.io.IOException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.ext.Provider;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.to.AbstractAnnotatedBean;
+
+/**
+ * Adds the <tt>ETag</tt> filter to any response containing an instance of <tt>AbstractSysInfoTO</tt> as entity.
+ * The actual ETag value is computed on the basis of last change date (or creation date if not available).
+ *
+ * @see AbstractSysInfoTO
+ */
+@Provider
+public class AddETagFilter implements ContainerResponseFilter {
+
+    @Override
+    public void filter(final ContainerRequestContext reqCtx, final ContainerResponseContext resCtx) throws IOException {
+        if (resCtx.getEntity() instanceof AbstractAnnotatedBean && resCtx.getEntityTag() == null) {
+            AbstractAnnotatedBean sysInfo = (AbstractAnnotatedBean) resCtx.getEntity();
+            String etagValue = sysInfo.getETagValue();
+            if (StringUtils.isNotBlank(etagValue)) {
+                resCtx.getHeaders().add(HttpHeaders.ETAG, new EntityTag(etagValue).toString());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/8b4e52d7/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/ConfigurationServiceImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/ConfigurationServiceImpl.java b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/ConfigurationServiceImpl.java
new file mode 100644
index 0000000..3a06c51
--- /dev/null
+++ b/syncope620/server/rest-cxf/src/main/java/org/apache/syncope/server/rest/cxf/service/ConfigurationServiceImpl.java
@@ -0,0 +1,91 @@
+/*
+ * 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.server.rest.cxf.service;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.StreamingOutput;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.ConfTO;
+import org.apache.syncope.common.lib.wrap.MailTemplate;
+import org.apache.syncope.common.lib.wrap.Validator;
+import org.apache.syncope.common.rest.api.CollectionWrapper;
+import org.apache.syncope.common.rest.api.service.ConfigurationService;
+import org.apache.syncope.server.logic.ConfigurationLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ConfigurationServiceImpl extends AbstractServiceImpl implements ConfigurationService {
+
+    private static final String CONTENT_XML = "content.xml";
+
+    @Autowired
+    private ConfigurationLogic logic;
+
+    @Override
+    public Response export() {
+        StreamingOutput sout = new StreamingOutput() {
+
+            @Override
+            public void write(final OutputStream os) throws IOException {
+                logic.export(os);
+            }
+        };
+        return Response.ok(sout).
+                type(MediaType.TEXT_XML).
+                header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + CONTENT_XML).
+                build();
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+
+    @Override
+    public List<MailTemplate> getMailTemplates() {
+        return CollectionWrapper.wrap(logic.getMailTemplates(), MailTemplate.class);
+    }
+
+    @Override
+    public List<Validator> getValidators() {
+        return CollectionWrapper.wrap(logic.getValidators(), Validator.class);
+    }
+
+    @Override
+    public ConfTO list() {
+        return logic.list();
+    }
+
+    @Override
+    public AttrTO read(final String key) {
+        return logic.read(key);
+    }
+
+    @Override
+    public void set(final String key, final AttrTO value) {
+        value.setSchema(key);
+        logic.set(value);
+    }
+}


Mime
View raw message