You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2016/12/13 15:59:01 UTC
[07/13] brooklyn-server git commit: update jclouds version
update jclouds version
- adapt brooklyn code to recent jclouds api
- add jclouds snapshot repo to pom.xml to be removed
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/2a0f3e56
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/2a0f3e56
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/2a0f3e56
Branch: refs/heads/master
Commit: 2a0f3e568bd3e8119291446aa11057933159f9b6
Parents: 1a27940
Author: Andrea Turli <an...@gmail.com>
Authored: Tue Nov 8 11:45:52 2016 +0100
Committer: Andrea Turli <an...@gmail.com>
Committed: Tue Dec 6 15:30:45 2016 +0100
----------------------------------------------------------------------
.../location/jclouds/JcloudsLocation.java | 2 +-
.../AbstractPortableTemplateBuilder.java | 3 ++
.../templates/PortableTemplateBuilder.java | 10 ++++-
.../persist/jclouds/BlobStoreExpiryTest.java | 44 +++++++-------------
.../jclouds/DelegatingComputeService.java | 24 +++++------
...dsByonLocationResolverStubbedRebindTest.java | 3 +-
.../JcloudsByonLocationResolverStubbedTest.java | 3 +-
.../jclouds/StubbedComputeServiceRegistry.java | 9 ++--
pom.xml | 12 +++++-
9 files changed, 53 insertions(+), 57 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2a0f3e56/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
index 08ddad9..8a95ca0 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
@@ -2208,7 +2208,7 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
if (useKey) {
// NB: further keys are added from config *after* user creation
- statements.add(new AuthorizeRSAPublicKeys("~"+user+"/.ssh", ImmutableList.of(credential.getPublicKeyData())));
+ statements.add(new AuthorizeRSAPublicKeys("~"+user+"/.ssh", ImmutableList.of(credential.getPublicKeyData()), null));
if (Strings.isNonBlank(credential.getPrivateKeyData())) {
createdUserCreds = LoginCredentials.builder().user(user).privateKey(credential.getPrivateKeyData()).build();
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2a0f3e56/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java
index c935ea8..f159834 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java
@@ -28,6 +28,7 @@ import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.options.TemplateOptions;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
@@ -63,6 +64,8 @@ public abstract class AbstractPortableTemplateBuilder<T extends AbstractPortable
private TemplateOptions options;
/** these are extra options that we want _added_, in order, on top of the last options set */
private List<TemplateOptions> additionalOptions = new ArrayList<TemplateOptions>();
+ @VisibleForTesting
+ protected Boolean forceCacheReload;
@Override
public T any() {
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2a0f3e56/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java
index eaea03b..8d33921 100644
--- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java
@@ -42,13 +42,19 @@ public class PortableTemplateBuilder<T extends PortableTemplateBuilder<?>> exten
ComputeService svc;
List<TemplateOptions> additionalOptionalOptions = new ArrayList<TemplateOptions>();
-
+
@Override
public synchronized Template build() {
if (svc!=null) return newJcloudsTemplate(svc);
throw new IllegalStateException("Cannot build a portable template until a compute service is attached");
}
-
+
+ @Override
+ public TemplateBuilder forceCacheReload() {
+ this.forceCacheReload = true;
+ return this;
+ }
+
public synchronized ComputeService attachComputeService(ComputeService svc) {
ComputeService old = this.svc;
this.svc = svc;
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2a0f3e56/locations/jclouds/src/test/java/org/apache/brooklyn/core/mgmt/persist/jclouds/BlobStoreExpiryTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/core/mgmt/persist/jclouds/BlobStoreExpiryTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/core/mgmt/persist/jclouds/BlobStoreExpiryTest.java
index 98c49a2..460d705 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/core/mgmt/persist/jclouds/BlobStoreExpiryTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/core/mgmt/persist/jclouds/BlobStoreExpiryTest.java
@@ -19,13 +19,6 @@
package org.apache.brooklyn.core.mgmt.persist.jclouds;
import static com.google.common.base.Preconditions.checkNotNull;
-import static org.jclouds.openstack.reference.AuthHeaders.URL_SUFFIX;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.List;
-import java.util.Map.Entry;
import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.core.entity.Entities;
@@ -33,34 +26,20 @@ import org.apache.brooklyn.core.internal.BrooklynProperties;
import org.apache.brooklyn.core.location.LocationConfigKeys;
import org.apache.brooklyn.core.location.cloud.CloudLocationConfig;
import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
-import org.apache.brooklyn.util.collections.MutableMap;
-import org.apache.brooklyn.util.http.HttpTool;
-import org.apache.brooklyn.util.http.HttpToolResponse;
+import org.apache.brooklyn.location.jclouds.JcloudsLocation;
+import org.apache.brooklyn.location.jclouds.JcloudsUtil;
import org.apache.brooklyn.util.text.Identifiers;
-import org.apache.brooklyn.util.time.Duration;
-import org.apache.brooklyn.util.time.Time;
-import org.apache.http.client.HttpClient;
import org.jclouds.blobstore.BlobStoreContext;
-import org.jclouds.blobstore.domain.PageSet;
-import org.jclouds.blobstore.domain.StorageMetadata;
-import org.jclouds.domain.Credentials;
-import org.jclouds.openstack.domain.AuthenticationResponse;
-import org.jclouds.openstack.reference.AuthHeaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
-import org.apache.brooklyn.location.jclouds.JcloudsLocation;
-import org.apache.brooklyn.location.jclouds.JcloudsUtil;
import com.google.common.base.Preconditions;
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMap.Builder;
-import com.google.inject.Inject;
-@Test(groups={"Live", "Live-sanity"})
+// Disabled as `swift` is not supported in jclouds 2.0.0+
+@Test(groups={"Live", "Live-sanity"} , enabled = false)
public class BlobStoreExpiryTest {
private static final Logger log = LoggerFactory.getLogger(BlobStoreExpiryTest.class);
@@ -121,6 +100,10 @@ public class BlobStoreExpiryTest {
context = null;
}
+ /** FIXME
+ * see comment on this class
+ */
+ /*
public void testRenewAuthSucceedsInSwiftObjectStore() throws Exception {
doTestRenewAuth();
}
@@ -143,11 +126,6 @@ public class BlobStoreExpiryTest {
context.getBlobStore().deleteContainer(testContainerName);
}
- private void assertContainerFound() {
- PageSet<? extends StorageMetadata> ps = context.getBlobStore().list();
- BlobStoreTest.assertHasItemNamed(ps, testContainerName);
- }
-
private void injectShortLivedTokenForSwiftAuth() throws Exception {
URL endpointUrl = new URL(endpoint);
@@ -178,6 +156,11 @@ public class BlobStoreExpiryTest {
this.authenticationResponseCache = authenticationResponseCache;
}
}
+
+ private void assertContainerFound() {
+ PageSet<? extends StorageMetadata> ps = context.getBlobStore().list();
+ BlobStoreTest.assertHasItemNamed(ps, testContainerName);
+ }
public static HttpToolResponse requestTokenWithExplicitLifetime(URL url, String user, String key, Duration expiration) throws URISyntaxException {
HttpClient client = HttpTool.httpClientBuilder().build();
@@ -192,5 +175,6 @@ public class BlobStoreExpiryTest {
log.info("Requested token with explicit lifetime: "+expiration+" at "+url+"\n"+response+"\n"+response.getHeaderLists());
return response;
}
+ */
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2a0f3e56/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/DelegatingComputeService.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/DelegatingComputeService.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/DelegatingComputeService.java
index 8e718e2..b1d8006 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/DelegatingComputeService.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/DelegatingComputeService.java
@@ -87,7 +87,7 @@ public class DelegatingComputeService implements ComputeService {
}
@Override
- public Set<? extends NodeMetadata> listNodesByIds(Iterable<String> ids) {
+ public Set<? extends ComputeMetadata> listNodesByIds(Iterable<String> ids) {
return delegate.listNodesByIds(ids);
}
@@ -118,7 +118,7 @@ public class DelegatingComputeService implements ComputeService {
}
@Override
- public Set<? extends NodeMetadata> resumeNodesMatching(Predicate<NodeMetadata> filter) {
+ public Set<? extends NodeMetadata> resumeNodesMatching(Predicate<? super NodeMetadata> filter) {
return delegate.resumeNodesMatching(filter);
}
@@ -128,7 +128,7 @@ public class DelegatingComputeService implements ComputeService {
}
@Override
- public Set<? extends NodeMetadata> suspendNodesMatching(Predicate<NodeMetadata> filter) {
+ public Set<? extends NodeMetadata> suspendNodesMatching(Predicate<? super NodeMetadata> filter) {
return delegate.suspendNodesMatching(filter);
}
@@ -138,7 +138,7 @@ public class DelegatingComputeService implements ComputeService {
}
@Override
- public Set<? extends NodeMetadata> destroyNodesMatching(Predicate<NodeMetadata> filter) {
+ public Set<? extends NodeMetadata> destroyNodesMatching(Predicate<? super NodeMetadata> filter) {
return delegate.destroyNodesMatching(filter);
}
@@ -148,7 +148,7 @@ public class DelegatingComputeService implements ComputeService {
}
@Override
- public Set<? extends NodeMetadata> rebootNodesMatching(Predicate<NodeMetadata> filter) {
+ public Set<? extends NodeMetadata> rebootNodesMatching(Predicate<? super NodeMetadata> filter) {
return delegate.rebootNodesMatching(filter);
}
@@ -158,31 +158,27 @@ public class DelegatingComputeService implements ComputeService {
}
@Override
- public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<ComputeMetadata> filter) {
+ public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<? super NodeMetadata> filter) {
return delegate.listNodesDetailsMatching(filter);
}
@Override
- public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter, String runScript)
- throws RunScriptOnNodesException {
+ public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<? super NodeMetadata> filter, String runScript) throws RunScriptOnNodesException {
return delegate.runScriptOnNodesMatching(filter, runScript);
}
@Override
- public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter, Statement runScript)
- throws RunScriptOnNodesException {
+ public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<? super NodeMetadata> filter, Statement runScript) throws RunScriptOnNodesException {
return delegate.runScriptOnNodesMatching(filter, runScript);
}
@Override
- public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
- String runScript, RunScriptOptions options) throws RunScriptOnNodesException {
+ public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<? super NodeMetadata> filter, String runScript, RunScriptOptions options) throws RunScriptOnNodesException {
return delegate.runScriptOnNodesMatching(filter, runScript, options);
}
@Override
- public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
- Statement runScript, RunScriptOptions options) throws RunScriptOnNodesException {
+ public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<? super NodeMetadata> filter, Statement runScript, RunScriptOptions options) throws RunScriptOnNodesException {
return delegate.runScriptOnNodesMatching(filter, runScript, options);
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2a0f3e56/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedRebindTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedRebindTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedRebindTest.java
index 96b5786..56e8ccc 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedRebindTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedRebindTest.java
@@ -42,7 +42,6 @@ import org.apache.brooklyn.location.jclouds.StubbedComputeServiceRegistry.NodeCr
import org.apache.brooklyn.util.os.Os;
import org.apache.brooklyn.util.text.Identifiers;
import org.apache.brooklyn.util.time.Duration;
-import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.compute.domain.NodeMetadataBuilder;
@@ -118,7 +117,7 @@ public class JcloudsByonLocationResolverStubbedRebindTest extends AbstractJcloud
}
public static class NodeCreatorForRebinding extends AbstractNodeCreator {
@Override
- public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<ComputeMetadata> filter) {
+ public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<? super NodeMetadata> filter) {
NodeMetadata result = new NodeMetadataBuilder()
.id(nodeId)
.credentials(LoginCredentials.builder().identity("dummy").credential("dummy").build())
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2a0f3e56/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedTest.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedTest.java
index 13cf30c..8432905 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedTest.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolverStubbedTest.java
@@ -33,7 +33,6 @@ import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation;
import org.apache.brooklyn.location.jclouds.StubbedComputeServiceRegistry.AbstractNodeCreator;
import org.apache.brooklyn.location.jclouds.StubbedComputeServiceRegistry.NodeCreator;
-import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.compute.domain.NodeMetadataBuilder;
@@ -79,7 +78,7 @@ public class JcloudsByonLocationResolverStubbedTest extends AbstractJcloudsStubb
protected NodeCreator newNodeCreator() {
return new AbstractNodeCreator() {
@Override
- public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<ComputeMetadata> filter) {
+ public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<? super NodeMetadata> filter) {
NodeMetadata result = new NodeMetadataBuilder()
.id(nodeId)
.credentials(LoginCredentials.builder().identity("dummy").credential("dummy").build())
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2a0f3e56/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/StubbedComputeServiceRegistry.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/StubbedComputeServiceRegistry.java b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/StubbedComputeServiceRegistry.java
index 9dee5ef..e97f0ea 100644
--- a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/StubbedComputeServiceRegistry.java
+++ b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/StubbedComputeServiceRegistry.java
@@ -25,7 +25,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.brooklyn.util.core.config.ConfigBag;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.RunNodesException;
-import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.compute.domain.NodeMetadataBuilder;
@@ -44,7 +43,7 @@ public class StubbedComputeServiceRegistry implements ComputeServiceRegistry {
public static interface NodeCreator {
public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, Template template) throws RunNodesException;
public void destroyNode(String id);
- public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<ComputeMetadata> filter);
+ public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<? super NodeMetadata> filter);
}
public static abstract class AbstractNodeCreator implements NodeCreator {
@@ -66,7 +65,7 @@ public class StubbedComputeServiceRegistry implements ComputeServiceRegistry {
destroyed.add(id);
}
@Override
- public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<ComputeMetadata> filter) {
+ public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<? super NodeMetadata> filter) {
return ImmutableSet.of();
}
protected abstract NodeMetadata newNode(String group, Template template);
@@ -116,7 +115,7 @@ public class StubbedComputeServiceRegistry implements ComputeServiceRegistry {
nodeCreator.destroyNode(id);
}
@Override
- public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<ComputeMetadata> filter) {
+ public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate<? super NodeMetadata> filter) {
return nodeCreator.listNodesDetailsMatching(filter);
}
@Override
@@ -128,7 +127,7 @@ public class StubbedComputeServiceRegistry implements ComputeServiceRegistry {
throw new UnsupportedOperationException();
}
@Override
- public Set<? extends NodeMetadata> destroyNodesMatching(Predicate<NodeMetadata> filter) {
+ public Set<? extends NodeMetadata> destroyNodesMatching(Predicate<? super NodeMetadata> filter) {
throw new UnsupportedOperationException();
}
}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2a0f3e56/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 11b97a7..f0218cd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -72,6 +72,16 @@
</mailingList>
</mailingLists>
+ <repositories>
+ <repository>
+ <id>jclouds-snapshots</id>
+ <url>https://repository.apache.org/content/repositories/snapshots</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+
<properties>
<brooklyn.version>0.11.0-SNAPSHOT</brooklyn.version> <!-- BROOKLYN_VERSION -->
@@ -91,7 +101,7 @@
<surefire.failIfNoSpecifiedTests>false</surefire.failIfNoSpecifiedTests>
<!-- Dependency Versions -->
- <jclouds.version>1.9.3</jclouds.version> <!-- JCLOUDS_VERSION -->
+ <jclouds.version>2.0.0</jclouds.version> <!-- JCLOUDS_VERSION -->
<logback.version>1.0.7</logback.version>
<slf4j.version>1.6.6</slf4j.version> <!-- used for java.util.logging jul-to-slf4j interception -->
<!-- Must match jclouds' version. From jclouds 1.9.3+ can be any version in the range [16-20) -->