This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 66ee0906db1d CAMEL-25068: camel-core - Route templates: fail when the
route id of a route from a template is already used (item 2) (#27467)
66ee0906db1d is described below
commit 66ee0906db1d73a22d3b437e1be01ffe747c0d31
Author: allthingssecurity <[email protected]>
AuthorDate: Wed Oct 7 16:48:45 2026 +0530
CAMEL-25068: camel-core - Route templates: fail when the route id of a
route from a template is already used (item 2) (#27467)
A route created from a route template (or a Kamelet) with the id of an
existing
route that was not created from the same template now fails with
FailedToCreateRouteFromTemplateException instead of silently replacing it.
A route from the same template is still replaced, so updating the parent
route
of a Kamelet endpoint keeps working. Also fixes a NullPointerException in
the
duplicate node id error of a route from a template without a route id.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../kamelet/KameletExistingRouteIdTest.java | 164 +++++++++++++++++++++
.../java/org/apache/camel/impl/DefaultModel.java | 19 ++-
.../org/apache/camel/model/RouteDefinition.java | 14 ++
.../builder/RouteTemplateExistingRouteIdTest.java | 146 ++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc | 12 ++
.../dsl/yaml/RouteReloadTemplatedRouteTest.java | 144 ++++++++++++++++++
6 files changed, 498 insertions(+), 1 deletion(-)
diff --git
a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletExistingRouteIdTest.java
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletExistingRouteIdTest.java
new file mode 100644
index 000000000000..9cebb3e285bc
--- /dev/null
+++
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletExistingRouteIdTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.camel.component.kamelet;
+
+import org.apache.camel.FailedToCreateRouteFromTemplateException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.apache.camel.util.ObjectHelper;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * The route of a Kamelet endpoint with a route id must not replace an
existing route that was not created from the same
+ * Kamelet, while the route of the same Kamelet is created again when its
parent route is updated.
+ */
+class KameletExistingRouteIdTest extends CamelTestSupport {
+
+ @Override
+ public boolean isUseRouteBuilder() {
+ return false;
+ }
+
+ @Test
+ void routeIdOfRegularRoute() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ routeTemplate("echo")
+ .templateParameter("prefix")
+ .from("kamelet:source")
+ .setBody().simple("{{prefix}}-${body}");
+
+ from("direct:existing").routeId("existing")
+ .setBody().constant("existing");
+ }
+ });
+ context.start();
+
+ assertThatThrownBy(() -> context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+
from("direct:b").routeId("b").to("kamelet:echo/existing?prefix=b");
+ }
+
})).hasRootCauseInstanceOf(FailedToCreateRouteFromTemplateException.class)
+ .satisfies(e ->
assertThat(ObjectHelper.getException(FailedToCreateKameletException.class,
e)).isNotNull());
+
+ // the regular route is kept
+
assertThat(context.getRoute("existing").getEndpoint().getEndpointUri()).isEqualTo("direct://existing");
+ assertThat(template.requestBody("direct:existing",
"x")).isEqualTo("existing");
+ }
+
+ @Test
+ void routeIdOfRouteFromOtherKamelet() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ routeTemplate("echo")
+ .templateParameter("prefix")
+ .from("kamelet:source")
+ .setBody().simple("{{prefix}}-${body}");
+
+ routeTemplate("shout")
+ .templateParameter("prefix")
+ .from("kamelet:source")
+ .setBody().simple("{{prefix}}-${body.toUpperCase()}");
+ }
+ });
+ context.start();
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:a").routeId("a").to("kamelet:echo/myId?prefix=a");
+ }
+ });
+ assertThat(template.requestBody("direct:a", "x")).isEqualTo("a-x");
+
+ assertThatThrownBy(() -> context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+
from("direct:b").routeId("b").to("kamelet:shout/myId?prefix=b");
+ }
+
})).hasRootCauseInstanceOf(FailedToCreateRouteFromTemplateException.class)
+ .satisfies(e ->
assertThat(ObjectHelper.getException(FailedToCreateKameletException.class,
e)).isNotNull());
+
+ // the route of the first Kamelet endpoint is kept
+ assertThat(template.requestBody("direct:a", "x")).isEqualTo("a-x");
+ }
+
+ @Test
+ void updateParent() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ routeTemplate("echo")
+ .templateParameter("prefix")
+ .from("kamelet:source")
+ .setBody().simple("{{prefix}}-${body}");
+
+ from("direct:a").routeId("a").to("kamelet:echo/myId?prefix=a");
+ }
+ });
+ context.start();
+ assertThat(template.requestBody("direct:a", "x")).isEqualTo("a-x");
+
+ // as a route reload with removeAllRoutes=false does
+ new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:a").routeId("a").to("kamelet:echo/myId?prefix=b");
+ }
+ }.updateRoutesToCamelContext(context);
+ assertThat(template.requestBody("direct:a", "x")).isEqualTo("b-x");
+ }
+
+ @Test
+ void removeAndReAddParent() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ routeTemplate("echo")
+ .templateParameter("prefix")
+ .from("kamelet:source")
+ .setBody().simple("{{prefix}}-${body}");
+ }
+ });
+ context.start();
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:a").routeId("a").to("kamelet:echo/myId?prefix=a");
+ }
+ });
+ assertThat(template.requestBody("direct:a", "x")).isEqualTo("a-x");
+
+ context.getRouteController().stopRoute("a");
+ context.removeRoute("a");
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:a").routeId("a").to("kamelet:echo/myId?prefix=b");
+ }
+ });
+ assertThat(template.requestBody("direct:a", "x")).isEqualTo("b-x");
+ }
+}
diff --git
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
index 0d740ce89a6c..c14a3d0dbde8 100644
---
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
+++
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
@@ -648,6 +648,22 @@ public class DefaultModel implements Model {
if (group != null) {
def.setGroup(group);
}
+ // a route from a template (or a Kamelet) must not replace an existing
route with the same id, unless that
+ // route was created from the same template (such as the route of a
Kamelet with a route id that is created
+ // again when its parent route is updated); a route that exists in the
CamelContext but has no route
+ // definition in the model is never replaced
+ String id = def.getId();
+ if (id != null) {
+ RouteDefinition existing = getRouteDefinition(id);
+ boolean sameTemplate = existing != null &&
routeTemplateId.equals(existing.getRouteTemplateId());
+ if (!sameTemplate && (existing != null ||
camelContext.getRoute(id) != null)) {
+ throw new FailedToCreateRouteFromTemplateException(
+ id, routeTemplateId,
+ "Route with id: " + id
+ + " already exists. Remove the
existing route first or use another route id.");
+ }
+ }
+ def.setRouteTemplateId(routeTemplateId);
def.setTemplateParameters(prop);
def.setTemplateDefaultParameters(propDefaultValues);
def.setRouteTemplateContext(routeTemplateContext);
@@ -674,7 +690,8 @@ public class DefaultModel implements Model {
String duplicate = RouteDefinitionHelper.validateUniqueIds(def,
routeDefinitions, prefixId);
if (duplicate != null) {
throw new FailedToCreateRouteFromTemplateException(
- routeId, routeTemplateId,
+
def.idOrCreate(camelContext.getCamelContextExtension().getContextPlugin(NodeIdFactory.class)),
+ routeTemplateId,
"Duplicate id detected: " + duplicate + ". Please correct
ids to be unique among all your routes.");
}
diff --git
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
index 3e94918b6868..d28f710977b6 100644
---
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
+++
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
@@ -97,6 +97,7 @@ public class RouteDefinition extends
OutputDefinition<RouteDefinition>
private Map<String, Object> templateParameters;
private Map<String, Object> templateDefaultParameters;
private RouteTemplateContext routeTemplateContext;
+ private String routeTemplateId;
private Resource resource;
private String precondition;
@@ -893,6 +894,19 @@ public class RouteDefinition extends
OutputDefinition<RouteDefinition>
this.routeTemplateContext = routeTemplateContext;
}
+ /**
+ * The id of the route template (or Kamelet) this route was created from,
or <tt>null</tt> if the route was not
+ * created from a route template.
+ */
+ public String getRouteTemplateId() {
+ return routeTemplateId;
+ }
+
+ @XmlTransient
+ public void setRouteTemplateId(String routeTemplateId) {
+ this.routeTemplateId = routeTemplateId;
+ }
+
public Resource getResource() {
return resource;
}
diff --git
a/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateExistingRouteIdTest.java
b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateExistingRouteIdTest.java
new file mode 100644
index 000000000000..abef275a61ba
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateExistingRouteIdTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.camel.builder;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.FailedToCreateRouteFromTemplateException;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.model.TemplatedRouteDefinition;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * A route created from a template must not replace an existing route with the
same id, unless that route was created
+ * from the same template.
+ */
+class RouteTemplateExistingRouteIdTest extends ContextTestSupport {
+
+ @Override
+ public boolean isUseRouteBuilder() {
+ return false;
+ }
+
+ @BeforeEach
+ void addRoutes() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ routeTemplate("myTemplate")
+ .templateParameter("foo")
+ .from("direct:{{foo}}")
+ .setBody(simple("template {{foo}}"));
+
+ routeTemplate("otherTemplate")
+ .templateParameter("foo")
+ .from("direct:other-{{foo}}")
+ .setBody(simple("other {{foo}}"));
+
+ from("direct:existing").routeId("existing")
+ .setBody(constant("existing"));
+ }
+ });
+ context.start();
+ }
+
+ @Test
+ void builderRouteIdOfExistingRoute() {
+ assertThatThrownBy(() -> TemplatedRouteBuilder.builder(context,
"myTemplate")
+ .routeId("existing")
+ .parameter("foo", "one")
+ .add())
+ .isInstanceOf(FailedToCreateRouteFromTemplateException.class)
+ .hasMessageContaining("existing");
+
+ assertExistingRouteUnchanged();
+ assertThat(context.hasEndpoint("direct:one")).isNull();
+ }
+
+ @Test
+ void templatedRouteIdOfExistingRoute() {
+ TemplatedRouteDefinition def = new TemplatedRouteDefinition();
+ def.setRouteTemplateRef("myTemplate");
+ def.setRouteId("existing");
+ def.parameter("foo", "one");
+
+ assertThatThrownBy(() -> ((ModelCamelContext)
context).addRouteFromTemplatedRoute(def))
+ .isInstanceOf(FailedToCreateRouteFromTemplateException.class)
+ .hasMessageContaining("existing");
+
+ assertExistingRouteUnchanged();
+ }
+
+ @Test
+ void routeIdOfRouteFromSameTemplate() {
+ TemplatedRouteBuilder.builder(context,
"myTemplate").routeId("mine").parameter("foo", "one").add();
+
+ // a route from the same template is replaced (as before), which is
how a route from a template is updated
+ TemplatedRouteBuilder.builder(context,
"myTemplate").routeId("mine").parameter("foo", "two").add();
+
+ assertThat(context.getRoutes()).hasSize(2);
+
assertThat(context.getRoute("mine").getEndpoint().getEndpointUri()).isEqualTo("direct://two");
+ assertThat(template.requestBody("direct:two",
"x")).isEqualTo("template two");
+ }
+
+ @Test
+ void routeIdOfRouteFromOtherTemplate() {
+ TemplatedRouteBuilder.builder(context,
"myTemplate").routeId("mine").parameter("foo", "one").add();
+
+ assertThatThrownBy(() -> TemplatedRouteBuilder.builder(context,
"otherTemplate")
+ .routeId("mine")
+ .parameter("foo", "two")
+ .add())
+ .isInstanceOf(FailedToCreateRouteFromTemplateException.class)
+ .hasMessageContaining("mine");
+
+ assertThat(context.getRoutes()).hasSize(2);
+
assertThat(context.getRoute("mine").getEndpoint().getEndpointUri()).isEqualTo("direct://one");
+ assertThat(template.requestBody("direct:one",
"x")).isEqualTo("template one");
+ assertThat(context.hasEndpoint("direct:other-two")).isNull();
+ }
+
+ @Test
+ void duplicateNodeIdWithoutRouteId() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ routeTemplate("fixedNodeId")
+ .templateParameter("foo")
+ .from("direct:{{foo}}")
+ .setBody(constant("fixed")).id("fixed");
+ }
+ });
+ TemplatedRouteBuilder.builder(context,
"fixedNodeId").prefixId("p").parameter("foo", "one").add();
+
+ // the same prefix gives the same node id, which is reported as such
(also without a route id)
+ assertThatThrownBy(() -> TemplatedRouteBuilder.builder(context,
"fixedNodeId")
+ .prefixId("p")
+ .parameter("foo", "two")
+ .add())
+ .isInstanceOf(FailedToCreateRouteFromTemplateException.class)
+ .hasMessageContaining("Duplicate id detected");
+ }
+
+ private void assertExistingRouteUnchanged() {
+ assertThat(context.getRoutes()).hasSize(1);
+ assertThat(context.getRouteDefinitions()).hasSize(1);
+
assertThat(context.getRoute("existing").getEndpoint().getEndpointUri()).isEqualTo("direct://existing");
+ assertThat(template.requestBody("direct:existing",
"x")).isEqualTo("existing");
+ }
+}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index 3164ab7f11d3..380ef42e8968 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -507,6 +507,18 @@ The
`org.apache.camel.processor.InterceptSendToEndpointCallback` class is deprec
the first route are now kept as beans of the caller, so the later routes
share these instances instead of each
getting new ones. Use a new `RouteTemplateContext` per route to get new
template beans for each route.
+==== A route id that is already used (Breaking change)
+
+Creating a route from a route template (`TemplatedRouteBuilder`,
`templatedRoute`, `addRouteFromTemplate`, or a
+Kamelet endpoint with a route id) with the id of an existing route that was
not created from the same template (a
+regular route, or a route from another template or Kamelet) now fails with
`FailedToCreateRouteFromTemplateException`.
+For a Kamelet endpoint this exception is wrapped in a
`FailedToCreateKameletException`. Prior to Camel 4.23 the
+existing route was removed and replaced without notice. To replace such a
route, stop and remove it first.
+
+An existing route that was created from the same template is still replaced,
as before. This is how the route of a
+Kamelet endpoint with a route id is created again when its parent route is
updated (for example by a route reload),
+and how a route from a template is updated with other parameters.
+
=== camel-aws2-ddb
The `aws2-ddb:application-json` data type transformer no longer overwrites a
`CamelAwsDdbReturnValues` header that
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadTemplatedRouteTest.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadTemplatedRouteTest.java
new file mode 100644
index 000000000000..a2aa2993be69
--- /dev/null
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadTemplatedRouteTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.camel.dsl.yaml;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+
+import org.apache.camel.ServiceStatus;
+import org.apache.camel.dsl.yaml.support.YamlTestSupport;
+import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.support.RouteWatcherReloadStrategy;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * CAMEL-25068: a route from a template fails when a route with its id exists.
Reloading a file with a templated route
+ * (keeping the other routes) must still work.
+ */
+class RouteReloadTemplatedRouteTest extends YamlTestSupport {
+
+ /** Subclass to expose protected lifecycle methods for testing without a
live file watcher. */
+ static class TestableStrategy extends RouteWatcherReloadStrategy {
+ TestableStrategy(String dir) {
+ super(dir);
+ }
+
+ @Override
+ public void doStart() throws Exception {
+ super.doStart();
+ }
+
+ @Override
+ public void doStop() throws Exception {
+ super.doStop();
+ }
+ }
+
+ private Path dir;
+ private Path file;
+
+ @Override
+ public void doSetup() throws Exception {
+ dir = Files.createTempDirectory("camel-reload");
+ file = dir.resolve("greet.camel.yaml");
+ Files.writeString(file, routes("Hello"));
+ context.start();
+ loadRoutes(ResourceHelper.resolveResource(context, "file:" + file));
+ }
+
+ @Override
+ public void doCleanup() throws Exception {
+ deleteRecursively(dir);
+ }
+
+ @Test
+ void reloadKeepingTheOtherRoutes() throws Exception {
+ TestableStrategy strategy = new TestableStrategy(dir.toString());
+ strategy.setCamelContext(context);
+ strategy.setPattern("*.yaml");
+ strategy.setRemoveAllRoutes(false);
+ // the strategy is not started (no file watcher in the test): its
reload callback is driven by hand
+ strategy.doStart();
+ try {
+
assertThat(context.getRouteController().getRouteStatus("greet")).isEqualTo(ServiceStatus.Started);
+
+ Files.writeString(file, routes("Hi"));
+ strategy.getResourceReload().onReload(file.toString(),
+ ResourceHelper.resolveResource(context, "file:" + file));
+
+ // the reload does not fail on the id of the templated route, and
both routes run
+
assertThat(context.getRouteController().getRouteStatus("greet")).isEqualTo(ServiceStatus.Started);
+
assertThat(context.getRouteController().getRouteStatus("plain")).isEqualTo(ServiceStatus.Started);
+
assertThat(context.createFluentProducerTemplate().to("direct:plain").request(String.class))
+ .isEqualTo("plain Hi");
+ } finally {
+ strategy.doStop();
+ }
+ }
+
+ private static String routes(String greeting) {
+ return """
+ - routeTemplate:
+ id: "greeting"
+ parameters:
+ - name: "greeting"
+ from:
+ uri: "direct:greet"
+ steps:
+ - setBody:
+ constant: "{{greeting}}"
+ - templatedRoute:
+ routeTemplateRef: "greeting"
+ routeId: "greet"
+ parameters:
+ - name: "greeting"
+ value: "%s"
+ - route:
+ id: plain
+ from:
+ uri: direct:plain
+ steps:
+ - setBody:
+ constant: "plain %s"
+ """.formatted(greeting, greeting);
+ }
+
+ private static void deleteRecursively(Path path) throws IOException {
+ if (path == null || !Files.exists(path)) {
+ return;
+ }
+ Files.walkFileTree(path, new SimpleFileVisitor<>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes
attrs) throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException
exc) throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+}