You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2013/12/02 17:47:18 UTC

svn commit: r1547101 - in /syncope/trunk: client/src/main/java/org/apache/syncope/client/ client/src/main/java/org/apache/syncope/client/rest/ common/src/main/java/org/apache/syncope/common/to/ common/src/main/java/org/apache/syncope/common/types/ core...

Author: ilgrosso
Date: Mon Dec  2 16:47:18 2013
New Revision: 1547101

URL: http://svn.apache.org/r1547101
Log:
[SYNCOPE-429] Adding ETag generation for user and role objects + support for If-Match / If-None-Match headers

Added:
    syncope/trunk/core/src/main/java/org/apache/syncope/core/services/AddETagFilter.java
Modified:
    syncope/trunk/client/src/main/java/org/apache/syncope/client/SyncopeClient.java
    syncope/trunk/client/src/main/java/org/apache/syncope/client/rest/RestClientFactoryBean.java
    syncope/trunk/common/src/main/java/org/apache/syncope/common/to/AbstractSysInfoTO.java
    syncope/trunk/common/src/main/java/org/apache/syncope/common/types/Preference.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/services/AbstractServiceImpl.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserSelfServiceImpl.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java
    syncope/trunk/core/src/main/resources/restContext.xml
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/AbstractTest.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ConfigurationTestITCase.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ConnectorTestITCase.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/NotificationTestITCase.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ReportTestITCase.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ResourceTestITCase.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserSelfTestITCase.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java

Modified: syncope/trunk/client/src/main/java/org/apache/syncope/client/SyncopeClient.java
URL: http://svn.apache.org/viewvc/syncope/trunk/client/src/main/java/org/apache/syncope/client/SyncopeClient.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/client/src/main/java/org/apache/syncope/client/SyncopeClient.java (original)
+++ syncope/trunk/client/src/main/java/org/apache/syncope/client/SyncopeClient.java Mon Dec  2 16:47:18 2013
@@ -18,9 +18,7 @@
  */
 package org.apache.syncope.client;
 
-import java.net.URI;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import javax.ws.rs.core.EntityTag;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.cxf.jaxrs.client.WebClient;
@@ -47,8 +45,6 @@ public class SyncopeClient {
 
     private final String password;
 
-    private final Map<Class<?>, Object> services = new ConcurrentHashMap<Class<?>, Object>();
-
     public SyncopeClient(final MediaType mediaType, final RestClientFactoryBean restClientFactory,
             final String username, final String password) {
 
@@ -58,37 +54,148 @@ public class SyncopeClient {
         this.password = password;
     }
 
-    @SuppressWarnings("unchecked")
+    /**
+     * Creates an instance of the given service class, with configured content type and authentication.
+     *
+     * @param <T> any service class
+     * @param serviceClass service class reference
+     * @return service instance of the given reference class
+     */
     public <T> T getService(final Class<T> serviceClass) {
-        if (!services.containsKey(serviceClass)) {
-            services.put(serviceClass,
-                    restClientFactory.createServiceInstance(serviceClass, mediaType, username, password));
-        }
-        return (T) services.get(serviceClass);
+        return restClientFactory.createServiceInstance(serviceClass, mediaType, username, password);
     }
 
-    public <T> T prefer(final Class<T> serviceClass, final Preference preference) {
-        return header(serviceClass, RESTHeaders.PREFER, preference.literal());
+    /**
+     * Sets the given header on the give service instance.
+     *
+     * @param <T> any service class
+     * @param service service class instance
+     * @param key HTTP header key
+     * @param values HTTP header values
+     * @return given service instance, with given header set
+     */
+    public <T> T header(final T service, final String key, final Object... values) {
+        WebClient.client(service).header(key, values);
+        return service;
     }
 
+    /**
+     * Creates an instance of the given service class and sets the given header.
+     *
+     * @param <T> any service class
+     * @param serviceClass service class reference
+     * @param key HTTP header key
+     * @param values HTTP header values
+     * @return service instance of the given reference class, with given header set
+     */
     public <T> T header(final Class<T> serviceClass, final String key, final Object... values) {
-        T service = getService(serviceClass);
-        WebClient.client(getService(serviceClass)).header(key, values);
-        return service;
+        return header(getService(serviceClass), key, values);
     }
 
-    public <T> T getObject(final URI location, final Class<?> serviceClass, final Class<T> resultClass) {
-        WebClient webClient = WebClient.fromClient(WebClient.client(getService(serviceClass)));
-        webClient.accept(mediaType).to(location.toASCIIString(), false);
+    /**
+     * Sets the <tt>Prefer</tt> header on the give service instance.
+     *
+     * @param <T> any service class
+     * @param service service class instance
+     * @param preference preference to be set via <tt>Prefer</tt> header
+     * @return given service instance, with <tt>Prefer</tt> header set
+     */
+    public <T> T prefer(final T service, final Preference preference) {
+        return header(service, RESTHeaders.PREFER, preference.toString());
+    }
+
+    /**
+     * Creates an instance of the given service class, with <tt>Prefer</tt> header set.
+     *
+     * @param <T> any service class
+     * @param serviceClass service class reference
+     * @param preference preference to be set via <tt>Prefer</tt> header
+     * @return service instance of the given reference class, with <tt>Prefer</tt> header set
+     */
+    public <T> T prefer(final Class<T> serviceClass, final Preference preference) {
+        return header(serviceClass, RESTHeaders.PREFER, preference.toString());
+    }
 
-        return webClient.get(resultClass);
+    /**
+     * Sets the <tt>If-Match</tt> or <tt>If-None-Match</tt> header on the given service instance.
+     *
+     * @param <T> any service class
+     * @param service service class instance
+     * @param etag ETag value
+     * @param ifNot if true then <tt>If-None-Match</tt> is set, <tt>If-Match</tt> otherwise
+     * @return given service instance, with <tt>If-Match</tt> or <tt>If-None-Match</tt> set
+     */
+    private <T> T match(final T service, final EntityTag etag, final boolean ifNot) {
+        WebClient.client(service).match(etag, ifNot);
+        return service;
     }
 
+    /**
+     * Sets the <tt>If-Match</tt> header on the given service instance.
+     *
+     * @param <T> any service class
+     * @param service service class instance
+     * @param etag ETag value
+     * @return given service instance, with <tt>If-Match</tt> set
+     */
+    public <T> T ifMatch(final T service, final EntityTag etag) {
+        return match(service, etag, false);
+    }
+
+    /**
+     * Creates an instance of the given service class, with <tt>If-Match</tt> header set.
+     *
+     * @param <T> any service class
+     * @param serviceClass service class reference
+     * @param etag ETag value
+     * @return given service instance, with <tt>If-Match</tt> set
+     */
+    public <T> T ifMatch(final Class<T> serviceClass, final EntityTag etag) {
+        return match(getService(serviceClass), etag, false);
+    }
+
+    /**
+     * Sets the <tt>If-None-Match</tt> header on the given service instance.
+     *
+     * @param <T> any service class
+     * @param service service class instance
+     * @param etag ETag value
+     * @return given service instance, with <tt>If-None-Match</tt> set
+     */
+    public <T> T ifNoneMatch(final T service, final EntityTag etag) {
+        return match(service, etag, true);
+    }
+
+    /**
+     * Creates an instance of the given service class, with <tt>If-None-Match</tt> header set.
+     *
+     * @param <T> any service class
+     * @param serviceClass service class reference
+     * @param etag ETag value
+     * @return given service instance, with <tt>If-None-Match</tt> set
+     */
+    public <T> T ifNoneMatch(final Class<T> serviceClass, final EntityTag etag) {
+        return match(getService(serviceClass), etag, true);
+    }
+
+    /**
+     * Checks whether self-registration is allowed by calling <tt>UserSelfService</tt>'s options.
+     *
+     * @return whether self-registration is allowed
+     * @see UserSelfService#getOptions()
+     */
     public boolean isSelfRegistrationAllowed() {
         return Boolean.valueOf(restClientFactory.createServiceInstance(UserSelfService.class, mediaType, null, null).
                 getOptions().getHeaderString(RESTHeaders.SELFREGISTRATION_ALLOWED));
     }
 
+    /**
+     * Checks whether Activiti workflow is enabled for users / roles, by calling <tt>WorkflowService</tt>'s options.
+     *
+     * @param attributableType user / role
+     * @return whether Activiti workflow is enabled for given attributable type
+     * @see WorkflowService#getOptions(org.apache.syncope.common.types.AttributableType)
+     */
     public boolean isActivitiEnabledFor(final AttributableType attributableType) {
         Response options = getService(WorkflowService.class).getOptions(attributableType);
 
@@ -109,4 +216,15 @@ public class SyncopeClient {
 
         return result;
     }
+
+    /**
+     * Fetches <tt>Etag</tt> header value from latest service run (if available).
+     *
+     * @param <T> any service class
+     * @param service service class instance
+     * @return <tt>Etag</tt> header value from latest service run (if available)
+     */
+    public <T> EntityTag getLatestEntityTag(final T service) {
+        return WebClient.client(service).getResponse().getEntityTag();
+    }
 }

Modified: syncope/trunk/client/src/main/java/org/apache/syncope/client/rest/RestClientFactoryBean.java
URL: http://svn.apache.org/viewvc/syncope/trunk/client/src/main/java/org/apache/syncope/client/rest/RestClientFactoryBean.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/client/src/main/java/org/apache/syncope/client/rest/RestClientFactoryBean.java (original)
+++ syncope/trunk/client/src/main/java/org/apache/syncope/client/rest/RestClientFactoryBean.java Mon Dec  2 16:47:18 2013
@@ -23,12 +23,33 @@ import org.apache.commons.lang3.StringUt
 import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
 import org.apache.cxf.jaxrs.client.WebClient;
 
+/**
+ * Provides shortcuts for creating JAX-RS service instances via CXF's <tt>JAXRSClientFactoryBean</tt>.
+ */
 public class RestClientFactoryBean extends JAXRSClientFactoryBean {
 
+    /**
+     * Creates an anonymous instance of the given service class, for the given content type.
+     *
+     * @param <T> any service class
+     * @param serviceClass service class reference
+     * @param mediaType XML or JSON are suppoorted
+     * @return anonymous service instance of the given reference class
+     */
     public <T> T createServiceInstance(final Class<T> serviceClass, final MediaType mediaType) {
         return createServiceInstance(serviceClass, mediaType, null, null);
     }
 
+    /**
+     * Creates an authenticated instance of the given service class, for the given content type.
+     *
+     * @param <T> any service class
+     * @param serviceClass service class reference
+     * @param mediaType XML or JSON are suppoorted
+     * @param username username for REST authentication
+     * @param password password for REST authentication
+     * @return anonymous service instance of the given reference class
+     */
     public <T> T createServiceInstance(
             final Class<T> serviceClass, final MediaType mediaType, final String username, final String password) {
 

Modified: syncope/trunk/common/src/main/java/org/apache/syncope/common/to/AbstractSysInfoTO.java
URL: http://svn.apache.org/viewvc/syncope/trunk/common/src/main/java/org/apache/syncope/common/to/AbstractSysInfoTO.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/common/src/main/java/org/apache/syncope/common/to/AbstractSysInfoTO.java (original)
+++ syncope/trunk/common/src/main/java/org/apache/syncope/common/to/AbstractSysInfoTO.java Mon Dec  2 16:47:18 2013
@@ -18,7 +18,9 @@
  */
 package org.apache.syncope.common.to;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import java.util.Date;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.AbstractBaseBean;
 
 /**
@@ -91,4 +93,14 @@ public class AbstractSysInfoTO extends A
     public void setLastChangeDate(final Date lastChangeDate) {
         this.lastChangeDate = lastChangeDate;
     }
+
+    @JsonIgnore
+    public String getETagValue() {
+        Date etagDate = getLastChangeDate() == null
+                ? getCreationDate() : getLastChangeDate();
+        return etagDate == null
+                ? StringUtils.EMPTY
+                : String.valueOf(etagDate.getTime());
+
+    }
 }

Modified: syncope/trunk/common/src/main/java/org/apache/syncope/common/types/Preference.java
URL: http://svn.apache.org/viewvc/syncope/trunk/common/src/main/java/org/apache/syncope/common/types/Preference.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/common/src/main/java/org/apache/syncope/common/types/Preference.java (original)
+++ syncope/trunk/common/src/main/java/org/apache/syncope/common/types/Preference.java Mon Dec  2 16:47:18 2013
@@ -36,15 +36,16 @@ public enum Preference {
         this.literal = literal;
     }
 
-    public String literal() {
+    @Override
+    public String toString() {
         return literal;
     }
 
-    public static Preference fromLiteral(final String literal) {
+    public static Preference fromString(final String literal) {
         Preference result = null;
 
         for (Preference preference : values()) {
-            if (preference.literal().equalsIgnoreCase(literal)) {
+            if (preference.toString().equalsIgnoreCase(literal)) {
                 result = preference;
             }
         }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/services/AbstractServiceImpl.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/services/AbstractServiceImpl.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/services/AbstractServiceImpl.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/services/AbstractServiceImpl.java Mon Dec  2 16:47:18 2013
@@ -48,7 +48,7 @@ abstract class AbstractServiceImpl {
      * or <tt>Preference.NONE</tt> if missing.
      */
     protected Preference getPreference() {
-        return Preference.fromLiteral(context.getHttpHeaders().getHeaderString(RESTHeaders.PREFER));
+        return Preference.fromString(context.getHttpHeaders().getHeaderString(RESTHeaders.PREFER));
     }
 
     /**
@@ -77,7 +77,7 @@ abstract class AbstractServiceImpl {
 
         }
         if (getPreference() == Preference.RETURN_CONTENT || getPreference() == Preference.RETURN_NO_CONTENT) {
-            builder = builder.header(RESTHeaders.PREFERENCE_APPLIED, getPreference().literal());
+            builder = builder.header(RESTHeaders.PREFERENCE_APPLIED, getPreference().toString());
         }
 
         return builder;
@@ -89,7 +89,7 @@ abstract class AbstractServiceImpl {
      * @param entity the entity just modified
      * @return response to successful modification request
      */
-    protected Response.ResponseBuilder updateResponse(final Object entity) {
+    protected Response.ResponseBuilder modificationResponse(final Object entity) {
         Response.ResponseBuilder builder;
         switch (getPreference()) {
             case RETURN_NO_CONTENT:
@@ -103,7 +103,7 @@ abstract class AbstractServiceImpl {
                 break;
         }
         if (getPreference() == Preference.RETURN_CONTENT || getPreference() == Preference.RETURN_NO_CONTENT) {
-            builder = builder.header(RESTHeaders.PREFERENCE_APPLIED, getPreference().literal());
+            builder = builder.header(RESTHeaders.PREFERENCE_APPLIED, getPreference().toString());
         }
 
         return builder;

Added: syncope/trunk/core/src/main/java/org/apache/syncope/core/services/AddETagFilter.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/services/AddETagFilter.java?rev=1547101&view=auto
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/services/AddETagFilter.java (added)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/services/AddETagFilter.java Mon Dec  2 16:47:18 2013
@@ -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.core.services;
+
+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.to.AbstractSysInfoTO;
+
+/**
+ * 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 AbstractSysInfoTO && resCtx.getEntityTag() == null) {
+            AbstractSysInfoTO sysInfo = (AbstractSysInfoTO) resCtx.getEntity();
+            String etagValue = sysInfo.getETagValue();
+            if (StringUtils.isNotBlank(etagValue)) {
+                resCtx.getHeaders().add(HttpHeaders.ETAG, new EntityTag(etagValue).toString());
+            }
+        }
+    }
+}

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/services/RoleServiceImpl.java Mon Dec  2 16:47:18 2013
@@ -21,7 +21,9 @@ package org.apache.syncope.core.services
 import java.util.List;
 
 import javax.ws.rs.ServiceUnavailableException;
+import javax.ws.rs.core.EntityTag;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
 
 import org.apache.syncope.common.mod.RoleMod;
 import org.apache.syncope.common.search.NodeCond;
@@ -59,8 +61,15 @@ public class RoleServiceImpl extends Abs
 
     @Override
     public Response delete(final Long roleId) {
-        RoleTO deleted = controller.delete(roleId);
-        return updateResponse(deleted).build();
+        RoleTO role = controller.read(roleId);
+
+        ResponseBuilder builder = context.getRequest().evaluatePreconditions(new EntityTag(role.getETagValue()));
+        if (builder == null) {
+            RoleTO deleted = controller.delete(roleId);
+            builder = modificationResponse(deleted);
+        }
+
+        return builder.build();
     }
 
     @Override
@@ -107,34 +116,48 @@ public class RoleServiceImpl extends Abs
 
     @Override
     public Response update(final Long roleId, final RoleMod roleMod) {
-        roleMod.setId(roleId);
-        RoleTO updated = controller.update(roleMod);
-        return updateResponse(updated).build();
+        RoleTO role = controller.read(roleId);
+
+        ResponseBuilder builder = context.getRequest().evaluatePreconditions(new EntityTag(role.getETagValue()));
+        if (builder == null) {
+            roleMod.setId(roleId);
+            RoleTO updated = controller.update(roleMod);
+            builder = modificationResponse(updated);
+        }
+
+        return builder.build();
     }
 
     @Override
     public Response associate(final Long roleId, final ResourceAssociationActionType type,
             final List<ResourceNameTO> resourceNames) {
 
-        RoleTO updated = null;
+        RoleTO role = controller.read(roleId);
 
-        switch (type) {
-            case UNLINK:
-                updated = controller.unlink(roleId, CollectionWrapper.unwrap(resourceNames));
-                break;
-
-            case UNASSIGN:
-                updated = controller.unassign(roleId, CollectionWrapper.unwrap(resourceNames));
-                break;
-
-            case DEPROVISION:
-                updated = controller.deprovision(roleId, CollectionWrapper.unwrap(resourceNames));
-                break;
+        ResponseBuilder builder = context.getRequest().evaluatePreconditions(new EntityTag(role.getETagValue()));
+        if (builder == null) {
+            RoleTO updated;
+
+            switch (type) {
+                case UNLINK:
+                    updated = controller.unlink(roleId, CollectionWrapper.unwrap(resourceNames));
+                    break;
+
+                case UNASSIGN:
+                    updated = controller.unassign(roleId, CollectionWrapper.unwrap(resourceNames));
+                    break;
+
+                case DEPROVISION:
+                    updated = controller.deprovision(roleId, CollectionWrapper.unwrap(resourceNames));
+                    break;
+
+                default:
+                    updated = controller.read(roleId);
+            }
 
-            default:
-                updated = controller.read(roleId);
+            builder = modificationResponse(updated);
         }
 
-        return updateResponse(updated).build();
+        return builder.build();
     }
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserSelfServiceImpl.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserSelfServiceImpl.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserSelfServiceImpl.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserSelfServiceImpl.java Mon Dec  2 16:47:18 2013
@@ -56,13 +56,13 @@ public class UserSelfServiceImpl extends
     public Response update(final Long userId, final UserMod userMod) {
         userMod.setId(userId);
         UserTO updated = controller.updateSelf(userMod);
-        return updateResponse(updated).build();
+        return modificationResponse(updated).build();
     }
 
     @Override
     public Response delete() {
         UserTO deleted = controller.deleteSelf();
-        return updateResponse(deleted).build();
+        return modificationResponse(deleted).build();
     }
 
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/services/UserServiceImpl.java Mon Dec  2 16:47:18 2013
@@ -19,8 +19,10 @@
 package org.apache.syncope.core.services;
 
 import java.util.List;
+import javax.ws.rs.core.EntityTag;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
 import org.apache.syncope.common.mod.StatusMod;
 import org.apache.syncope.common.mod.UserMod;
 import org.apache.syncope.common.search.NodeCond;
@@ -70,8 +72,15 @@ public class UserServiceImpl extends Abs
 
     @Override
     public Response delete(final Long userId) {
-        UserTO deleted = controller.delete(userId);
-        return updateResponse(deleted).build();
+        UserTO user = controller.read(userId);
+
+        ResponseBuilder builder = context.getRequest().evaluatePreconditions(new EntityTag(user.getETagValue()));
+        if (builder == null) {
+            UserTO deleted = controller.delete(userId);
+            builder = modificationResponse(deleted);
+        }
+
+        return builder.build();
     }
 
     @Override
@@ -108,16 +117,30 @@ public class UserServiceImpl extends Abs
 
     @Override
     public Response update(final Long userId, final UserMod userMod) {
-        userMod.setId(userId);
-        UserTO updated = controller.update(userMod);
-        return updateResponse(updated).build();
+        UserTO user = controller.read(userId);
+
+        ResponseBuilder builder = context.getRequest().evaluatePreconditions(new EntityTag(user.getETagValue()));
+        if (builder == null) {
+            userMod.setId(userId);
+            UserTO updated = controller.update(userMod);
+            builder = modificationResponse(updated);
+        }
+
+        return builder.build();
     }
 
     @Override
     public Response status(final Long userId, final StatusMod statusMod) {
-        statusMod.setId(userId);
-        UserTO updated = controller.status(statusMod);
-        return updateResponse(updated).build();
+        UserTO user = controller.read(userId);
+
+        ResponseBuilder builder = context.getRequest().evaluatePreconditions(new EntityTag(user.getETagValue()));
+        if (builder == null) {
+            statusMod.setId(userId);
+            UserTO updated = controller.status(statusMod);
+            builder = modificationResponse(updated);
+        }
+
+        return builder.build();
     }
 
     @Override
@@ -129,25 +152,32 @@ public class UserServiceImpl extends Abs
     public Response associate(final Long userId, final ResourceAssociationActionType type,
             final List<ResourceNameTO> resourceNames) {
 
-        UserTO updated = null;
+        UserTO user = controller.read(userId);
 
-        switch (type) {
-            case UNLINK:
-                updated = controller.unlink(userId, CollectionWrapper.unwrap(resourceNames));
-                break;
-
-            case UNASSIGN:
-                updated = controller.unassign(userId, CollectionWrapper.unwrap(resourceNames));
-                break;
-
-            case DEPROVISION:
-                updated = controller.deprovision(userId, CollectionWrapper.unwrap(resourceNames));
-                break;
+        ResponseBuilder builder = context.getRequest().evaluatePreconditions(new EntityTag(user.getETagValue()));
+        if (builder == null) {
+            UserTO updated;
+
+            switch (type) {
+                case UNLINK:
+                    updated = controller.unlink(userId, CollectionWrapper.unwrap(resourceNames));
+                    break;
+
+                case UNASSIGN:
+                    updated = controller.unassign(userId, CollectionWrapper.unwrap(resourceNames));
+                    break;
+
+                case DEPROVISION:
+                    updated = controller.deprovision(userId, CollectionWrapper.unwrap(resourceNames));
+                    break;
+
+                default:
+                    updated = controller.read(userId);
+            }
 
-            default:
-                updated = controller.read(userId);
+            builder = modificationResponse(updated);
         }
 
-        return updateResponse(updated).build();
+        return builder.build();
     }
 }

Modified: syncope/trunk/core/src/main/resources/restContext.xml
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/resources/restContext.xml?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/main/resources/restContext.xml (original)
+++ syncope/trunk/core/src/main/resources/restContext.xml Mon Dec  2 16:47:18 2013
@@ -93,6 +93,8 @@ under the License.
     <property name="addResourceAndMethodIds" value="true"/>
   </bean>
   
+  <bean id="addETagFilter" class="org.apache.syncope.core.services.AddETagFilter"/>
+  
   <jaxrs:server id="restContainer" address="/" staticSubresourceResolution="true">
     <jaxrs:serviceBeans>
       <ref bean="connectorServiceImpl"/>
@@ -119,6 +121,7 @@ under the License.
       <ref bean="jsonProvider"/>
       <ref bean="exceptionMapper"/>
       <ref bean="wadlGenerator"/>
+      <ref bean="addETagFilter"/>
     </jaxrs:providers>
     <jaxrs:extensionMappings>
       <entry key="json" value="application/json;charset=UTF-8"/>

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/AbstractTest.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/AbstractTest.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/AbstractTest.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/AbstractTest.java Mon Dec  2 16:47:18 2013
@@ -21,6 +21,7 @@ package org.apache.syncope.core.rest;
 import static org.junit.Assert.assertNotNull;
 
 import java.io.InputStream;
+import java.net.URI;
 import java.util.Properties;
 import java.util.UUID;
 
@@ -28,6 +29,7 @@ import javax.sql.DataSource;
 import javax.ws.rs.core.Response;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.jaxrs.client.WebClient;
 
 import org.apache.syncope.client.SyncopeClient;
 import org.apache.syncope.client.SyncopeClientFactoryBean;
@@ -52,7 +54,6 @@ import org.apache.syncope.common.service
 import org.apache.syncope.common.to.AbstractPolicyTO;
 import org.apache.syncope.common.to.AbstractSchemaTO;
 import org.apache.syncope.common.to.AttributeTO;
-import org.apache.syncope.common.to.ConnObjectTO;
 import org.apache.syncope.common.to.ResourceTO;
 import org.apache.syncope.common.to.RoleTO;
 import org.apache.syncope.common.to.UserTO;
@@ -215,10 +216,6 @@ public abstract class AbstractTest {
         schemaService = adminClient.getService(SchemaService.class);
     }
 
-//    protected ConnObjectTO readConnectorObject(final String resourceName, final Long userId, AttributableType type) {
-//        return resourceService.getConnectorObject(resourceName, type, userId);
-//    }
-
     protected static String getUUIDString() {
         return UUID.randomUUID().toString().substring(0, 8);
     }
@@ -260,7 +257,14 @@ public abstract class AbstractTest {
     protected UserTO deleteUser(final Long id) {
         return userService.delete(id).readEntity(UserTO.class);
     }
+    
+    public <T> T getObject(final URI location, final Class<?> serviceClass, final Class<T> resultClass) {
+        WebClient webClient = WebClient.fromClient(WebClient.client(adminClient.getService(serviceClass)));
+        webClient.accept(clientFactory.getContentType().getMediaType()).to(location.toASCIIString(), false);
 
+        return webClient.get(resultClass);
+    }
+    
     @SuppressWarnings("unchecked")
     protected <T extends AbstractSchemaTO> T createSchema(final AttributableType kind,
             final SchemaType type, final T schemaTO) {
@@ -273,7 +277,7 @@ public abstract class AbstractTest {
             }
         }
 
-        return (T) adminClient.getObject(response.getLocation(), SchemaService.class, schemaTO.getClass());
+        return (T) getObject(response.getLocation(), SchemaService.class, schemaTO.getClass());
     }
 
     protected RoleTO createRole(final RoleTO newRoleTO) {
@@ -284,7 +288,7 @@ public abstract class AbstractTest {
                 throw (RuntimeException) ex;
             }
         }
-        return adminClient.getObject(response.getLocation(), RoleService.class, RoleTO.class);
+        return getObject(response.getLocation(), RoleService.class, RoleTO.class);
     }
 
     protected RoleTO updateRole(final RoleMod roleMod) {
@@ -304,7 +308,7 @@ public abstract class AbstractTest {
                 throw (RuntimeException) ex;
             }
         }
-        return (T) adminClient.getObject(response.getLocation(), PolicyService.class, policy.getClass());
+        return (T) getObject(response.getLocation(), PolicyService.class, policy.getClass());
     }
 
     protected ResourceTO createResource(final ResourceTO resourceTO) {
@@ -315,6 +319,6 @@ public abstract class AbstractTest {
                 throw (RuntimeException) ex;
             }
         }
-        return adminClient.getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        return getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
     }
 }

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ConfigurationTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ConfigurationTestITCase.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ConfigurationTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ConfigurationTestITCase.java Mon Dec  2 16:47:18 2013
@@ -56,7 +56,7 @@ public class ConfigurationTestITCase ext
         Response response = configurationService.create(configurationTO);
         assertNotNull(response);
         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
-        ConfigurationTO newConfigurationTO = adminClient.getObject(response.getLocation(), ConfigurationService.class,
+        ConfigurationTO newConfigurationTO = getObject(response.getLocation(), ConfigurationService.class,
                 ConfigurationTO.class);
         assertEquals(configurationTO, newConfigurationTO);
     }
@@ -81,7 +81,7 @@ public class ConfigurationTestITCase ext
         Response response = configurationService.create(tokenLengthTO);
         assertNotNull(response);
         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
-        ConfigurationTO newConfigurationTO = adminClient.getObject(response.getLocation(), ConfigurationService.class,
+        ConfigurationTO newConfigurationTO = getObject(response.getLocation(), ConfigurationService.class,
                 ConfigurationTO.class);
         assertEquals(tokenLengthTO, newConfigurationTO);
     }

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ConnectorTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ConnectorTestITCase.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ConnectorTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ConnectorTestITCase.java Mon Dec  2 16:47:18 2013
@@ -145,7 +145,7 @@ public class ConnectorTestITCase extends
             throw (RuntimeException) clientFactory.getExceptionMapper().fromResponse(response);
         }
 
-        ConnInstanceTO actual = adminClient.getObject(
+        ConnInstanceTO actual = getObject(
                 response.getLocation(), ConnectorService.class, ConnInstanceTO.class);
         assertNotNull(actual);
 
@@ -287,7 +287,7 @@ public class ConnectorTestITCase extends
             throw (RuntimeException) clientFactory.getExceptionMapper().fromResponse(response);
         }
 
-        connInstanceTO = adminClient.getObject(response.getLocation(), ConnectorService.class, ConnInstanceTO.class);
+        connInstanceTO = getObject(response.getLocation(), ConnectorService.class, ConnInstanceTO.class);
         assertNotNull(connInstanceTO);
         assertTrue(connInstanceTO.getCapabilities().isEmpty());
 
@@ -301,7 +301,7 @@ public class ConnectorTestITCase extends
         // Check for connector instance update after resource creation.
         // ----------------------------------
         response = resourceService.create(resourceTO);
-        resourceTO = adminClient.getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        resourceTO = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
 
         assertNotNull(resourceTO);
 
@@ -620,7 +620,7 @@ public class ConnectorTestITCase extends
                 throw (RuntimeException) clientFactory.getExceptionMapper().fromResponse(response);
             }
 
-            connectorTO = adminClient.getObject(response.getLocation(), ConnectorService.class, ConnInstanceTO.class);
+            connectorTO = getObject(response.getLocation(), ConnectorService.class, ConnInstanceTO.class);
             assertNotNull(connectorTO);
             // ----------------------------------------
 
@@ -677,12 +677,12 @@ public class ConnectorTestITCase extends
         conn.setId(0);
         conn.setDisplayName("forBulk1");
 
-        bulkAction.getTargets().add(String.valueOf(adminClient.getObject(
+        bulkAction.getTargets().add(String.valueOf(getObject(
                 connectorService.create(conn).getLocation(), ConnectorService.class, ConnInstanceTO.class).getId()));
 
         conn.setDisplayName("forBulk2");
 
-        bulkAction.getTargets().add(String.valueOf(adminClient.getObject(
+        bulkAction.getTargets().add(String.valueOf(getObject(
                 connectorService.create(conn).getLocation(), ConnectorService.class, ConnInstanceTO.class).getId()));
 
         Iterator<String> iter = bulkAction.getTargets().iterator();

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/NotificationTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/NotificationTestITCase.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/NotificationTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/NotificationTestITCase.java Mon Dec  2 16:47:18 2013
@@ -94,7 +94,7 @@ public class NotificationTestITCase exte
         notificationTO.setRecipients(recipients);
 
         Response response = notificationService.create(notificationTO);
-        NotificationTO actual = adminClient.getObject(response.getLocation(), NotificationService.class,
+        NotificationTO actual = getObject(response.getLocation(), NotificationService.class,
                 NotificationTO.class);
 
         assertNotNull(actual);
@@ -135,7 +135,7 @@ public class NotificationTestITCase exte
         NotificationTO notification = buildNotificationTO();
         notification.setSelfAsRecipient(true);
         Response response = notificationService.create(notification);
-        notification = adminClient.getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
+        notification = getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
 
         notificationService.delete(notification.getId());
 
@@ -156,7 +156,7 @@ public class NotificationTestITCase exte
         SyncopeClientException exception = null;
         try {
             Response response = notificationService.create(notificationTO);
-            actual = adminClient.getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
+            actual = getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
         } catch (SyncopeClientException e) {
             assertEquals(ClientExceptionType.InvalidNotification, e.getType());
         }

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ReportTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ReportTestITCase.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ReportTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ReportTestITCase.java Mon Dec  2 16:47:18 2013
@@ -49,7 +49,7 @@ public class ReportTestITCase extends Ab
     public ReportTO createReport(final ReportTO report) {
         Response response = reportService.create(report);
         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
-        return adminClient.getObject(response.getLocation(), ReportService.class, ReportTO.class);
+        return getObject(response.getLocation(), ReportService.class, ReportTO.class);
     }
 
     @Test

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ResourceTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ResourceTestITCase.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ResourceTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/ResourceTestITCase.java Mon Dec  2 16:47:18 2013
@@ -101,7 +101,7 @@ public class ResourceTestITCase extends 
         ResourceTO resourceTO = buildResourceTO(resourceName);
 
         Response response = resourceService.create(resourceTO);
-        ResourceTO actual = adminClient.getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
         assertNotNull(actual);
 
         // check for existence
@@ -156,7 +156,7 @@ public class ResourceTestITCase extends 
         resourceTO.getConnConfProperties().addAll(connectorConfigurationProperties);
 
         Response response = resourceService.create(resourceTO);
-        ResourceTO actual = adminClient.getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
         assertNotNull(actual);
 
         // check the existence
@@ -192,7 +192,7 @@ public class ResourceTestITCase extends 
         resourceTO.setRmapping(rmapping);
 
         Response response = resourceService.create(resourceTO);
-        ResourceTO actual = adminClient.getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
 
         assertNotNull(actual);
         assertNotNull(actual.getUmapping());
@@ -280,7 +280,7 @@ public class ResourceTestITCase extends 
         resourceTO.setUmapping(mapping);
 
         Response response = resourceService.create(resourceTO);
-        ResourceTO actual = adminClient.getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
         assertNotNull(actual);
 
         // check the existence
@@ -379,7 +379,7 @@ public class ResourceTestITCase extends 
 
         ResourceTO resource = buildResourceTO(resourceName);
         Response response = resourceService.create(resource);
-        ResourceTO actual = adminClient.getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
         assertNotNull(actual);
 
         resourceService.delete(resourceName);

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java Mon Dec  2 16:47:18 2013
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.rest;
 
-import static org.apache.syncope.core.rest.AbstractTest.clientFactory;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -529,10 +528,10 @@ public class RoleTestITCase extends Abst
 
         Response response = noContentService.create(role);
         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
-        assertEquals(Preference.RETURN_NO_CONTENT.literal(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
         assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
 
-        role = noContentclient.getObject(response.getLocation(), RoleService.class, RoleTO.class);
+        role = getObject(response.getLocation(), RoleService.class, RoleTO.class);
         assertNotNull(role);
 
         RoleMod roleMod = new RoleMod();
@@ -540,12 +539,12 @@ public class RoleTestITCase extends Abst
 
         response = noContentService.update(role.getId(), roleMod);
         assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
-        assertEquals(Preference.RETURN_NO_CONTENT.literal(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
         assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
 
         response = noContentService.delete(role.getId());
         assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
-        assertEquals(Preference.RETURN_NO_CONTENT.literal(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
         assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
     }
 }

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java Mon Dec  2 16:47:18 2013
@@ -122,7 +122,7 @@ public class TaskTestITCase extends Abst
         task.setRoleTemplate(roleTemplate);
 
         Response response = taskService.create(task);
-        SyncTaskTO actual = adminClient.getObject(response.getLocation(), TaskService.class, SyncTaskTO.class);
+        SyncTaskTO actual = getObject(response.getLocation(), TaskService.class, SyncTaskTO.class);
         assertNotNull(actual);
 
         task = taskService.read(actual.getId());
@@ -571,7 +571,7 @@ public class TaskTestITCase extends Abst
         notification.setTemplate("optin");
 
         Response response = notificationService.create(notification);
-        notification = adminClient.getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
+        notification = getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
         assertNotNull(notification);
 
         // 2. create user
@@ -666,7 +666,7 @@ public class TaskTestITCase extends Abst
         task.setJobClassName(SyncJob.class.getName());
 
         Response response = taskService.create(task);
-        SchedTaskTO actual = adminClient.getObject(response.getLocation(), TaskService.class, SchedTaskTO.class);
+        SchedTaskTO actual = getObject(response.getLocation(), TaskService.class, SchedTaskTO.class);
         assertNotNull(actual);
         assertEquals("issueSYNCOPE144", actual.getName());
         assertEquals("issueSYNCOPE144 Description", actual.getDescription());
@@ -680,7 +680,7 @@ public class TaskTestITCase extends Abst
         task.setDescription("issueSYNCOPE144 Description_2");
 
         response = taskService.create(task);
-        actual = adminClient.getObject(response.getLocation(), TaskService.class, SchedTaskTO.class);
+        actual = getObject(response.getLocation(), TaskService.class, SchedTaskTO.class);
         assertNotNull(actual);
         assertEquals("issueSYNCOPE144_2", actual.getName());
         assertEquals("issueSYNCOPE144 Description_2", actual.getDescription());
@@ -818,7 +818,7 @@ public class TaskTestITCase extends Abst
         task.setPerformUpdate(true);
 
         Response response = taskService.create(task);
-        SyncTaskTO actual = adminClient.getObject(response.getLocation(), TaskService.class, SyncTaskTO.class);
+        SyncTaskTO actual = getObject(response.getLocation(), TaskService.class, SyncTaskTO.class);
         assertNotNull(actual);
 
         UserTO userTO = UserTestITCase.getUniqueSampleTO("s258_1@apache.org");

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserSelfTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserSelfTestITCase.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserSelfTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserSelfTestITCase.java Mon Dec  2 16:47:18 2013
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.rest;
 
-import static org.apache.syncope.core.rest.AbstractTest.clientFactory;
 import static org.apache.syncope.core.rest.UserTestITCase.getUniqueSampleTO;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -247,7 +246,7 @@ public class UserSelfTestITCase extends 
 
         Response response = noContentService.create(user);
         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
-        assertEquals(Preference.RETURN_NO_CONTENT.literal(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
         assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
     }
 }

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java Mon Dec  2 16:47:18 2013
@@ -18,10 +18,12 @@
  */
 package org.apache.syncope.core.rest;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.security.AccessControlException;
@@ -66,14 +68,13 @@ import org.apache.syncope.common.util.At
 import org.apache.syncope.common.util.CollectionWrapper;
 import org.apache.syncope.common.validation.SyncopeClientException;
 import org.apache.syncope.core.persistence.beans.user.SyncopeUser;
-import static org.apache.syncope.core.rest.AbstractTest.attributeTO;
-import static org.junit.Assert.assertEquals;
 import org.apache.syncope.core.workflow.ActivitiDetector;
 import org.identityconnectors.framework.common.objects.OperationalAttributes;
-import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.io.InputStream;
+import javax.ws.rs.core.EntityTag;
+import javax.xml.ws.WebServiceException;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.syncope.client.SyncopeClient;
@@ -179,7 +180,7 @@ public class UserTestITCase extends Abst
         } finally {
             for (PasswordPolicyTO policyTO : policies) {
                 Response response = policyService.create(policyTO);
-                PasswordPolicyTO cPolicyTO = adminClient.getObject(
+                PasswordPolicyTO cPolicyTO = getObject(
                         response.getLocation(), PolicyService.class, PasswordPolicyTO.class);
                 assertNotNull(cPolicyTO);
             }
@@ -275,7 +276,7 @@ public class UserTestITCase extends Abst
         resourceTO.setEnforceMandatoryCondition(true);
 
         Response response = resourceService.create(resourceTO);
-        resourceTO = adminClient.getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+        resourceTO = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
         assertNotNull(resourceTO);
 
         UserTO userTO = getUniqueSampleTO("syncope222@apache.org");
@@ -1203,10 +1204,10 @@ public class UserTestITCase extends Abst
 
         Response response = noContentService.create(user);
         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
-        assertEquals(Preference.RETURN_NO_CONTENT.literal(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
         assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
 
-        user = noContentclient.getObject(response.getLocation(), UserService.class, UserTO.class);
+        user = getObject(response.getLocation(), UserService.class, UserTO.class);
         assertNotNull(user);
 
         UserMod userMod = new UserMod();
@@ -1214,12 +1215,12 @@ public class UserTestITCase extends Abst
 
         response = noContentService.update(user.getId(), userMod);
         assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
-        assertEquals(Preference.RETURN_NO_CONTENT.literal(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
         assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
 
         response = noContentService.delete(user.getId());
         assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
-        assertEquals(Preference.RETURN_NO_CONTENT.literal(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
+        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
         assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
     }
 
@@ -2011,4 +2012,35 @@ public class UserTestITCase extends Abst
         assertTrue(userTO.getPropagationStatusTOs().get(0).getFailureReason().
                 startsWith("Not attempted because there are mandatory attributes without value(s): [__PASSWORD__]"));
     }
+
+    @Test
+    public void ifMatch() {
+        UserTO userTO = userService.create(getUniqueSampleTO("ifmatch@syncope.apache.org")).readEntity(UserTO.class);
+        assertNotNull(userTO);
+        assertNotNull(userTO.getId());
+
+        EntityTag etag = adminClient.getLatestEntityTag(userService);
+        assertNotNull(etag);
+        assertTrue(StringUtils.isNotBlank(etag.getValue()));
+
+        UserMod userMod = new UserMod();
+        userMod.setId(userTO.getId());
+        userMod.setUsername(userTO.getUsername() + "XX");
+        userTO = userService.update(userMod.getId(), userMod).readEntity(UserTO.class);
+        assertTrue(userTO.getUsername().endsWith("XX"));
+        EntityTag etag1 = adminClient.getLatestEntityTag(userService);
+        assertFalse(etag.getValue().equals(etag1.getValue()));
+
+        UserService ifMatchService = adminClient.ifMatch(UserService.class, etag);
+        userMod.setUsername(userTO.getUsername() + "YY");
+        try {
+            ifMatchService.update(userMod.getId(), userMod);
+            fail();
+        } catch (WebServiceException e) {
+            assertTrue(e.getMessage().endsWith(Response.Status.PRECONDITION_FAILED.name()));
+        }
+
+        userTO = userService.read(userTO.getId());
+        assertTrue(userTO.getUsername().endsWith("XX"));
+    }
 }

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java?rev=1547101&r1=1547100&r2=1547101&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java Mon Dec  2 16:47:18 2013
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.core.rest;
 
-import static org.apache.syncope.core.rest.AbstractTest.RESOURCE_NAME_DBVIRATTR;
-import static org.apache.syncope.core.rest.AbstractTest.attributeMod;
 import static org.apache.syncope.core.rest.UserTestITCase.getUniqueSampleTO;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;