Author: fmartelli
Date: Wed Feb 13 10:26:16 2013
New Revision: 1445543

URL: http://svn.apache.org/r1445543
Log:
SYNCOPE-154 added virtual attribute cache on the branch 1_0_X

Added:
    
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCache.java
   (with props)
    
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheKey.java
   (with props)
    
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheValue.java
   (with props)
Modified:
    
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationManager.java
    
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/ConnObjectUtil.java
    
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/SchemaMappingUtil.java
    syncope/branches/1_0_X/core/src/main/resources/syncopeContext.xml
    
syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java

Modified: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationManager.java
URL: 
http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationManager.java?rev=1445543&r1=1445542&r2=1445543&view=diff
==============================================================================
--- 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationManager.java
 (original)
+++ 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/PropagationManager.java
 Wed Feb 13 10:26:16 2013
@@ -50,6 +50,7 @@ import org.apache.syncope.core.rest.data
 import org.apache.syncope.core.util.AttributableUtil;
 import org.apache.syncope.core.util.JexlUtil;
 import org.apache.syncope.core.util.SchemaMappingUtil;
+import org.apache.syncope.core.util.VirAttrCache;
 import org.apache.syncope.core.workflow.WorkflowResult;
 import org.apache.syncope.types.AttributableType;
 import org.apache.syncope.types.IntMappingType;
@@ -122,6 +123,12 @@ public class PropagationManager {
     private TaskDAO taskDAO;
 
     /**
+     * Virtual attribute cache.
+     */
+    @Autowired
+    private VirAttrCache virAttrCache;
+
+    /**
      * JEXL engine for evaluating connector's account link.
      */
     @Autowired
@@ -388,6 +395,10 @@ public class PropagationManager {
                 schemaType = schema == null ? SchemaType.String : 
schema.getType();
                 break;
 
+            case UserVirtualSchema:
+                LOG.error("AAAAAAAAAAAAAAAAAAA Expire entry cache {}-{}", 
user.getId(), mapping.getIntAttrName());
+                virAttrCache.expire(user.getId(), mapping.getIntAttrName());
+            // no break ....
             default:
                 schemaType = SchemaType.String;
         }

Modified: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/ConnObjectUtil.java
URL: 
http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/ConnObjectUtil.java?rev=1445543&r1=1445542&r2=1445543&view=diff
==============================================================================
--- 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/ConnObjectUtil.java
 (original)
+++ 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/ConnObjectUtil.java
 Wed Feb 13 10:26:16 2013
@@ -18,10 +18,10 @@
  */
 package org.apache.syncope.core.util;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javassist.NotFoundException;
@@ -45,6 +45,7 @@ import org.apache.syncope.core.persisten
 import org.apache.syncope.core.propagation.ConnectorFacadeProxy;
 import org.apache.syncope.core.rest.controller.UnauthorizedRoleException;
 import org.apache.syncope.core.rest.data.UserDataBinder;
+import org.apache.syncope.types.IntMappingType;
 import org.identityconnectors.common.security.GuardedByteArray;
 import org.identityconnectors.common.security.GuardedString;
 import org.identityconnectors.framework.common.objects.Attribute;
@@ -80,6 +81,12 @@ public class ConnObjectUtil {
     private UserDataBinder userDataBinder;
 
     /**
+     * Virtual attribute cache.
+     */
+    @Autowired
+    private VirAttrCache virAttrCache;
+
+    /**
      * Build an UserTO out of connector object attributes and schema mapping.
      *
      * @param obj connector object
@@ -303,76 +310,96 @@ public class ConnObjectUtil {
         final ConfigurableApplicationContext context = 
ApplicationContextProvider.getApplicationContext();
         final ConnInstanceLoader connInstanceLoader = 
context.getBean(ConnInstanceLoader.class);
 
-        final Map<SchemaMappingUtil.SchemaMappingsWrapper, ConnectorObject> 
remoteObjects =
-                new HashMap<SchemaMappingUtil.SchemaMappingsWrapper, 
ConnectorObject>();
+        final IntMappingType type = owner instanceof SyncopeUser
+                ? IntMappingType.UserVirtualSchema : 
IntMappingType.RoleVirtualSchema;
 
-        for (ExternalResource resource : owner.getResources()) {
-            LOG.debug("Retrieve remote object from '{}'", resource.getName());
-            try {
-                final ConnectorFacadeProxy connector = 
connInstanceLoader.getConnector(resource);
+        final Map<String, ConnectorObject> externalResources = new 
HashMap<String, ConnectorObject>();
 
-                final SchemaMappingUtil.SchemaMappingsWrapper mappings = new 
SchemaMappingUtil.SchemaMappingsWrapper(
-                        resource.getMappings());
+        // -----------------------
+        // Retrieve virtual attribute values
+        // -----------------------
+        for (AbstractVirAttr virAttr : owner.getVirtualAttributes()) {
+            final String schemaName = virAttr.getVirtualSchema().getName();
+            final List<String> values = virAttrCache.get(owner.getId(), 
schemaName);
 
-                final String accountId = 
SchemaMappingUtil.getAccountIdValue(owner, mappings.getAccountIdMapping());
+            LOG.debug("Retrieve values for virtual attribute {}", schemaName);
 
-                LOG.debug("Search for object with accountId '{}'", accountId);
+            if (values == null) {
+                // non cached ...
+                LOG.debug("Need one or more remote connections");
+                for (ExternalResource resource : getTargetResource(virAttr, 
type)) {
+                    LOG.debug("Seach values into {}", resource.getName());
+                    try {
+                        final ConnectorObject connectorObject;
+
+                        if (externalResources.containsKey(resource.getName())) 
{
+                            connectorObject = 
externalResources.get(resource.getName());
+                        } else {
+                            LOG.debug("Perform connection to {}", 
resource.getName());
+                            final String accountId = 
SchemaMappingUtil.getAccountIdValue(
+                                    owner, 
SchemaMappingUtil.getAccountIdMapping(resource.getMappings()));
 
-                if (StringUtils.isNotBlank(accountId)) {
-                    // Retrieve attributes to get
-                    final Set<String> extAttrNames = new HashSet<String>();
+                            if (StringUtils.isBlank(accountId)) {
+                                throw new IllegalArgumentException("No 
AccountId found for " + resource.getName());
+                            }
 
-                    for (Collection<SchemaMapping> virAttrMappings : 
mappings.getuVirMappings().values()) {
-                        for (SchemaMapping virAttrMapping : virAttrMappings) {
-                            
extAttrNames.add(SchemaMappingUtil.getExtAttrName(virAttrMapping));
-                        }
-                    }
+                            final Set<String> extAttrNames =
+                                    
SchemaMappingUtil.getExtAttrNames(resource.getMappings(), type);
 
-                    // Search for remote object
-                    if (extAttrNames != null) {
-                        final OperationOptionsBuilder oob = new 
OperationOptionsBuilder();
-                        oob.setAttributesToGet(extAttrNames);
+                            LOG.debug("External attribute ({}) names to get 
'{}'", type, extAttrNames);
 
-                        final ConnectorObject connectorObject = 
connector.getObject(ObjectClass.ACCOUNT, new Uid(
-                                accountId), oob.build());
+                            final OperationOptionsBuilder oob = new 
OperationOptionsBuilder();
+                            oob.setAttributesToGet(extAttrNames);
 
-                        if (connectorObject != null) {
-                            remoteObjects.put(mappings, connectorObject);
+                            final ConnectorFacadeProxy connector = 
connInstanceLoader.getConnector(resource);
+                            connectorObject = 
connector.getObject(ObjectClass.ACCOUNT, new Uid(accountId), oob.build());
+                            externalResources.put(resource.getName(), 
connectorObject);
                         }
 
-                        LOG.debug("Retrieved remotye object {}", 
connectorObject);
-                    }
-                }
-            } catch (Exception e) {
-                LOG.error("Unable to retrieve virtual attribute values on 
'{}'", resource.getName(), e);
-            }
-        }
 
-        for (AbstractVirAttr virAttr : owner.getVirtualAttributes()) {
-            LOG.debug("Provide value for virtual attribute '{}'", 
virAttr.getVirtualSchema().getName());
+                        final Set<SchemaMapping> mappings = 
resource.getMappings(schemaName, type);
 
-            for (SchemaMappingUtil.SchemaMappingsWrapper mappings : 
remoteObjects.keySet()) {
-                Collection<SchemaMapping> virAttrMappings = 
mappings.getuVirMappings().get(
-                        virAttr.getVirtualSchema().getName());
-
-                if (virAttrMappings != null) {
-                    for (SchemaMapping virAttrMapping : virAttrMappings) {
-                        String extAttrName = 
SchemaMappingUtil.getExtAttrName(virAttrMapping);
-                        Attribute extAttr = 
remoteObjects.get(mappings).getAttributeByName(extAttrName);
-
-                        if (extAttr != null && extAttr.getValue() != null && 
!extAttr.getValue().isEmpty()) {
-                            for (Object obj : extAttr.getValue()) {
-                                if (obj != null) {
-                                    virAttr.addValue(obj.toString());
+                        // the same virtual attribute could be mapped with one 
or more external attribute 
+                        for (SchemaMapping mapping : mappings) {
+                            final Attribute attribute =
+                                    
connectorObject.getAttributeByName(SchemaMappingUtil.getExtAttrName(mapping));
+
+                            if (attribute != null && attribute.getValue() != 
null) {
+                                for (Object obj : attribute.getValue()) {
+                                    if (obj != null) {
+                                        virAttr.addValue(obj.toString());
+                                    }
                                 }
                             }
                         }
+
+                        LOG.debug("Retrieved values {}", virAttr.getValues());
+                    } catch (Exception e) {
+                        LOG.error("Error reading connector object from {}", 
resource.getName(), e);
                     }
                 }
+
+                virAttrCache.put(owner.getId(), schemaName, 
virAttr.getValues());
+            } else {
+                // cached ...
+                LOG.debug("Values found in cache {}", values);
+                virAttr.setValues(values);
+            }
+        }
+        // -----------------------
+    }
+
+    private Set<ExternalResource> getTargetResource(final AbstractVirAttr 
attr, final IntMappingType type) {
+
+        final Set<ExternalResource> resources = new 
HashSet<ExternalResource>();
+
+        for (ExternalResource res : attr.getOwner().getResources()) {
+            if (!SchemaMappingUtil.getMappings(res.getMappings(), 
attr.getVirtualSchema().getName(), type).isEmpty()) {
+                resources.add(res);
             }
         }
 
-        LOG.debug("Virtual attribute evaluation ended");
+        return resources;
     }
 
     private void fillFromTemplate(final AbstractAttributableTO attributableTO, 
final AbstractAttributableTO template) {

Modified: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/SchemaMappingUtil.java
URL: 
http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/SchemaMappingUtil.java?rev=1445543&r1=1445542&r2=1445543&view=diff
==============================================================================
--- 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/SchemaMappingUtil.java
 (original)
+++ 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/SchemaMappingUtil.java
 Wed Feb 13 10:26:16 2013
@@ -57,6 +57,18 @@ public class SchemaMappingUtil {
      */
     private static final Logger LOG = 
LoggerFactory.getLogger(SchemaMappingUtil.class);
 
+    public static Set<String> getExtAttrNames(final Collection<SchemaMapping> 
mappings, final IntMappingType type) {
+        final Set<String> res = new HashSet<String>();
+
+        for (SchemaMapping mapping : mappings) {
+            if (mapping.getIntMappingType() == type) {
+                res.add(getExtAttrName(mapping));
+            }
+        }
+        
+        return res;
+    }
+
     public static String getExtAttrName(final SchemaMapping mapping) {
         final String name;
 

Added: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCache.java
URL: 
http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCache.java?rev=1445543&view=auto
==============================================================================
--- 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCache.java
 (added)
+++ 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCache.java
 Wed Feb 13 10:26:16 2013
@@ -0,0 +1,159 @@
+/*
+ * 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.util;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Virtual Attribute Value cache.
+ */
+public final class VirAttrCache {
+
+    /**
+     * Elapsed time in seconds.
+     */
+    private final int ttl;
+
+    /**
+     * Max cache size.
+     */
+    private final int maxCacheSize;
+
+    /**
+     * Clean period.
+     */
+    private final int cleanPeriod;
+
+    /**
+     * Cache entries.
+     */
+    private final Map<VirAttrCacheKey, VirAttrCacheValue> cache = new 
HashMap<VirAttrCacheKey, VirAttrCacheValue>();
+
+    public VirAttrCache(final int ttl, final int maxCacheSize, final int 
cleanPeriod) {
+        this.ttl = ttl;
+        this.maxCacheSize = maxCacheSize;
+        this.cleanPeriod = cleanPeriod;
+
+        Executors.newScheduledThreadPool(1).scheduleWithFixedDelay(
+                new Runnable() {
+
+                    @Override
+                    public void run() {
+                        synchronized (cache) {
+                            freeCacheSpace(false);
+                        }
+                    }
+                }, cleanPeriod, cleanPeriod, TimeUnit.MINUTES);
+    }
+
+    /**
+     * Cache virtual attribute values.
+     *
+     * @param userId user id.
+     * @param schemaName virtual attribute name.
+     * @param values virtual attribute values.
+     */
+    public void put(final Long userId, final String schemaName, final 
List<String> values) {
+        synchronized (cache) {
+            // this operations (retrieve cache space and put entry on) have to 
be thread safe.
+
+            if (cache.size() >= maxCacheSize) {
+                freeCacheSpace(true);
+            }
+
+            cache.put(new VirAttrCacheKey(userId, schemaName), new 
VirAttrCacheValue(values));
+        }
+    }
+
+    /**
+     * Retrieve cached value. Return null in case of virtual attribute not 
cached.
+     *
+     * @param userId user id.
+     * @param schemaName virtual attribute schema name.
+     * @return cached values or null in case of virtual attribute not found.
+     */
+    public List<String> get(final Long userId, final String schemaName) {
+        final VirAttrCacheValue value = cache.get(new VirAttrCacheKey(userId, 
schemaName));
+        return isValidEntry(value) ? value.getValues() : null;
+    }
+
+    /**
+     * Force entry expiring.
+     *
+     * @param userId user id.
+     * @param schemaName virtual attribute schema name.
+     */
+    public void expire(final Long userId, final String schemaName) {
+        final VirAttrCacheValue value = cache.get(new VirAttrCacheKey(userId, 
schemaName));
+        if (isValidEntry(value)) {
+            synchronized (cache) {
+                value.forceExpiring();
+            }
+        }
+    }
+
+    /**
+     * Remove expired entries if exist. If required, one entry at least (the 
latest recently used) will be taken off.
+     * This method is not thread safe: the caller have to take care to 
synchronize the call.
+     *
+     * @param forceEscape if TRUE the latest recently used entry at least will 
be taken off.
+     */
+    private void freeCacheSpace(final boolean forceEscape) {
+        final Set<VirAttrCacheKey> toBeRemoved = new 
HashSet<VirAttrCacheKey>();
+
+        Map.Entry<VirAttrCacheKey, VirAttrCacheValue> latest = null;
+
+        for (Map.Entry<VirAttrCacheKey, VirAttrCacheValue> entry : 
cache.entrySet()) {
+            if (isValidEntry(entry.getValue())) {
+                final Date date = entry.getValue().getLastAccessDate();
+                if (latest == null || 
latest.getValue().getLastAccessDate().after(date)) {
+                    latest = entry;
+                }
+            } else {
+                toBeRemoved.add(entry.getKey());
+            }
+        }
+
+        if (toBeRemoved.isEmpty() && forceEscape) {
+            // remove the oldest entry.
+            cache.remove(latest.getKey());
+        } else {
+            // remove expired entries.
+            cache.keySet().removeAll(toBeRemoved);
+        }
+    }
+
+    /**
+     * Cache entry is valid if and only if value exist and it is not expired.
+     *
+     * @param value cache entry value.
+     * @return TRUE if the value is valid; FALSE otherwise.
+     */
+    private boolean isValidEntry(final VirAttrCacheValue value) {
+        final Date expiringDate = new Date(value == null ? 0 : 
value.getCreationDate().getTime() + ttl * 1000);
+        return expiringDate.after(new Date());
+    }
+}

Propchange: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCache.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCache.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheKey.java
URL: 
http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheKey.java?rev=1445543&view=auto
==============================================================================
--- 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheKey.java
 (added)
+++ 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheKey.java
 Wed Feb 13 10:26:16 2013
@@ -0,0 +1,64 @@
+/*
+ * 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.util;
+
+/**
+ * Ccahe entry key.
+ */
+public class VirAttrCacheKey {
+
+    /**
+     * User ID.
+     */
+    private final transient Long userId;
+
+    /**
+     * Virtual attribute schema name.
+     */
+    private final transient String virAttrSchema;
+
+    public VirAttrCacheKey(final Long userId, final String virAttrSchema) {
+        this.userId = userId;
+        this.virAttrSchema = virAttrSchema;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public String getVirAttrSchema() {
+        return virAttrSchema;
+    }
+
+    @Override
+    public int hashCode() {
+        return 31 * (31
+                + (userId == null ? 0 : userId.hashCode()))
+                + virAttrSchema == null ? 0 : virAttrSchema.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return o != null && o instanceof VirAttrCacheKey
+                && ((userId == null && ((VirAttrCacheKey) o).getUserId() == 
null)
+                || userId.equals(((VirAttrCacheKey) o).getUserId()))
+                && ((virAttrSchema == null && ((VirAttrCacheKey) 
o).getVirAttrSchema() == null)
+                || virAttrSchema.equals(((VirAttrCacheKey) 
o).getVirAttrSchema()));
+    }
+}

Propchange: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheKey.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheKey.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheKey.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheValue.java
URL: 
http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheValue.java?rev=1445543&view=auto
==============================================================================
--- 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheValue.java
 (added)
+++ 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheValue.java
 Wed Feb 13 10:26:16 2013
@@ -0,0 +1,69 @@
+/*
+ * 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.util;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Cache entry value.
+ */
+public class VirAttrCacheValue {
+
+    /**
+     * Virtual attribute values.
+     */
+    private final List<String> values;
+
+    /**
+     * Entry creation date.
+     */
+    private Date creationDate;
+
+    /**
+     * Entry access date.
+     */
+    private Date lastAccessDate;
+
+    public VirAttrCacheValue(final List<String> values) {
+        this.values = values;
+        this.creationDate = new Date();
+        this.lastAccessDate = new Date();
+    }
+
+    public Date getCreationDate() {
+        return creationDate;
+    }
+
+    public void forceExpiring() {
+        creationDate = new Date(0);
+    }
+
+    public List<String> getValues() {
+        return values;
+    }
+
+    public Date getLastAccessDate() {
+        return lastAccessDate;
+    }
+
+    void setLastAccessDate(Date lastAccessDate) {
+        this.lastAccessDate = lastAccessDate;
+    }
+}

Propchange: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheValue.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheValue.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: 
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/util/VirAttrCacheValue.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: syncope/branches/1_0_X/core/src/main/resources/syncopeContext.xml
URL: 
http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/resources/syncopeContext.xml?rev=1445543&r1=1445542&r2=1445543&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/main/resources/syncopeContext.xml (original)
+++ syncope/branches/1_0_X/core/src/main/resources/syncopeContext.xml Wed Feb 
13 10:26:16 2013
@@ -84,5 +84,12 @@ under the License.
     <property name="lenient" value="true"/>
     <property name="silent" value="false"/>
   </bean>
+  
   <bean id="jexlUtil" class="org.apache.syncope.core.util.JexlUtil"/>
+  
+  <bean id="virAttrCache" class="org.apache.syncope.core.util.VirAttrCache" 
scope="singleton">
+    <constructor-arg value="60"/>
+    <constructor-arg value="5000"/>
+    <constructor-arg value="5"/>
+  </bean>
 </beans>

Modified: 
syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
URL: 
http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java?rev=1445543&r1=1445542&r2=1445543&view=diff
==============================================================================
--- 
syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
 (original)
+++ 
syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
 Wed Feb 13 10:26:16 2013
@@ -2216,4 +2216,69 @@ public class UserTestITCase extends Abst
         userTO.addResource("ws-target-resource-3");
         restTemplate.postForObject(BASE_URL + "user/create", userTO, 
UserTO.class);
     }
+
+    @Test
+    public void virAttrCache() {
+        UserTO userTO = getSampleTO("virattrca...@apache.org");
+        userTO.getVirtualAttributes().clear();
+
+        AttributeTO virAttrTO = new AttributeTO();
+        virAttrTO.setSchema("virtualdata");
+        virAttrTO.addValue("virattrcache");
+        userTO.addVirtualAttribute(virAttrTO);
+
+        userTO.getMemberships().clear();
+        userTO.getResources().clear();
+        userTO.addResource("resource-db-virattr");
+
+        // 1. create user
+        UserTO actual = restTemplate.postForObject(BASE_URL + "user/create", 
userTO, UserTO.class);
+        assertNotNull(actual);
+
+        // 2. check for virtual attribute value
+        actual = restTemplate.getForObject(BASE_URL + 
"user/read/{userId}.json", UserTO.class, actual.getId());
+        assertEquals("virattrcache", 
actual.getVirtualAttributeMap().get("virtualdata").getValues().get(0));
+
+        Exception exception = null;
+        try {
+            final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+            String value = jdbcTemplate.queryForObject(
+                    "SELECT USERNAME FROM testsync WHERE ID=?", String.class, 
actual.getId());
+            assertEquals("virattrcache", value);
+
+            jdbcTemplate.update("UPDATE testsync set USERNAME='virattrcache2' 
WHERE ID=?", userTO.getId());
+
+            value = jdbcTemplate.queryForObject(
+                    "SELECT USERNAME FROM testsync WHERE ID=?", String.class, 
userTO.getId());
+            assertEquals("virattrcache2", value);
+
+        } catch (EmptyResultDataAccessException e) {
+            exception = e;
+        }
+        assertNotNull(exception);
+
+        // 2. check for cached attribute value
+        actual = restTemplate.getForObject(BASE_URL + 
"user/read/{userId}.json", UserTO.class, actual.getId());
+        assertEquals("virattrcache", 
actual.getVirtualAttributeMap().get("virtualdata").getValues().get(0));
+
+        UserMod userMod = new UserMod();
+        userMod.setId(actual.getId());
+
+        AttributeMod virtualdata = new AttributeMod();
+        virtualdata.setSchema("virtualdata");
+        virtualdata.addValueToBeAdded("virtualupdated");
+
+        userMod.addVirtualAttributeToBeRemoved("virtualdata");
+        userMod.addVirtualAttributeToBeUpdated(virtualdata);
+
+        // 3. update virtual attribute
+        actual = restTemplate.postForObject(BASE_URL + "user/update", userMod, 
UserTO.class);
+        assertNotNull(actual);
+
+        // 4. check for virtual attribute value
+        actual = restTemplate.getForObject(BASE_URL + 
"user/read/{userId}.json", UserTO.class, actual.getId());
+        assertNotNull(actual);
+        assertEquals("virtualupdated", 
actual.getVirtualAttributeMap().get("virtualdata").getValues().get(0));
+    }
 }


Reply via email to