[SYNCOPE-931] Refusing invalid route definition updates, restoring original 
route content on error


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/7a7d415c
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/7a7d415c
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/7a7d415c

Branch: refs/heads/master
Commit: 7a7d415cdb8df78f78ee4fcd73b0d5b392022d86
Parents: f038a67
Author: Francesco Chicchiriccò <ilgro...@apache.org>
Authored: Wed Sep 7 10:16:16 2016 +0200
Committer: Francesco Chicchiriccò <ilgro...@apache.org>
Committed: Wed Sep 7 15:14:41 2016 +0200

----------------------------------------------------------------------
 .../syncope/core/logic/CamelRouteLogic.java     | 14 +++++-
 .../camel/AbstractCamelProvisioningManager.java | 14 +++---
 .../core/provisioning/camel/CamelException.java | 32 +++++++++++++
 .../provisioning/camel/SyncopeCamelContext.java | 47 +++++++++++++-------
 4 files changed, 81 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/7a7d415c/ext/camel/logic/src/main/java/org/apache/syncope/core/logic/CamelRouteLogic.java
----------------------------------------------------------------------
diff --git 
a/ext/camel/logic/src/main/java/org/apache/syncope/core/logic/CamelRouteLogic.java
 
b/ext/camel/logic/src/main/java/org/apache/syncope/core/logic/CamelRouteLogic.java
index b489f3b..2856daf 100644
--- 
a/ext/camel/logic/src/main/java/org/apache/syncope/core/logic/CamelRouteLogic.java
+++ 
b/ext/camel/logic/src/main/java/org/apache/syncope/core/logic/CamelRouteLogic.java
@@ -38,6 +38,7 @@ import 
org.apache.syncope.core.persistence.api.dao.CamelRouteDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.entity.CamelRoute;
 import org.apache.syncope.core.provisioning.api.data.CamelRouteDataBinder;
+import org.apache.syncope.core.provisioning.camel.CamelException;
 import org.apache.syncope.core.provisioning.camel.SyncopeCamelContext;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -84,11 +85,20 @@ public class CamelRouteLogic extends 
AbstractTransactionalLogic<CamelRouteTO> {
         if (route == null) {
             throw new NotFoundException("CamelRoute with key=" + 
routeTO.getKey());
         }
+        String originalContent = route.getContent();
 
         LOG.debug("Updating route {} with content {}", routeTO.getKey(), 
routeTO.getContent());
         binder.update(route, routeTO);
 
-        context.updateContext(routeTO.getKey());
+        try {
+            context.updateContext(routeTO.getKey());
+        } catch (CamelException e) {
+            // if an exception was thrown while updating the context, restore 
the former route definition
+            LOG.debug("Update of route {} failed, reverting", 
routeTO.getKey());
+            context.restoreRoute(routeTO.getKey(), originalContent);
+
+            throw e;
+        }
     }
 
     @PreAuthorize("hasRole('" + CamelEntitlement.ROUTE_UPDATE + "')")
@@ -106,7 +116,7 @@ public class CamelRouteLogic extends 
AbstractTransactionalLogic<CamelRouteTO> {
         } else {
             MetricRegistry registry = registryService.getMetricsRegistry();
             for (Map.Entry<String, Timer> entry : 
registry.getTimers().entrySet()) {
-                CamelMetrics.MeanRate meanRate = new CamelMetrics.MeanRate();  
              
+                CamelMetrics.MeanRate meanRate = new CamelMetrics.MeanRate();
                 
meanRate.setRouteId(StringUtils.substringBetween(entry.getKey(), ".", "."));
                 meanRate.setValue(entry.getValue().getMeanRate());
                 metrics.getResponseMeanRates().add(meanRate);

http://git-wip-us.apache.org/repos/asf/syncope/blob/7a7d415c/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/AbstractCamelProvisioningManager.java
----------------------------------------------------------------------
diff --git 
a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/AbstractCamelProvisioningManager.java
 
b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/AbstractCamelProvisioningManager.java
index 1a6d7df..30ae2b3 100644
--- 
a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/AbstractCamelProvisioningManager.java
+++ 
b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/AbstractCamelProvisioningManager.java
@@ -53,23 +53,19 @@ abstract class AbstractCamelProvisioningManager {
 
     protected final List<String> knownURIs = new ArrayList<>();
 
-    protected SpringCamelContext getContext() {
-        return contextFactory.getContext();
-    }
-
     protected void sendMessage(final String uri, final Object obj) {
-        Exchange exchange = new DefaultExchange(getContext());
+        Exchange exchange = new DefaultExchange(contextFactory.getContext());
 
         DefaultMessage message = new DefaultMessage();
         message.setBody(obj);
         exchange.setIn(message);
 
-        ProducerTemplate template = getContext().createProducerTemplate();
+        ProducerTemplate template = 
contextFactory.getContext().createProducerTemplate();
         template.send(uri, exchange);
     }
 
     protected void sendMessage(final String uri, final Object body, final 
Map<String, Object> properties) {
-        Exchange exchange = new DefaultExchange(getContext());
+        Exchange exchange = new DefaultExchange(contextFactory.getContext());
 
         for (Map.Entry<String, Object> property : properties.entrySet()) {
             exchange.setProperty(property.getKey(), property.getValue());
@@ -79,14 +75,14 @@ abstract class AbstractCamelProvisioningManager {
         DefaultMessage message = new DefaultMessage();
         message.setBody(body);
         exchange.setIn(message);
-        ProducerTemplate template = getContext().createProducerTemplate();
+        ProducerTemplate template = 
contextFactory.getContext().createProducerTemplate();
         template.send(uri, exchange);
     }
 
     protected PollingConsumer getConsumer(final String uri) {
         if (!knownURIs.contains(uri)) {
             knownURIs.add(uri);
-            Endpoint endpoint = getContext().getEndpoint(uri);
+            Endpoint endpoint = contextFactory.getContext().getEndpoint(uri);
             PollingConsumer pollingConsumer = null;
             try {
                 pollingConsumer = endpoint.createPollingConsumer();

http://git-wip-us.apache.org/repos/asf/syncope/blob/7a7d415c/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelException.java
----------------------------------------------------------------------
diff --git 
a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelException.java
 
b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelException.java
new file mode 100644
index 0000000..4df3137
--- /dev/null
+++ 
b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelException.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * Wraps checked exceptions thrown by Apache Camel.
+ */
+public class CamelException extends RuntimeException {
+
+    private static final long serialVersionUID = 517692473216575144L;
+
+    public CamelException(final Throwable cause) {
+        super(cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/7a7d415c/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/SyncopeCamelContext.java
----------------------------------------------------------------------
diff --git 
a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/SyncopeCamelContext.java
 
b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/SyncopeCamelContext.java
index d57c421..5466004 100644
--- 
a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/SyncopeCamelContext.java
+++ 
b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/SyncopeCamelContext.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.provisioning.camel;
 
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import javax.xml.bind.JAXBContext;
@@ -28,6 +29,8 @@ import 
org.apache.camel.component.metrics.routepolicy.MetricsRoutePolicyFactory;
 import org.apache.camel.model.Constants;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.spring.SpringCamelContext;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
 import org.apache.commons.io.IOUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
@@ -37,6 +40,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 import org.w3c.dom.Node;
 import org.w3c.dom.bootstrap.DOMImplementationRegistry;
 import org.w3c.dom.ls.DOMImplementationLS;
@@ -64,18 +69,25 @@ public class SyncopeCamelContext {
         if (camelContext.getRouteDefinitions().isEmpty()) {
             List<CamelRoute> routes = routeDAO.findAll();
             LOG.debug("{} route(s) are going to be loaded ", routes.size());
-            loadContext(routes);
+            loadContext(CollectionUtils.collect(routes, new 
Transformer<CamelRoute, String>() {
+
+                @Override
+                public String transform(final CamelRoute input) {
+                    return input.getContent();
+                }
+            }));
             try {
                 camelContext.start();
             } catch (Exception e) {
                 LOG.error("While starting Camel context", e);
+                throw new CamelException(e);
             }
         }
 
         return camelContext;
     }
 
-    private void loadContext(final List<CamelRoute> routes) {
+    private void loadContext(final Collection<String> routes) {
         try {
             DOMImplementationRegistry reg = 
DOMImplementationRegistry.newInstance();
             DOMImplementationLS domImpl = (DOMImplementationLS) 
reg.getDOMImplementation("LS");
@@ -84,10 +96,10 @@ public class SyncopeCamelContext {
             JAXBContext jaxbContext = 
JAXBContext.newInstance(Constants.JAXB_CONTEXT_PACKAGES);
             Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
             List<RouteDefinition> routeDefs = new ArrayList<>();
-            for (CamelRoute route : routes) {
+            for (String route : routes) {
                 InputStream input = null;
                 try {
-                    input = IOUtils.toInputStream(route.getContent(), 
SyncopeConstants.DEFAULT_CHARSET);
+                    input = IOUtils.toInputStream(route, 
SyncopeConstants.DEFAULT_CHARSET);
                     LSInput lsinput = domImpl.createLSInput();
                     lsinput.setByteStream(input);
 
@@ -100,25 +112,29 @@ public class SyncopeCamelContext {
             camelContext.addRouteDefinitions(routeDefs);
         } catch (Exception e) {
             LOG.error("While loading Camel context {}", e);
+            throw new CamelException(e);
         }
     }
 
+    @Transactional(propagation = Propagation.SUPPORTS)
     public void updateContext(final String routeKey) {
         if (camelContext == null) {
             getContext();
         } else if (!camelContext.getRouteDefinitions().isEmpty()) {
             
camelContext.getRouteDefinitions().remove(camelContext.getRouteDefinition(routeKey));
-            loadContext(Collections.singletonList(routeDAO.find(routeKey)));
-            
-            // Start the Camel Context if it's stopped, as maybe this update 
fixes a previous route error which
-            // caused startup to fail
-            if (camelContext.isStopped()) {
-                try {
-                    camelContext.start();
-                } catch (Exception e) {
-                    LOG.error("While restarting Camel context", e);
-                }
-            }
+            
loadContext(Collections.singletonList(routeDAO.find(routeKey).getContent()));
+        }
+    }
+
+    public void restoreRoute(final String routeKey, final String routeContent) 
{
+        try {
+            
camelContext.getRouteDefinitions().remove(camelContext.getRouteDefinition(routeKey));
+            loadContext(Collections.singletonList(routeContent));
+
+            camelContext.start();
+        } catch (Exception e) {
+            LOG.error("While restoring Camel route {}", routeKey, e);
+            throw new CamelException(e);
         }
     }
 
@@ -128,6 +144,7 @@ public class SyncopeCamelContext {
             camelContext.start();
         } catch (Exception e) {
             LOG.error("While restarting Camel context", e);
+            throw new CamelException(e);
         }
     }
 

Reply via email to