Repository: syncope Updated Branches: refs/heads/master 550145f8e -> 891122c2f
SYNCOPE-910 - Introduce new Camel propagation component - part II Project: http://git-wip-us.apache.org/repos/asf/syncope/repo Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/891122c2 Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/891122c2 Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/891122c2 Branch: refs/heads/master Commit: 891122c2f3d4ec814eff8df3154ba425ce172335 Parents: 550145f Author: Colm O hEigeartaigh <cohei...@apache.org> Authored: Fri Jul 29 14:38:53 2016 +0100 Committer: Colm O hEigeartaigh <cohei...@apache.org> Committed: Fri Jul 29 14:39:28 2016 +0100 ---------------------------------------------------------------------- .../camel/component/PropagateComponent.java | 5 + .../camel/component/PropagateEndpoint.java | 30 +++++- .../camel/component/PropagateType.java | 2 +- .../processor/GroupCreateInPullProcessor.java | 73 ------------- .../processor/UserConfirmPwdResetProcessor.java | 52 ---------- .../processor/UserInternalSuspendProcessor.java | 62 ----------- .../processor/UserSetStatusInPullProcessor.java | 73 ------------- .../UserStatusPropagationProcessor.java | 72 ------------- .../processor/UserUpdateInPullProcessor.java | 60 ----------- .../camel/producer/AbstractProducer.java | 10 ++ .../producer/ConfirmPasswordResetProducer.java | 48 +++++++++ .../camel/producer/CreateProducer.java | 40 +++++-- .../camel/producer/StatusProducer.java | 103 +++++++++++++++++++ .../camel/producer/SuspendProducer.java | 57 ++++++++++ .../camel/producer/UpdateProducer.java | 13 ++- .../src/main/resources/groupRoutes.xml | 4 +- .../src/main/resources/userRoutes.xml | 10 +- 17 files changed, 302 insertions(+), 412 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java index a904b47..c0728cd 100644 --- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java +++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java @@ -26,6 +26,7 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.provisioning.api.data.GroupDataBinder; import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; +import org.apache.syncope.core.workflow.api.UserWorkflowAdapter; import org.springframework.beans.factory.annotation.Autowired; public class PropagateComponent extends UriEndpointComponent { @@ -48,6 +49,9 @@ public class PropagateComponent extends UriEndpointComponent { @Autowired protected GroupDataBinder groupDataBinder; + @Autowired + protected UserWorkflowAdapter uwfAdapter; + public PropagateComponent() { super(PropagateEndpoint.class); } @@ -63,6 +67,7 @@ public class PropagateComponent extends UriEndpointComponent { endpoint.setGroupDAO(groupDAO); endpoint.setAnyObjectDAO(anyObjectDAO); endpoint.setGroupDataBinder(groupDataBinder); + endpoint.setUwfAdapter(uwfAdapter); setProperties(endpoint, parameters); return endpoint; http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateEndpoint.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateEndpoint.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateEndpoint.java index 7dff3a9..e619f91 100644 --- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateEndpoint.java +++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateEndpoint.java @@ -33,11 +33,15 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; import org.apache.syncope.core.provisioning.camel.AnyType; import org.apache.syncope.core.provisioning.camel.producer.AbstractProducer; +import org.apache.syncope.core.provisioning.camel.producer.ConfirmPasswordResetProducer; import org.apache.syncope.core.provisioning.camel.producer.CreateProducer; import org.apache.syncope.core.provisioning.camel.producer.DeleteProducer; import org.apache.syncope.core.provisioning.camel.producer.DeprovisionProducer; import org.apache.syncope.core.provisioning.camel.producer.ProvisionProducer; +import org.apache.syncope.core.provisioning.camel.producer.StatusProducer; +import org.apache.syncope.core.provisioning.camel.producer.SuspendProducer; import org.apache.syncope.core.provisioning.camel.producer.UpdateProducer; +import org.apache.syncope.core.workflow.api.UserWorkflowAdapter; @UriEndpoint(scheme = "propagate", title = "propagate", syntax = "propagate:propagateType", producerOnly = true) public class PropagateEndpoint extends DefaultEndpoint { @@ -47,6 +51,9 @@ public class PropagateEndpoint extends DefaultEndpoint { @UriParam private AnyType anyType; + + @UriParam + private boolean pull; private PropagationManager propagationManager; @@ -59,6 +66,8 @@ public class PropagateEndpoint extends DefaultEndpoint { private AnyObjectDAO anyObjectDAO; private GroupDataBinder groupDataBinder; + + private UserWorkflowAdapter uwfAdapter; public PropagateEndpoint(final String endpointUri, final Component component) { super(endpointUri, component); @@ -77,11 +86,18 @@ public class PropagateEndpoint extends DefaultEndpoint { producer = new ProvisionProducer(this, anyType); } else if (PropagateType.deprovision == propagateType) { producer = new DeprovisionProducer(this, anyType, userDAO, groupDAO, anyObjectDAO); - } + } else if (PropagateType.status == propagateType) { + producer = new StatusProducer(this, anyType, userDAO, uwfAdapter); + } else if (PropagateType.suspend == propagateType) { + producer = new SuspendProducer(this, anyType); + } else if (PropagateType.confirmPasswordReset == propagateType) { + producer = new ConfirmPasswordResetProducer(this, anyType); + } if (producer != null) { producer.setPropagationManager(propagationManager); producer.setPropagationTaskExecutor(taskExecutor); + producer.setPull(pull); } return producer; } @@ -135,4 +151,16 @@ public class PropagateEndpoint extends DefaultEndpoint { public void setGroupDataBinder(final GroupDataBinder groupDataBinder) { this.groupDataBinder = groupDataBinder; } + + public boolean isPull() { + return pull; + } + + public void setPull(final boolean pull) { + this.pull = pull; + } + + public void setUwfAdapter(final UserWorkflowAdapter uwfAdapter) { + this.uwfAdapter = uwfAdapter; + } } http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateType.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateType.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateType.java index ca0ae1a..fd36908 100644 --- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateType.java +++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateType.java @@ -18,6 +18,6 @@ package org.apache.syncope.core.provisioning.camel.component; public enum PropagateType { - create, update, delete, provision, deprovision + create, update, delete, provision, deprovision, status, suspend, confirmPasswordReset } http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/GroupCreateInPullProcessor.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/GroupCreateInPullProcessor.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/GroupCreateInPullProcessor.java deleted file mode 100644 index 256bccd..0000000 --- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/GroupCreateInPullProcessor.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.apache.syncope.core.provisioning.camel.processor; - -/* - * 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. - */ -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.apache.camel.Exchange; -import org.apache.camel.Processor; -import org.springframework.beans.factory.annotation.Autowired; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.syncope.common.lib.to.AttrTO; -import org.apache.syncope.common.lib.to.GroupTO; -import org.apache.syncope.common.lib.types.AnyTypeKind; -import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; -import org.apache.syncope.core.provisioning.api.WorkflowResult; -import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; -import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter; -import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; -import org.springframework.stereotype.Component; - -@Component -public class GroupCreateInPullProcessor implements Processor { - - @Autowired - protected PropagationManager propagationManager; - - @Autowired - protected PropagationTaskExecutor taskExecutor; - - @Override - @SuppressWarnings("unchecked") - public void process(final Exchange exchange) { - WorkflowResult<String> created = (WorkflowResult<String>) exchange.getIn().getBody(); - - GroupTO groupTO = exchange.getProperty("any", GroupTO.class); - Map<String, String> groupOwnerMap = exchange.getProperty("groupOwnerMap", Map.class); - Set<String> excludedResources = exchange.getProperty("excludedResources", Set.class); - Boolean nullPriorityAsync = exchange.getProperty("nullPriorityAsync", Boolean.class); - - AttrTO groupOwner = groupTO.getPlainAttrMap().get(StringUtils.EMPTY); - if (groupOwner != null) { - groupOwnerMap.put(created.getResult(), groupOwner.getValues().iterator().next()); - } - - List<PropagationTask> tasks = propagationManager.getCreateTasks( - AnyTypeKind.GROUP, - created.getResult(), - created.getPropByRes(), - groupTO.getVirAttrs(), - excludedResources); - PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync); - - exchange.getOut().setBody(new ImmutablePair<>(created.getResult(), null)); - } -} http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserConfirmPwdResetProcessor.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserConfirmPwdResetProcessor.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserConfirmPwdResetProcessor.java deleted file mode 100644 index b86ee5f..0000000 --- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserConfirmPwdResetProcessor.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.provisioning.camel.processor; - -import java.util.List; -import org.apache.camel.Exchange; -import org.apache.camel.Processor; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.common.lib.patch.UserPatch; -import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; -import org.apache.syncope.core.provisioning.api.WorkflowResult; -import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; -import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class UserConfirmPwdResetProcessor implements Processor { - - @Autowired - protected PropagationManager propagationManager; - - @Autowired - protected PropagationTaskExecutor taskExecutor; - - @Override - public void process(final Exchange exchange) { - @SuppressWarnings("unchecked") - WorkflowResult<Pair<UserPatch, Boolean>> updated = - (WorkflowResult<Pair<UserPatch, Boolean>>) exchange.getIn().getBody(); - - List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(updated); - - taskExecutor.execute(tasks, false); - } -} http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserInternalSuspendProcessor.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserInternalSuspendProcessor.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserInternalSuspendProcessor.java deleted file mode 100644 index a349575..0000000 --- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserInternalSuspendProcessor.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.provisioning.camel.processor; - -import java.util.List; -import org.apache.camel.Exchange; -import org.apache.camel.Processor; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.common.lib.patch.UserPatch; -import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; -import org.apache.syncope.core.provisioning.api.WorkflowResult; -import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; -import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class UserInternalSuspendProcessor implements Processor { - - @Autowired - protected PropagationManager propagationManager; - - @Autowired - protected PropagationTaskExecutor taskExecutor; - - @Override - public void process(final Exchange exchange) { - @SuppressWarnings("unchecked") - Pair<WorkflowResult<String>, Boolean> updated = - (Pair<WorkflowResult<String>, Boolean>) exchange.getIn().getBody(); - - // propagate suspension if and only if it is required by policy - if (updated != null && updated.getValue()) { - UserPatch userPatch = new UserPatch(); - userPatch.setKey(updated.getKey().getResult()); - - List<PropagationTask> tasks = propagationManager.getUserUpdateTasks( - new WorkflowResult<Pair<UserPatch, Boolean>>( - new ImmutablePair<>(userPatch, Boolean.FALSE), - updated.getKey().getPropByRes(), updated.getKey().getPerformedTasks())); - taskExecutor.execute(tasks); - } - } - -} http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserSetStatusInPullProcessor.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserSetStatusInPullProcessor.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserSetStatusInPullProcessor.java deleted file mode 100644 index ce0eeb7..0000000 --- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserSetStatusInPullProcessor.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.provisioning.camel.processor; - -import java.util.Map; -import java.util.Map.Entry; - -import org.apache.camel.Processor; -import org.apache.camel.Exchange; -import org.apache.syncope.common.lib.patch.UserPatch; -import org.apache.syncope.core.persistence.api.dao.UserDAO; -import org.apache.syncope.core.persistence.api.entity.user.User; -import org.apache.syncope.core.provisioning.api.WorkflowResult; -import org.apache.syncope.core.workflow.api.UserWorkflowAdapter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class UserSetStatusInPullProcessor implements Processor { - - @Autowired - protected UserDAO userDAO; - - @Autowired - protected UserWorkflowAdapter uwfAdapter; - - @SuppressWarnings("unchecked") - @Override - public void process(final Exchange exchange) { - WorkflowResult<Map.Entry<UserPatch, Boolean>> updated = - (WorkflowResult<Entry<UserPatch, Boolean>>) exchange.getIn().getBody(); - - Boolean enabled = exchange.getProperty("enabled", Boolean.class); - String key = exchange.getProperty("key", String.class); - - if (enabled != null) { - User user = userDAO.find(key); - - WorkflowResult<String> enableUpdate = null; - if (user.isSuspended() == null) { - enableUpdate = uwfAdapter.activate(key, null); - } else if (enabled && user.isSuspended()) { - enableUpdate = uwfAdapter.reactivate(key); - } else if (!enabled && !user.isSuspended()) { - enableUpdate = uwfAdapter.suspend(key); - } - - if (enableUpdate != null) { - if (enableUpdate.getPropByRes() != null) { - updated.getPropByRes().merge(enableUpdate.getPropByRes()); - updated.getPropByRes().purge(); - } - updated.getPerformedTasks().addAll(enableUpdate.getPerformedTasks()); - } - } - } -} http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserStatusPropagationProcessor.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserStatusPropagationProcessor.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserStatusPropagationProcessor.java deleted file mode 100644 index f2ad3d8..0000000 --- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserStatusPropagationProcessor.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.provisioning.camel.processor; - -import java.util.List; -import org.apache.camel.Exchange; -import org.apache.camel.Processor; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.syncope.common.lib.patch.StatusPatch; -import org.apache.syncope.common.lib.types.AnyTypeKind; -import org.apache.syncope.core.provisioning.api.PropagationByResource; -import org.apache.syncope.common.lib.types.ResourceOperation; -import org.apache.syncope.common.lib.types.StatusPatchType; -import org.apache.syncope.core.persistence.api.dao.UserDAO; -import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; -import org.apache.syncope.core.provisioning.api.WorkflowResult; -import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; -import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter; -import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class UserStatusPropagationProcessor implements Processor { - - @Autowired - protected PropagationManager propagationManager; - - @Autowired - protected PropagationTaskExecutor taskExecutor; - - @Autowired - protected UserDAO userDAO; - - @SuppressWarnings("unchecked") - @Override - public void process(final Exchange exchange) { - WorkflowResult<Long> updated = (WorkflowResult<Long>) exchange.getIn().getBody(); - StatusPatch statusPatch = exchange.getProperty("statusPatch", StatusPatch.class); - Boolean nullPriorityAsync = exchange.getProperty("nullPriorityAsync", Boolean.class); - - PropagationByResource propByRes = new PropagationByResource(); - propByRes.addAll(ResourceOperation.UPDATE, statusPatch.getResources()); - List<PropagationTask> tasks = propagationManager.getUpdateTasks( - AnyTypeKind.USER, - statusPatch.getKey(), - false, - statusPatch.getType() != StatusPatchType.SUSPEND, - propByRes, - null, - null); - PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync); - - exchange.getOut().setBody(new ImmutablePair<>(updated.getResult(), propagationReporter.getStatuses())); - } -} http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserUpdateInPullProcessor.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserUpdateInPullProcessor.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserUpdateInPullProcessor.java deleted file mode 100644 index 1fb917d..0000000 --- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserUpdateInPullProcessor.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.provisioning.camel.processor; - -import java.util.List; -import java.util.Set; -import org.apache.camel.Exchange; -import org.apache.camel.Processor; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.common.lib.patch.UserPatch; -import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; -import org.apache.syncope.core.provisioning.api.WorkflowResult; -import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; -import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter; -import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class UserUpdateInPullProcessor implements Processor { - - @Autowired - protected PropagationManager propagationManager; - - @Autowired - protected PropagationTaskExecutor taskExecutor; - - @SuppressWarnings("unchecked") - @Override - public void process(final Exchange exchange) { - WorkflowResult<Pair<UserPatch, Boolean>> updated = - (WorkflowResult<Pair<UserPatch, Boolean>>) exchange.getIn().getBody(); - Set<String> excludedResources = exchange.getProperty("excludedResources", Set.class); - Boolean nullPriorityAsync = exchange.getProperty("nullPriorityAsync", Boolean.class); - - List<PropagationTask> tasks = propagationManager.getUserUpdateTasks( - updated, updated.getResult().getKey().getPassword() != null, excludedResources); - PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync); - - exchange.getOut().setBody(new ImmutablePair<>( - updated.getResult().getKey().getKey(), propagationReporter.getStatuses())); - } -} http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/AbstractProducer.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/AbstractProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/AbstractProducer.java index de1007d..a76620c 100644 --- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/AbstractProducer.java +++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/AbstractProducer.java @@ -29,6 +29,8 @@ public abstract class AbstractProducer extends DefaultProducer { private PropagationTaskExecutor taskExecutor; private AnyType anyType; + + private boolean pull; public AbstractProducer(final Endpoint endpoint, final AnyType anyType) { super(endpoint); @@ -54,4 +56,12 @@ public abstract class AbstractProducer extends DefaultProducer { public AnyType getAnyType() { return anyType; } + + public boolean isPull() { + return pull; + } + + public void setPull(final boolean pull) { + this.pull = pull; + } } http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ConfirmPasswordResetProducer.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ConfirmPasswordResetProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ConfirmPasswordResetProducer.java new file mode 100644 index 0000000..0769d3d --- /dev/null +++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/ConfirmPasswordResetProducer.java @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.syncope.core.provisioning.camel.producer; + +import java.util.List; + +import org.apache.camel.Endpoint; +import org.apache.camel.Exchange; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.patch.UserPatch; +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; +import org.apache.syncope.core.provisioning.api.WorkflowResult; +import org.apache.syncope.core.provisioning.camel.AnyType; + +public class ConfirmPasswordResetProducer extends AbstractProducer { + + public ConfirmPasswordResetProducer(final Endpoint endpoint, final AnyType anyType) { + super(endpoint, anyType); + } + + @SuppressWarnings("unchecked") + @Override + public void process(final Exchange exchange) throws Exception { + if (getAnyType() == AnyType.user) { + WorkflowResult<Pair<UserPatch, Boolean>> updated = + (WorkflowResult<Pair<UserPatch, Boolean>>) exchange.getIn().getBody(); + + List<PropagationTask> tasks = getPropagationManager().getUserUpdateTasks(updated); + + getPropagationTaskExecutor().execute(tasks, false); + } + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java index 26ddc3c..3c72c60 100644 --- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java +++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java @@ -17,14 +17,18 @@ package org.apache.syncope.core.provisioning.camel.producer; import java.util.List; +import java.util.Map; import java.util.Set; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.to.AnyObjectTO; import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; @@ -69,16 +73,36 @@ public class CreateProducer extends AbstractProducer { if (actual instanceof AnyObjectTO) { anyTypeKind = AnyTypeKind.ANY_OBJECT; } - List<PropagationTask> tasks = getPropagationManager().getCreateTasks( - anyTypeKind, - created.getResult(), - created.getPropByRes(), - ((AnyTO) actual).getVirAttrs(), - excludedResources); - PropagationReporter propagationReporter = + + if (actual instanceof GroupTO && isPull()) { + Map<String, String> groupOwnerMap = exchange.getProperty("groupOwnerMap", Map.class); + AttrTO groupOwner = ((GroupTO) actual).getPlainAttrMap().get(StringUtils.EMPTY); + if (groupOwner != null) { + groupOwnerMap.put(created.getResult(), groupOwner.getValues().iterator().next()); + } + + List<PropagationTask> tasks = getPropagationManager().getCreateTasks( + AnyTypeKind.GROUP, + created.getResult(), + created.getPropByRes(), + ((AnyTO) actual).getVirAttrs(), + excludedResources); getPropagationTaskExecutor().execute(tasks, nullPriorityAsync); - exchange.getOut().setBody(new ImmutablePair<>(created.getResult(), propagationReporter.getStatuses())); + exchange.getOut().setBody(new ImmutablePair<>(created.getResult(), null)); + } else { + List<PropagationTask> tasks = getPropagationManager().getCreateTasks( + anyTypeKind, + created.getResult(), + created.getPropByRes(), + ((AnyTO) actual).getVirAttrs(), + excludedResources); + PropagationReporter propagationReporter = + getPropagationTaskExecutor().execute(tasks, nullPriorityAsync); + + exchange.getOut().setBody(new ImmutablePair<>(created.getResult(), + propagationReporter.getStatuses())); + } } } } http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/StatusProducer.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/StatusProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/StatusProducer.java new file mode 100644 index 0000000..01c4bd8 --- /dev/null +++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/StatusProducer.java @@ -0,0 +1,103 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.syncope.core.provisioning.camel.producer; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.camel.Endpoint; +import org.apache.camel.Exchange; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.syncope.common.lib.patch.StatusPatch; +import org.apache.syncope.common.lib.patch.UserPatch; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.common.lib.types.StatusPatchType; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.PropagationByResource; +import org.apache.syncope.core.provisioning.api.WorkflowResult; +import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter; +import org.apache.syncope.core.provisioning.camel.AnyType; +import org.apache.syncope.core.workflow.api.UserWorkflowAdapter; + +public class StatusProducer extends AbstractProducer { + + private UserDAO userDAO; + private UserWorkflowAdapter uwfAdapter; + + public StatusProducer(final Endpoint endpoint, final AnyType anyType, final UserDAO userDAO, + final UserWorkflowAdapter uwfAdapter) { + super(endpoint, anyType); + this.userDAO = userDAO; + this.uwfAdapter = uwfAdapter; + } + + @SuppressWarnings("unchecked") + @Override + public void process(final Exchange exchange) throws Exception { + if (getAnyType() == AnyType.user && isPull()) { + WorkflowResult<Map.Entry<UserPatch, Boolean>> updated = + (WorkflowResult<Entry<UserPatch, Boolean>>) exchange.getIn().getBody(); + + Boolean enabled = exchange.getProperty("enabled", Boolean.class); + String key = exchange.getProperty("key", String.class); + + if (enabled != null) { + User user = userDAO.find(key); + + WorkflowResult<String> enableUpdate = null; + if (user.isSuspended() == null) { + enableUpdate = uwfAdapter.activate(key, null); + } else if (enabled && user.isSuspended()) { + enableUpdate = uwfAdapter.reactivate(key); + } else if (!enabled && !user.isSuspended()) { + enableUpdate = uwfAdapter.suspend(key); + } + + if (enableUpdate != null) { + if (enableUpdate.getPropByRes() != null) { + updated.getPropByRes().merge(enableUpdate.getPropByRes()); + updated.getPropByRes().purge(); + } + updated.getPerformedTasks().addAll(enableUpdate.getPerformedTasks()); + } + } + } else if (getAnyType() == AnyType.user) { + WorkflowResult<Long> updated = (WorkflowResult<Long>) exchange.getIn().getBody(); + StatusPatch statusPatch = exchange.getProperty("statusPatch", StatusPatch.class); + Boolean nullPriorityAsync = exchange.getProperty("nullPriorityAsync", Boolean.class); + + PropagationByResource propByRes = new PropagationByResource(); + propByRes.addAll(ResourceOperation.UPDATE, statusPatch.getResources()); + List<PropagationTask> tasks = getPropagationManager().getUpdateTasks( + AnyTypeKind.USER, + statusPatch.getKey(), + false, + statusPatch.getType() != StatusPatchType.SUSPEND, + propByRes, + null, + null); + PropagationReporter propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync); + + exchange.getOut().setBody(new ImmutablePair<>(updated.getResult(), propagationReporter.getStatuses())); + } + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/SuspendProducer.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/SuspendProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/SuspendProducer.java new file mode 100644 index 0000000..d8d2708 --- /dev/null +++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/SuspendProducer.java @@ -0,0 +1,57 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.syncope.core.provisioning.camel.producer; + +import java.util.List; + +import org.apache.camel.Endpoint; +import org.apache.camel.Exchange; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.patch.UserPatch; +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; +import org.apache.syncope.core.provisioning.api.WorkflowResult; +import org.apache.syncope.core.provisioning.camel.AnyType; + +public class SuspendProducer extends AbstractProducer { + + public SuspendProducer(final Endpoint endpoint, final AnyType anyType) { + super(endpoint, anyType); + } + + @SuppressWarnings("unchecked") + @Override + public void process(final Exchange exchange) throws Exception { + if (getAnyType() == AnyType.user) { + Pair<WorkflowResult<String>, Boolean> updated = + (Pair<WorkflowResult<String>, Boolean>) exchange.getIn().getBody(); + + // propagate suspension if and only if it is required by policy + if (updated != null && updated.getValue()) { + UserPatch userPatch = new UserPatch(); + userPatch.setKey(updated.getKey().getResult()); + + List<PropagationTask> tasks = getPropagationManager().getUserUpdateTasks( + new WorkflowResult<Pair<UserPatch, Boolean>>( + new ImmutablePair<>(userPatch, Boolean.FALSE), + updated.getKey().getPropByRes(), updated.getKey().getPerformedTasks())); + getPropagationTaskExecutor().execute(tasks); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java index 6146c60..2f9183b 100644 --- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java +++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java @@ -46,11 +46,18 @@ public class UpdateProducer extends AbstractProducer { Boolean nullPriorityAsync = exchange.getProperty("nullPriorityAsync", Boolean.class); Set<String> excludedResources = exchange.getProperty("excludedResources", Set.class); - if (actual instanceof UserPatch) { + if (actual instanceof UserPatch || isPull()) { WorkflowResult<Pair<UserPatch, Boolean>> updated = (WorkflowResult<Pair<UserPatch, Boolean>>) exchange.getIn().getBody(); - List<PropagationTask> tasks = getPropagationManager().getUserUpdateTasks(updated); + List<PropagationTask> tasks = null; + + if (isPull()) { + boolean passwordNotNull = updated.getResult().getKey().getPassword() != null; + tasks = getPropagationManager().getUserUpdateTasks(updated, passwordNotNull, excludedResources); + } else { + tasks = getPropagationManager().getUserUpdateTasks(updated); + } PropagationReporter propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync); @@ -72,7 +79,7 @@ public class UpdateProducer extends AbstractProducer { updated.getPropByRes(), ((AnyPatch) actual).getVirAttrs(), excludedResources); - PropagationReporter propagationReporter = + PropagationReporter propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync); exchange.getOut().setBody(new ImmutablePair<>(updated.getResult(), propagationReporter.getStatuses())); http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/resources/groupRoutes.xml ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/resources/groupRoutes.xml b/ext/camel/provisioning-camel/src/main/resources/groupRoutes.xml index d21c220..9c92f25 100644 --- a/ext/camel/provisioning-camel/src/main/resources/groupRoutes.xml +++ b/ext/camel/provisioning-camel/src/main/resources/groupRoutes.xml @@ -40,12 +40,12 @@ under the License. <route id="createGroupPull"> <from uri="direct:createGroupInPull"/> - <setProperty propertyName="any"> + <setProperty propertyName="actual"> <simple>${body}</simple> </setProperty> <doTry> <bean ref="gwfAdapter" method="create(${body})"/> - <process ref="groupCreateInPullProcessor"/> + <to uri="propagate:create?anyType=group&pull=true"/> <to uri="direct:createGroupInPullPort"/> <doCatch> <exception>java.lang.RuntimeException</exception> http://git-wip-us.apache.org/repos/asf/syncope/blob/891122c2/ext/camel/provisioning-camel/src/main/resources/userRoutes.xml ---------------------------------------------------------------------- diff --git a/ext/camel/provisioning-camel/src/main/resources/userRoutes.xml b/ext/camel/provisioning-camel/src/main/resources/userRoutes.xml index 73564f9..5d68d2a 100644 --- a/ext/camel/provisioning-camel/src/main/resources/userRoutes.xml +++ b/ext/camel/provisioning-camel/src/main/resources/userRoutes.xml @@ -75,8 +75,8 @@ under the License. <route id="userInPull"> <from uri="direct:userInPull"/> - <process ref="userSetStatusInPullProcessor"/> - <process ref="userUpdateInPullProcessor"/> + <to uri="propagate:status?anyType=user&pull=true"/> + <to uri="propagate:update?anyType=user&pull=true"/> <to uri="direct:updateInPullPort"/> </route> @@ -182,7 +182,7 @@ under the License. <route id="userStatusPropagation"> <from uri="direct:userStatusPropagation"/> - <process ref="userStatusPropagationProcessor"/> + <to uri="propagate:status?anyType=user"/> <to uri="direct:statusPort"/> </route> @@ -202,7 +202,7 @@ under the License. <from uri="direct:internalSuspendUser"/> <doTry> <bean ref="uwfAdapter" method="internalSuspend(${body})"/> - <process ref="userInternalSuspendProcessor"/> + <to uri="propagate:suspend?anyType=user"/> <to uri="direct:internalSuspendUserPort"/> <doCatch> <exception>java.lang.RuntimeException</exception> @@ -233,7 +233,7 @@ under the License. <from uri="direct:confirmPwdReset"/> <doTry> <bean ref="uwfAdapter" method="confirmPasswordReset(${property.key},${property.token},${property.password})"/> - <process ref="userConfirmPwdResetProcessor"/> + <to uri="propagate:confirmPasswordReset?anyType=user"/> <to uri="direct:confirmPwdResetPort"/> <doCatch> <exception>java.lang.RuntimeException</exception>