You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by fm...@apache.org on 2013/01/22 15:18:00 UTC
svn commit: r1436942 - in /syncope/branches/1_0_X:
build-tools/src/main/java/org/apache/syncope/buildtools/
build-tools/src/main/webapp/WEB-INF/
core/src/main/java/org/apache/syncope/core/init/
core/src/main/java/org/apache/syncope/core/propagation/ co...
Author: fmartelli
Date: Tue Jan 22 14:17:59 2013
New Revision: 1436942
URL: http://svn.apache.org/viewvc?rev=1436942&view=rev
Log:
SYNCOPE-279 - Provided connector request timeout on 1_0_X
Added:
syncope/branches/1_0_X/build-tools/src/main/java/org/apache/syncope/buildtools/ServiceTimeoutServlet.java (with props)
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/AsyncConnectorFacade.java (with props)
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/TimeoutException.java (with props)
Modified:
syncope/branches/1_0_X/build-tools/src/main/webapp/WEB-INF/web.xml
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/init/ConnInstanceLoader.java
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/ConnectorFacadeProxy.java
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/rest/controller/ConnInstanceController.java
syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java
syncope/branches/1_0_X/core/src/main/resources/content.xml
syncope/branches/1_0_X/core/src/main/resources/securityContext.xml
syncope/branches/1_0_X/core/src/main/resources/syncopeContext.xml
syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/init/ConnInstanceLoaderTest.java
syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
syncope/branches/1_0_X/core/src/test/resources/content.xml
Added: syncope/branches/1_0_X/build-tools/src/main/java/org/apache/syncope/buildtools/ServiceTimeoutServlet.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/build-tools/src/main/java/org/apache/syncope/buildtools/ServiceTimeoutServlet.java?rev=1436942&view=auto
==============================================================================
--- syncope/branches/1_0_X/build-tools/src/main/java/org/apache/syncope/buildtools/ServiceTimeoutServlet.java (added)
+++ syncope/branches/1_0_X/build-tools/src/main/java/org/apache/syncope/buildtools/ServiceTimeoutServlet.java Tue Jan 22 14:17:59 2013
@@ -0,0 +1,101 @@
+/*
+ * 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.buildtools;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Just used to verify a connector request timeout.
+ */
+public class ServiceTimeoutServlet extends HttpServlet {
+
+ /**
+ * Processes requests for both HTTP
+ * <code>GET</code> and
+ * <code>POST</code> methods.
+ *
+ * @param request servlet request
+ * @param response servlet response
+ * @throws ServletException if a servlet-specific error occurs
+ * @throws IOException if an I/O error occurs
+ */
+ protected void processRequest(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ response.setContentType("text/html;charset=UTF-8");
+
+ try {
+ Thread.sleep(60000);
+ } catch (InterruptedException ignore) {
+ // ignore
+ }
+
+ final PrintWriter out = response.getWriter();
+
+ try {
+ out.println("OK");
+ } finally {
+ out.close();
+ }
+ }
+
+ /**
+ * Handles the HTTP
+ * <code>GET</code> method.
+ *
+ * @param request servlet request
+ * @param response servlet response
+ * @throws ServletException if a servlet-specific error occurs
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ processRequest(request, response);
+ }
+
+ /**
+ * Handles the HTTP
+ * <code>POST</code> method.
+ *
+ * @param request servlet request
+ * @param response servlet response
+ * @throws ServletException if a servlet-specific error occurs
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ protected void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ processRequest(request, response);
+ }
+
+ /**
+ * Returns a short description of the servlet.
+ *
+ * @return a String containing servlet description
+ */
+ @Override
+ public String getServletInfo() {
+ return "Service Timeout";
+ }
+}
Propchange: syncope/branches/1_0_X/build-tools/src/main/java/org/apache/syncope/buildtools/ServiceTimeoutServlet.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: syncope/branches/1_0_X/build-tools/src/main/java/org/apache/syncope/buildtools/ServiceTimeoutServlet.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: syncope/branches/1_0_X/build-tools/src/main/java/org/apache/syncope/buildtools/ServiceTimeoutServlet.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: syncope/branches/1_0_X/build-tools/src/main/webapp/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/build-tools/src/main/webapp/WEB-INF/web.xml?rev=1436942&r1=1436941&r2=1436942&view=diff
==============================================================================
--- syncope/branches/1_0_X/build-tools/src/main/webapp/WEB-INF/web.xml (original)
+++ syncope/branches/1_0_X/build-tools/src/main/webapp/WEB-INF/web.xml Tue Jan 22 14:17:59 2013
@@ -44,8 +44,16 @@ under the License.
<servlet-name>ApacheDSRootDseServlet</servlet-name>
<servlet-class>org.apache.syncope.buildtools.ApacheDSRootDseServlet</servlet-class>
</servlet>
+ <servlet>
+ <servlet-name>ServiceTimeoutServlet</servlet-name>
+ <servlet-class>org.apache.syncope.buildtools.ServiceTimeoutServlet</servlet-class>
+ </servlet>
<servlet-mapping>
<servlet-name>ApacheDSRootDseServlet</servlet-name>
<url-pattern>/apacheDS</url-pattern>
</servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>ServiceTimeoutServlet</servlet-name>
+ <url-pattern>/services/*</url-pattern>
+ </servlet-mapping>
</web-app>
Modified: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/init/ConnInstanceLoader.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/init/ConnInstanceLoader.java?rev=1436942&r1=1436941&r2=1436942&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/init/ConnInstanceLoader.java (original)
+++ syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/init/ConnInstanceLoader.java Tue Jan 22 14:17:59 2013
@@ -110,7 +110,8 @@ public class ConnInstanceLoader {
connInstanceClone.setConfiguration(configuration);
- return new ConnectorFacadeProxy(connInstanceClone, connBundleManager);
+ return getBeanFactory().getBean(
+ "connectorFacadeProxy", ConnectorFacadeProxy.class, connInstanceClone, connBundleManager);
}
public void registerConnector(final ExternalResource resource)
@@ -150,6 +151,7 @@ public class ConnInstanceLoader {
}
}
- LOG.info("Done loading {} connectors.", getBeanFactory().getBeansOfType(ConnectorFacadeProxy.class).size());
+ LOG.info("Done loading {} connectors.",
+ getBeanFactory().getBeansOfType(ConnectorFacadeProxy.class, false, true).size());
}
}
Added: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/AsyncConnectorFacade.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/AsyncConnectorFacade.java?rev=1436942&view=auto
==============================================================================
--- syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/AsyncConnectorFacade.java (added)
+++ syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/AsyncConnectorFacade.java Tue Jan 22 14:17:59 2013
@@ -0,0 +1,223 @@
+/*
+ * 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.propagation;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Future;
+import org.identityconnectors.framework.api.ConnectorFacade;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeInfo;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.Name;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.ObjectClassInfo;
+import org.identityconnectors.framework.common.objects.OperationOptions;
+import org.identityconnectors.framework.common.objects.OperationalAttributes;
+import org.identityconnectors.framework.common.objects.ResultsHandler;
+import org.identityconnectors.framework.common.objects.Schema;
+import org.identityconnectors.framework.common.objects.SyncDeltaBuilder;
+import org.identityconnectors.framework.common.objects.SyncDeltaType;
+import org.identityconnectors.framework.common.objects.SyncResultsHandler;
+import org.identityconnectors.framework.common.objects.SyncToken;
+import org.identityconnectors.framework.common.objects.Uid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.AsyncResult;
+
+/**
+ * Intercept calls to ConnectorFacade's methods and check if the corresponding connector instance has been configured to
+ * allow every single operation: if not, simply do nothing.
+ */
+public class AsyncConnectorFacade {
+
+ /**
+ * Logger.
+ */
+ private static final Logger LOG = LoggerFactory.getLogger(AsyncConnectorFacade.class);
+
+ @Async
+ public Future<Uid> create(
+ final ConnectorFacade connector,
+ final ObjectClass objectClass,
+ final Set<Attribute> attrs,
+ final OperationOptions options) {
+
+ return new AsyncResult<Uid>(connector.create(objectClass, attrs, options));
+ }
+
+ @Async
+ public Future<Uid> update(
+ final ConnectorFacade connector,
+ final ObjectClass objectClass,
+ final Uid uid,
+ final Set<Attribute> attrs,
+ final OperationOptions options) {
+
+ return new AsyncResult<Uid>(connector.update(objectClass, uid, attrs, options));
+ }
+
+ @Async
+ public Future<Uid> delete(
+ final ConnectorFacade connector,
+ final ObjectClass objectClass,
+ final Uid uid,
+ final OperationOptions options) {
+
+ connector.delete(objectClass, uid, options);
+ return new AsyncResult<Uid>(uid);
+ }
+
+ @Async
+ public Future<SyncResultsHandler> sync(
+ final ConnectorFacade connector,
+ final SyncToken token,
+ final SyncResultsHandler handler,
+ final OperationOptions options) {
+
+ connector.sync(ObjectClass.ACCOUNT, token, handler, options);
+ return new AsyncResult<SyncResultsHandler>(handler);
+ }
+
+ @Async
+ public Future<SyncToken> getLatestSyncToken(
+ final ConnectorFacade connector) {
+
+ return new AsyncResult<SyncToken>(connector.getLatestSyncToken(ObjectClass.ACCOUNT));
+ }
+
+ @Async
+ public Future<ConnectorObject> getObject(
+ final ConnectorFacade connector,
+ final ObjectClass objectClass,
+ final Uid uid,
+ final OperationOptions options) {
+
+ return new AsyncResult<ConnectorObject>(connector.getObject(objectClass, uid, options));
+ }
+
+ @Async
+ public Future<SyncResultsHandler> getAllObjects(
+ final ConnectorFacade connector,
+ final ObjectClass objectClass,
+ final SyncResultsHandler handler,
+ final OperationOptions options) {
+
+ connector.search(objectClass, null, new ResultsHandler() {
+
+ @Override
+ public boolean handle(final ConnectorObject obj) {
+ final SyncDeltaBuilder bld = new SyncDeltaBuilder();
+ bld.setObject(obj);
+ bld.setUid(obj.getUid());
+ bld.setDeltaType(SyncDeltaType.CREATE_OR_UPDATE);
+ bld.setToken(new SyncToken(""));
+
+ return handler.handle(bld.build());
+ }
+ }, options);
+
+ return new AsyncResult<SyncResultsHandler>(handler);
+ }
+
+ @Async
+ public Future<Attribute> getObjectAttribute(
+ final ConnectorFacade connector,
+ final ObjectClass objectClass,
+ final Uid uid,
+ final OperationOptions options,
+ final String attributeName) {
+
+ Attribute attribute = null;
+
+ try {
+ final ConnectorObject object = connector.getObject(objectClass, uid, options);
+ attribute = object.getAttributeByName(attributeName);
+ } catch (NullPointerException e) {
+ // ignore exception
+ LOG.debug("Object for '{}' not found", uid.getUidValue());
+ }
+
+ return new AsyncResult<Attribute>(attribute);
+ }
+
+ @Async
+ public Future<Set<Attribute>> getObjectAttributes(
+ final ConnectorFacade connector,
+ final ObjectClass objectClass,
+ final Uid uid,
+ final OperationOptions options) {
+
+ final Set<Attribute> attributes = new HashSet<Attribute>();
+
+ try {
+ final ConnectorObject object = connector.getObject(objectClass, uid, options);
+
+ for (String attribute : options.getAttributesToGet()) {
+ attributes.add(object.getAttributeByName(attribute));
+ }
+ } catch (NullPointerException e) {
+ // ignore exception
+ LOG.debug("Object for '{}' not found", uid.getUidValue());
+ }
+
+ return new AsyncResult<Set<Attribute>>(attributes);
+ }
+
+ @Async
+ public Future<Set<String>> getSchema(
+ final ConnectorFacade connector,
+ final boolean showall) {
+ final Set<String> resourceSchemaNames = new HashSet<String>();
+
+ final Schema schema = connector.schema();
+
+ try {
+ for (ObjectClassInfo info : schema.getObjectClassInfo()) {
+ for (AttributeInfo attrInfo : info.getAttributeInfo()) {
+ if (showall
+ || (!Name.NAME.equals(attrInfo.getName())
+ && !OperationalAttributes.PASSWORD_NAME.equals(attrInfo.getName()) && !OperationalAttributes.ENABLE_NAME.
+ equals(attrInfo.getName()))) {
+
+ resourceSchemaNames.add(attrInfo.getName());
+ }
+ }
+ }
+ } catch (Exception e) {
+ // catch exception in order to manage unpredictable behaviors
+ LOG.debug("Unsupported operation {}", e);
+ }
+
+ return new AsyncResult<Set<String>>(resourceSchemaNames);
+ }
+
+ @Async
+ public Future<String> validate(final ConnectorFacade connector) {
+ connector.validate();
+ return new AsyncResult<String>("OK");
+ }
+
+ @Async
+ public Future<String> test(final ConnectorFacade connector) {
+ connector.test();
+ return new AsyncResult<String>("OK");
+ }
+}
Propchange: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/AsyncConnectorFacade.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/AsyncConnectorFacade.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/AsyncConnectorFacade.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/ConnectorFacadeProxy.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/ConnectorFacadeProxy.java?rev=1436942&r1=1436941&r2=1436942&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/ConnectorFacadeProxy.java (original)
+++ syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/ConnectorFacadeProxy.java Tue Jan 22 14:17:59 2013
@@ -24,10 +24,15 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
import javassist.NotFoundException;
import org.apache.syncope.core.persistence.beans.ConnInstance;
import org.apache.syncope.core.persistence.beans.ExternalResource;
import org.apache.syncope.core.persistence.beans.SchemaMapping;
+import org.apache.syncope.core.persistence.dao.ConfDAO;
import org.apache.syncope.core.persistence.dao.MissingConfKeyException;
import org.apache.syncope.core.util.ConnBundleManager;
import org.apache.syncope.core.util.SchemaMappingUtil;
@@ -44,23 +49,18 @@ import org.identityconnectors.framework.
import org.identityconnectors.framework.api.ConnectorInfo;
import org.identityconnectors.framework.api.ConnectorKey;
import org.identityconnectors.framework.common.objects.Attribute;
-import org.identityconnectors.framework.common.objects.AttributeInfo;
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.ObjectClass;
-import org.identityconnectors.framework.common.objects.ObjectClassInfo;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.OperationOptionsBuilder;
import org.identityconnectors.framework.common.objects.OperationalAttributes;
-import org.identityconnectors.framework.common.objects.ResultsHandler;
-import org.identityconnectors.framework.common.objects.Schema;
-import org.identityconnectors.framework.common.objects.SyncDeltaBuilder;
-import org.identityconnectors.framework.common.objects.SyncDeltaType;
import org.identityconnectors.framework.common.objects.SyncResultsHandler;
import org.identityconnectors.framework.common.objects.SyncToken;
import org.identityconnectors.framework.common.objects.Uid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -87,6 +87,23 @@ public class ConnectorFacadeProxy {
*/
private final ConnInstance activeConnInstance;
+ private int timeout;
+
+ @Autowired
+ private AsyncConnectorFacade asyncFacade;
+
+ @Autowired
+ private ConfDAO confDAO;
+
+ @Autowired
+ private void setTimeout() {
+ try {
+ timeout = Integer.parseInt(confDAO.find("connectorRequest.timeout", "60").getValue());
+ } catch (Throwable t) {
+ timeout = 60;
+ }
+ }
+
/**
* Use the passed connector instance to build a ConnectorFacade that will be used to make all wrapped calls.
*
@@ -187,7 +204,20 @@ public class ConnectorFacadeProxy {
propagationAttempted.add("create");
- result = connector.create(objectClass, attrs, options);
+ final Future<Uid> future = asyncFacade.create(connector, objectClass, attrs, options);
+ try {
+ result = future.get(timeout, TimeUnit.SECONDS);
+ } catch (java.util.concurrent.TimeoutException e) {
+ throw new TimeoutException("Request timeout");
+ } catch (Exception e) {
+ LOG.error("Connector request execution failure", e);
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new IllegalArgumentException(e.getCause());
+ }
+ }
+
} else {
LOG.info("Create was attempted, although the " + "connector only has these capabilities: {}. No action.",
activeConnInstance.getCapabilities());
@@ -218,7 +248,21 @@ public class ConnectorFacadeProxy {
propagationAttempted.add("update");
- result = connector.update(objectClass, uid, attrs, options);
+ final Future<Uid> future = asyncFacade.update(connector, objectClass, uid, attrs, options);
+
+ try {
+ result = future.get(timeout, TimeUnit.SECONDS);
+ } catch (java.util.concurrent.TimeoutException e) {
+ throw new TimeoutException("Request timeout");
+ } catch (Exception e) {
+ LOG.error("Connector request execution failure", e);
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new IllegalArgumentException(e.getCause());
+ }
+ }
+
} else {
LOG.info("Update for {} was attempted, although the "
+ "connector only has these capabilities: {}. No action.", uid.getUidValue(), activeConnInstance.
@@ -246,7 +290,20 @@ public class ConnectorFacadeProxy {
propagationAttempted.add("delete");
- connector.delete(objectClass, uid, options);
+ final Future<Uid> future = asyncFacade.delete(connector, objectClass, uid, options);
+
+ try {
+ future.get(timeout, TimeUnit.SECONDS);
+ } catch (java.util.concurrent.TimeoutException e) {
+ throw new TimeoutException("Request timeout");
+ } catch (Exception e) {
+ LOG.error("Connector request execution failure", e);
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new IllegalArgumentException(e.getCause());
+ }
+ }
} else {
LOG.info("Delete for {} was attempted, although the connector only has these capabilities: {}. No action.",
uid.getUidValue(), activeConnInstance.getCapabilities());
@@ -262,7 +319,21 @@ public class ConnectorFacadeProxy {
public void sync(final SyncToken token, final SyncResultsHandler handler, final OperationOptions options) {
if (activeConnInstance.getCapabilities().contains(ConnectorCapability.SYNC)) {
- connector.sync(ObjectClass.ACCOUNT, token, handler, options);
+ final Future<SyncResultsHandler> future =
+ asyncFacade.sync(connector, token, handler, options);
+
+ try {
+ // no timeout
+ future.get();
+ } catch (Exception e) {
+ LOG.error("Connector request execution failure", e);
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new IllegalArgumentException(e.getCause());
+ }
+ }
+
} else {
LOG.info("Sync was attempted, although the connector only has these capabilities: {}. No action.",
activeConnInstance.getCapabilities());
@@ -278,7 +349,20 @@ public class ConnectorFacadeProxy {
SyncToken result = null;
if (activeConnInstance.getCapabilities().contains(ConnectorCapability.SYNC)) {
- result = connector.getLatestSyncToken(ObjectClass.ACCOUNT);
+ final Future<SyncToken> future = asyncFacade.getLatestSyncToken(connector);
+
+ try {
+ result = future.get(timeout, TimeUnit.SECONDS);
+ } catch (java.util.concurrent.TimeoutException e) {
+ throw new TimeoutException("Request timeout");
+ } catch (Exception e) {
+ LOG.error("Connector request execution failure", e);
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new IllegalArgumentException(e.getCause());
+ }
+ }
} else {
LOG.info("getLatestSyncToken was attempted, although the "
+ "connector only has these capabilities: {}. No action.", activeConnInstance.getCapabilities());
@@ -314,11 +398,11 @@ public class ConnectorFacadeProxy {
public ConnectorObject getObject(final PropagationMode propagationMode, final PropagationOperation operationType,
final ObjectClass objectClass, final Uid uid, final OperationOptions options) {
- ConnectorObject result = null;
+ Future<ConnectorObject> future = null;
if (activeConnInstance.getCapabilities().contains(ConnectorCapability.SEARCH)) {
if (operationType == null) {
- result = connector.getObject(objectClass, uid, options);
+ future = asyncFacade.getObject(connector, objectClass, uid, options);
} else {
switch (operationType) {
case CREATE:
@@ -326,7 +410,7 @@ public class ConnectorFacadeProxy {
? activeConnInstance.getCapabilities().contains(ConnectorCapability.ONE_PHASE_CREATE)
: activeConnInstance.getCapabilities().contains(ConnectorCapability.TWO_PHASES_CREATE))) {
- result = connector.getObject(objectClass, uid, options);
+ future = asyncFacade.getObject(connector, objectClass, uid, options);
}
break;
case UPDATE:
@@ -334,11 +418,11 @@ public class ConnectorFacadeProxy {
? activeConnInstance.getCapabilities().contains(ConnectorCapability.ONE_PHASE_UPDATE)
: activeConnInstance.getCapabilities().contains(ConnectorCapability.TWO_PHASES_UPDATE))) {
- result = connector.getObject(objectClass, uid, options);
+ future = asyncFacade.getObject(connector, objectClass, uid, options);
}
break;
default:
- result = connector.getObject(objectClass, uid, options);
+ future = asyncFacade.getObject(connector, objectClass, uid, options);
}
}
} else {
@@ -346,7 +430,18 @@ public class ConnectorFacadeProxy {
activeConnInstance.getCapabilities());
}
- return result;
+ try {
+ return future == null ? null : future.get(timeout, TimeUnit.SECONDS);
+ } catch (java.util.concurrent.TimeoutException e) {
+ throw new TimeoutException("Request timeout");
+ } catch (Exception e) {
+ LOG.error("Connector request execution failure", e);
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new IllegalArgumentException(e.getCause());
+ }
+ }
}
/**
@@ -361,19 +456,19 @@ public class ConnectorFacadeProxy {
final OperationOptions options) {
if (activeConnInstance.getCapabilities().contains(ConnectorCapability.SEARCH)) {
- connector.search(objectClass, null, new ResultsHandler() {
-
- @Override
- public boolean handle(final ConnectorObject obj) {
- final SyncDeltaBuilder bld = new SyncDeltaBuilder();
- bld.setObject(obj);
- bld.setUid(obj.getUid());
- bld.setDeltaType(SyncDeltaType.CREATE_OR_UPDATE);
- bld.setToken(new SyncToken(""));
+ final Future<SyncResultsHandler> future = asyncFacade.getAllObjects(connector, objectClass, handler, options);
- return handler.handle(bld.build());
+ try {
+ // no timeout
+ future.get();
+ } catch (Exception e) {
+ LOG.error("Connector request execution failure", e);
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new IllegalArgumentException(e.getCause());
}
- }, options);
+ }
} else {
LOG.info("Search was attempted, although the connector only has these capabilities: {}. No action.",
@@ -396,12 +491,22 @@ public class ConnectorFacadeProxy {
Attribute attribute = null;
try {
- final ConnectorObject object = connector.getObject(objectClass, uid, options);
+ final Future<Attribute> future =
+ asyncFacade.getObjectAttribute(connector, objectClass, uid, options, attributeName);
- attribute = object.getAttributeByName(attributeName);
+ attribute = future.get(timeout, TimeUnit.SECONDS);
} catch (NullPointerException e) {
// ignore exception
LOG.debug("Object for '{}' not found", uid.getUidValue());
+ } catch (java.util.concurrent.TimeoutException e) {
+ throw new TimeoutException("Request timeout");
+ } catch (Exception e) {
+ LOG.error("Connector request execution failure", e);
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new IllegalArgumentException(e.getCause());
+ }
}
return attribute;
@@ -421,14 +526,20 @@ public class ConnectorFacadeProxy {
final Set<Attribute> attributes = new HashSet<Attribute>();
try {
- final ConnectorObject object = connector.getObject(objectClass, uid, options);
-
- for (String attribute : options.getAttributesToGet()) {
- attributes.add(object.getAttributeByName(attribute));
- }
+ final Future<Set<Attribute>> future = asyncFacade.getObjectAttributes(connector, objectClass, uid, options);
+ attributes.addAll(future.get(timeout, TimeUnit.SECONDS));
} catch (NullPointerException e) {
// ignore exception
LOG.debug("Object for '{}' not found", uid.getUidValue());
+ } catch (java.util.concurrent.TimeoutException e) {
+ throw new TimeoutException("Request timeout");
+ } catch (Exception e) {
+ LOG.error("Connector request execution failure", e);
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new IllegalArgumentException(e.getCause());
+ }
}
return attributes;
@@ -443,40 +554,59 @@ public class ConnectorFacadeProxy {
public Set<String> getSchema(final boolean showall) {
final Set<String> resourceSchemaNames = new HashSet<String>();
- final Schema schema = connector.schema();
+ final Future<Set<String>> future = asyncFacade.getSchema(connector, showall);
try {
- for (ObjectClassInfo info : schema.getObjectClassInfo()) {
- for (AttributeInfo attrInfo : info.getAttributeInfo()) {
- if (showall
- || (!Name.NAME.equals(attrInfo.getName())
- && !OperationalAttributes.PASSWORD_NAME.equals(attrInfo.getName()) && !OperationalAttributes.ENABLE_NAME.
- equals(attrInfo.getName()))) {
-
- resourceSchemaNames.add(attrInfo.getName());
- }
- }
- }
+ resourceSchemaNames.addAll(future.get(timeout, TimeUnit.SECONDS));
+ } catch (java.util.concurrent.TimeoutException e) {
+ throw new TimeoutException("Request timeout");
} catch (Exception e) {
- // catch exception in order to manage unpredictable behaviors
- LOG.debug("Unsupported operation {}", e);
+ LOG.error("Connector request execution failure", e);
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new IllegalArgumentException(e.getCause());
+ }
}
return resourceSchemaNames;
+
}
/**
* Validate a connector instance.
*/
public void validate() {
- connector.validate();
+ try {
+ asyncFacade.test(connector).get(timeout, TimeUnit.SECONDS);
+ } catch (java.util.concurrent.TimeoutException e) {
+ throw new TimeoutException("Request timeout");
+ } catch (Exception e) {
+ LOG.error("Connector request execution failure", e);
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new IllegalArgumentException(e.getCause());
+ }
+ }
}
/**
* Check connection to resource.
*/
public void test() {
- connector.test();
+ try {
+ asyncFacade.test(connector).get(timeout, TimeUnit.SECONDS);
+ } catch (java.util.concurrent.TimeoutException e) {
+ throw new TimeoutException("Request timeout");
+ } catch (Exception e) {
+ LOG.error("Connector request execution failure", e);
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new IllegalArgumentException(e.getCause());
+ }
+ }
}
@Override
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=1436942&r1=1436941&r2=1436942&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 Tue Jan 22 14:17:59 2013
@@ -889,6 +889,9 @@ public class PropagationManager {
? task.getAccountId()
: task.getOldAccountId()), connector.getOperationOptions(task.getResource()));
+ } catch (TimeoutException toe) {
+ LOG.debug("Request timeout", toe);
+ throw toe;
} catch (RuntimeException ignore) {
LOG.debug("Resolving username", ignore);
return null;
Added: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/TimeoutException.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/TimeoutException.java?rev=1436942&view=auto
==============================================================================
--- syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/TimeoutException.java (added)
+++ syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/TimeoutException.java Tue Jan 22 14:17:59 2013
@@ -0,0 +1,43 @@
+/*
+ * 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.propagation;
+
+/**
+ *
+ * @author fabio
+ */
+public class TimeoutException extends RuntimeException {
+
+ /**
+ * Creates a new instance of
+ * <code>TimeoutException</code> without detail message.
+ */
+ public TimeoutException() {
+ }
+
+ /**
+ * Constructs an instance of
+ * <code>TimeoutException</code> with the specified detail message.
+ *
+ * @param msg the detail message.
+ */
+ public TimeoutException(String msg) {
+ super(msg);
+ }
+}
Propchange: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/TimeoutException.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/TimeoutException.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/propagation/TimeoutException.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/rest/controller/ConnInstanceController.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/rest/controller/ConnInstanceController.java?rev=1436942&r1=1436941&r2=1436942&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/rest/controller/ConnInstanceController.java (original)
+++ syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/rest/controller/ConnInstanceController.java Tue Jan 22 14:17:59 2013
@@ -369,8 +369,8 @@ public class ConnInstanceController exte
public ModelAndView check(final HttpServletResponse response, @RequestBody final ConnInstanceTO connectorTO)
throws SyncopeClientCompositeErrorException, NotFoundException {
- final ConnectorFacadeProxy connector = new ConnectorFacadeProxy(binder.getConnInstance(connectorTO),
- bundleManager);
+ final ConnectorFacadeProxy connector =
+ connLoader.createConnectorBean(binder.getConnInstance(connectorTO), connectorTO.getConfiguration());
boolean result;
try {
Modified: syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java?rev=1436942&r1=1436941&r2=1436942&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java (original)
+++ syncope/branches/1_0_X/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java Tue Jan 22 14:17:59 2013
@@ -23,21 +23,6 @@ import java.util.List;
import java.util.Set;
import javassist.NotFoundException;
import javax.servlet.http.HttpServletResponse;
-import org.identityconnectors.framework.common.objects.Attribute;
-import org.identityconnectors.framework.common.objects.AttributeUtil;
-import org.identityconnectors.framework.common.objects.ConnectorObject;
-import org.identityconnectors.framework.common.objects.Name;
-import org.identityconnectors.framework.common.objects.ObjectClass;
-import org.identityconnectors.framework.common.objects.Uid;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.stereotype.Controller;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
import org.apache.syncope.client.to.ConnObjectTO;
import org.apache.syncope.client.to.ResourceTO;
import org.apache.syncope.client.to.SchemaMappingTO;
@@ -59,6 +44,21 @@ import org.apache.syncope.types.AuditEle
import org.apache.syncope.types.AuditElements.Category;
import org.apache.syncope.types.AuditElements.ResourceSubCategory;
import org.apache.syncope.types.AuditElements.Result;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeUtil;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.Name;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.Uid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
@@ -261,7 +261,7 @@ public class ResourceController extends
final ConnInstance connInstance = binder.getConnInstance(resourceTO);
final ConnectorFacadeProxy connector =
- new ConnectorFacadeProxy(connInstance, bundleManager);
+ connLoader.createConnectorBean(connInstance, connInstance.getConfiguration());
boolean result;
try {
Modified: syncope/branches/1_0_X/core/src/main/resources/content.xml
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/resources/content.xml?rev=1436942&r1=1436941&r2=1436942&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/main/resources/content.xml (original)
+++ syncope/branches/1_0_X/core/src/main/resources/content.xml Tue Jan 22 14:17:59 2013
@@ -26,6 +26,7 @@ under the License.
<SyncopeConf confKey="connid.bundles.directory" confValue="${bundles.directory}"/>
<SyncopeConf confKey="password.cipher.algorithm" confValue="MD5"/>
<SyncopeConf confKey="createRequest.allowed" confValue="false"/>
+ <SyncopeConf confKey="connectorRequest.timeout" confValue="10"/>
<Policy DTYPE="SyncPolicy" id="1" description="Global Sync Policy" type="GLOBAL_SYNC"
specification="%3Corg.apache.syncope.types.SyncPolicySpec%2F%3E"/>
Modified: syncope/branches/1_0_X/core/src/main/resources/securityContext.xml
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/main/resources/securityContext.xml?rev=1436942&r1=1436941&r2=1436942&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/main/resources/securityContext.xml (original)
+++ syncope/branches/1_0_X/core/src/main/resources/securityContext.xml Tue Jan 22 14:17:59 2013
@@ -51,4 +51,13 @@ under the License.
<security:authentication-provider ref="syncopeAuthenticationProvider"/>
</security:authentication-manager>
+ <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
+ <property name="targetClass" value="org.springframework.security.core.context.SecurityContextHolder"/>
+ <property name="targetMethod" value="setStrategyName"/>
+ <property name="arguments">
+ <list>
+ <value>MODE_INHERITABLETHREADLOCAL</value>
+ </list>
+ </property>
+ </bean>
</beans>
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=1436942&r1=1436941&r2=1436942&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 Tue Jan 22 14:17:59 2013
@@ -20,11 +20,14 @@ under the License.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:task="http://www.springframework.org/schema/task"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context.xsd">
+ http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/task
+ http://www.springframework.org/schema/task/spring-task-3.1.xsd">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
@@ -69,7 +72,13 @@ under the License.
<bean id="propagationManager" class="org.apache.syncope.core.propagation.PropagationManager"/>
<bean id="notificationManager" class="org.apache.syncope.core.notification.NotificationManager"/>
<bean id="auditManager" class="org.apache.syncope.core.audit.AuditManager"/>
-
+
+ <bean id="connectorFacadeProxy" class="org.apache.syncope.core.propagation.ConnectorFacadeProxy" scope="prototype"/>
+ <bean id="asyncConnFacade" class="org.apache.syncope.core.propagation.AsyncConnectorFacade" scope="singleton"/>
+
+ <task:annotation-driven executor="connectorExecutor"/>
+ <task:executor id="connectorExecutor" pool-size="10"/>
+
<bean id="jexlEngine" class="org.apache.commons.jexl2.JexlEngine">
<property name="cache" value="512"/>
<property name="lenient" value="true"/>
Modified: syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/init/ConnInstanceLoaderTest.java
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/init/ConnInstanceLoaderTest.java?rev=1436942&r1=1436941&r2=1436942&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/init/ConnInstanceLoaderTest.java (original)
+++ syncope/branches/1_0_X/core/src/test/java/org/apache/syncope/core/init/ConnInstanceLoaderTest.java Tue Jan 22 14:17:59 2013
@@ -68,6 +68,6 @@ public class ConnInstanceLoaderTest exte
assertEquals(resourceDAO.findAll().size(),
ApplicationContextProvider.getApplicationContext().
- getBeanNamesForType(ConnectorFacadeProxy.class).length);
+ getBeanNamesForType(ConnectorFacadeProxy.class, false, true).length);
}
}
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=1436942&r1=1436941&r2=1436942&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 Tue Jan 22 14:17:59 2013
@@ -2207,4 +2207,12 @@ public class UserTestITCase extends Abst
userTO = restTemplate.postForObject(BASE_URL + "user/update", userMod, UserTO.class);
assertNotNull(userTO);
}
+
+ @Test(expected = SyncopeClientCompositeErrorException.class)
+ public void issueSYNCOPE279() {
+ UserTO userTO = getSampleTO("syncope260@apache.org");
+ userTO.getResources().clear();
+ userTO.addResource("ws-target-resource-3");
+ restTemplate.postForObject(BASE_URL + "user/create", userTO, UserTO.class);
+ }
}
Modified: syncope/branches/1_0_X/core/src/test/resources/content.xml
URL: http://svn.apache.org/viewvc/syncope/branches/1_0_X/core/src/test/resources/content.xml?rev=1436942&r1=1436941&r2=1436942&view=diff
==============================================================================
--- syncope/branches/1_0_X/core/src/test/resources/content.xml (original)
+++ syncope/branches/1_0_X/core/src/test/resources/content.xml Tue Jan 22 14:17:59 2013
@@ -26,6 +26,7 @@ under the License.
<SyncopeConf confKey="connid.bundles.directory" confValue="${bundles.directory}"/>
<SyncopeConf confKey="password.cipher.algorithm" confValue="MD5"/>
<SyncopeConf confKey="createRequest.allowed" confValue="true"/>
+ <SyncopeConf confKey="connectorRequest.timeout" confValue="10"/>
<!-- sample policies -->
<Policy DTYPE="SyncPolicy" id="1" description="sync policy 1" type="GLOBAL_SYNC" specification="%3Corg.apache.syncope.types.SyncPolicySpec%2F%3E"/>
@@ -335,6 +336,9 @@ under the License.
propagationPriority="0" propagationPrimary="1" createTraceLevel="ALL" deleteTraceLevel="ALL" updateTraceLevel="ALL" syncTraceLevel="ALL"/>
<ExternalResource name="ws-target-resource-2" connector_id="100" forceMandatoryConstraint="1" propagationMode="TWO_PHASES"
propagationPriority="0" propagationPrimary="1" createTraceLevel="FAILURES" deleteTraceLevel="NONE" updateTraceLevel="ALL" syncTraceLevel="ALL"/>
+ <ExternalResource name="ws-target-resource-3" connector_id="102" forceMandatoryConstraint="0" propagationMode="TWO_PHASES"
+ propagationPriority="0" propagationPrimary="1" createTraceLevel="FAILURES" deleteTraceLevel="NONE" updateTraceLevel="ALL" syncTraceLevel="ALL"
+ xmlConfiguration="%3Cset%3E%0A++%3Corg.apache.syncope.types.ConnConfProperty%3E%0A++++%3Cschema%3E%0A++++++%3Cname%3Eendpoint%3C/name%3E%0A++++++%3Ctype%3Ejava.lang.String%3C/type%3E%0A++++++%3Crequired%3Etrue%3C/required%3E%0A++++%3C/schema%3E%0A++++%3Cvalues%3E%0A++++++%3Cjava.lang.String%3Ehttp%3A//localhost%3A9080/syncope-build-tools/services/provisioning%3C/java.lang.String%3E%0A++++%3C/values%3E%0A++++%3Coverridable%3Efalse%3C/overridable%3E%0A++%3C/org.apache.syncope.types.ConnConfProperty%3E%0A%3C/set%3E"/>
<ExternalResource name="ws-target-resource-list-mappings-1" connector_id="100" forceMandatoryConstraint="0" propagationMode="TWO_PHASES"
propagationPriority="0" propagationPrimary="0" createTraceLevel="ALL" deleteTraceLevel="ALL" updateTraceLevel="ALL" syncTraceLevel="ALL"/>
<ExternalResource name="ws-target-resource-list-mappings-2" connector_id="100" forceMandatoryConstraint="1" propagationMode="TWO_PHASES"
@@ -597,6 +601,11 @@ under the License.
<SchemaMapping id="332" resource_name="resource-db-virattr" accountid="0" password="0"
extAttrName="USERNAME" intAttrName="virtualdata" intMappingType="UserVirtualSchema"
mandatoryCondition="false" />
+
+ <SchemaMapping id="333"
+ resource_name="ws-target-resource-3"
+ intAttrName="fullname" intMappingType="UserSchema" mandatoryCondition="true"
+ accountid="1" password="0"/>
<Task DTYPE="PropagationTask" id="1" propagationMode="TWO_PHASES" propagationOperation="UPDATE"
resource_name="ws-target-resource-2" syncopeUser_id="1"