[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); } }