This is an automated email from the ASF dual-hosted git repository. johndament pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 82bf8edab63b8a20b82d2d4b4609a0d3f7b47daf Author: John D. Ament <[email protected]> AuthorDate: Tue Dec 5 20:42:48 2017 -0500 [CXF-7579] Begin the implementation of the MP Rest Client in CXF --- rt/rs/microprofile-client/pom.xml | 218 +++++++++++++++++++++ .../client/CxfTypeSafeClientBuilder.java | 117 +++++++++++ .../microprofile/client/cdi/RestClientBean.java | 153 +++++++++++++++ .../client/cdi/RestClientExtension.java | 53 +++++ .../client/spi/CxfRestClientBuilderResolver.java | 31 +++ .../services/javax.enterprise.inject.spi.Extension | 1 + ...ofile.rest.client.spi.RestClientBuilderResolver | 1 + rt/rs/pom.xml | 1 + 8 files changed, 575 insertions(+) diff --git a/rt/rs/microprofile-client/pom.xml b/rt/rs/microprofile-client/pom.xml new file mode 100644 index 0000000..a893fb9 --- /dev/null +++ b/rt/rs/microprofile-client/pom.xml @@ -0,0 +1,218 @@ +<?xml version="1.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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <artifactId>cxf-rt-rs-mp-client</artifactId> + <packaging>bundle</packaging> + <name>Apache CXF MicroProfile Client</name> + <description>Apache CXF MicroProfile Client</description> + <url>http://cxf.apache.org</url> + <parent> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-parent</artifactId> + <version>3.2.2-SNAPSHOT</version> + <relativePath>../../../parent/pom.xml</relativePath> + </parent> + <properties> + <cxf.osgi.import> + org.apache.aries*;version="${cxf.aries.version.range}";resolution:=optional, + org.springframework*;resolution:="optional";version="${cxf.osgi.spring.version}" + </cxf.osgi.import> + <!-- keep in sync with services exported in activator --> + <cxf.export.service> + org.apache.aries.blueprint.NamespaceHandler;osgi.service.blueprint.namespace="http://cxf.apache.org/blueprint/jaxrs-client" + </cxf.export.service> + <cxf.bundle.activator>org.apache.cxf.jaxrs.client.blueprint.Activator</cxf.bundle.activator> + </properties> + <dependencies> + <dependency> + <groupId>org.apache.aries.blueprint</groupId> + <artifactId>org.apache.aries.blueprint.core</artifactId> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.apache.aries.blueprint</groupId> + <artifactId>blueprint-parser</artifactId> + <scope>provided</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-transports-http</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-rs-client</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-transports-local</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-features-logging</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-frontend-jaxrs</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.microprofile.rest.client</groupId> + <artifactId>microprofile-rest-client-api</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.eclipse.microprofile.config</groupId> + <artifactId>microprofile-config-api</artifactId> + <version>1.1</version> + <scope>provided</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>javax.enterprise</groupId> + <artifactId>cdi-api</artifactId> + <version>1.2</version> + <scope>provided</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.apache.geronimo.specs</groupId> + <artifactId>geronimo-jcache_1.0_spec</artifactId> + <version>1.0-alpha-1</version> + <scope>provided</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-core</artifactId> + <scope>provided</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context</artifactId> + <scope>provided</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>cglib</groupId> + <artifactId>cglib-nodep</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${cxf.servlet-api.group}</groupId> + <artifactId>${cxf.servlet-api.artifact}</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>jcl-over-slf4j</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-jdk14</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-databinding-jaxb</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-jcs-jcache</artifactId> + <version>2.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.awaitility</groupId> + <artifactId>awaitility</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <profiles> + <profile> + <id>java9</id> + <activation> + <jdk>9</jdk> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy</id> + <phase>validate</phase> + <goals> + <goal>copy</goal> + </goals> + <configuration> + <artifactItems> + <artifactItem> + <groupId>javax.annotation</groupId> + <artifactId>javax.annotation-api</artifactId> + <type>jar</type> + <overWrite>false</overWrite> + <outputDirectory>${project.basedir}/target/java9</outputDirectory> + </artifactItem> + </artifactItems> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>true</overWriteSnapshots> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilder.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilder.java new file mode 100644 index 0000000..4e231aa --- /dev/null +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/CxfTypeSafeClientBuilder.java @@ -0,0 +1,117 @@ +/** + * 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.cxf.microprofile.client; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import javax.ws.rs.core.Configuration; +import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean; +import org.eclipse.microprofile.rest.client.RestClientBuilder; +import org.eclipse.microprofile.rest.client.annotation.RegisterProviders; + +public class CxfTypeSafeClientBuilder implements RestClientBuilder { + private String baseUri; + private Map<String, Object> properties; + private List<Object> jaxrsProviders; + + public CxfTypeSafeClientBuilder() { + this.properties = new HashMap<>(); + this.jaxrsProviders = new ArrayList<>(); + } + + @Override + public RestClientBuilder baseUrl(URL url) { + this.baseUri = Objects.requireNonNull(url).toExternalForm(); + return this; + } + + @Override + public <T> T build(Class<T> aClass) { + RegisterProviders providers = aClass.getAnnotation(RegisterProviders.class); + List<Object> providerClasses = new ArrayList<>(); + providerClasses.addAll(this.jaxrsProviders); + if (providers != null) { + providerClasses.addAll(Arrays.asList(providers.value())); + } + JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean(); + bean.setAddress(baseUri); + bean.setServiceClass(aClass); + bean.setProviders(providerClasses); + bean.setProperties(properties); + return bean.create(aClass); + } + + @Override + public Configuration getConfiguration() { + return null; + } + + @Override + public RestClientBuilder property(String s, Object o) { + this.properties.put(s, o); + return this; + } + + @Override + public RestClientBuilder register(Class<?> providerClass) { + this.jaxrsProviders.add(providerClass); + return this; + } + + @Override + public RestClientBuilder register(Class<?> aClass, int i) { + return this; + } + + @Override + public RestClientBuilder register(Class<?> aClass, Class<?>... classes) { + return this; + } + + @Override + public RestClientBuilder register(Class<?> aClass, Map<Class<?>, Integer> map) { + return this; + } + + @Override + public RestClientBuilder register(Object o) { + this.jaxrsProviders.add(o); + return this; + } + + @Override + public RestClientBuilder register(Object o, int i) { + return this; + } + + @Override + public RestClientBuilder register(Object o, Class<?>... classes) { + return this; + } + + @Override + public RestClientBuilder register(Object o, Map<Class<?>, Integer> map) { + return this; + } +} diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/RestClientBean.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/RestClientBean.java new file mode 100644 index 0000000..83f7cde --- /dev/null +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/RestClientBean.java @@ -0,0 +1,153 @@ +/** + * 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.cxf.microprofile.client.cdi; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import javax.enterprise.context.Dependent; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.PassivationCapable; +import org.apache.cxf.microprofile.client.CxfTypeSafeClientBuilder; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.rest.client.inject.RestClient; + +public class RestClientBean implements Bean<Object>, PassivationCapable { + public static final String REST_URL_FORMAT = "%s/mp-rest/url"; + public static final String REST_SCOPE_FORMAT = "%s/mp-rest/scope"; + private final Class<?> clientInterface; + private final Class<? extends Annotation> scope; + private final BeanManager beanManager; + private final Config config; + + RestClientBean(Class<?> clientInterface, BeanManager beanManager) { + this.clientInterface = clientInterface; + this.beanManager = beanManager; + this.config = ConfigProvider.getConfig(); + this.scope = this.readScope(); + } + @Override + public String getId() { + return clientInterface.getName(); + } + + @Override + public Class<?> getBeanClass() { + return clientInterface; + } + + @Override + public Set<InjectionPoint> getInjectionPoints() { + return Collections.emptySet(); + } + + @Override + public boolean isNullable() { + return false; + } + + @Override + public Object create(CreationalContext<Object> creationalContext) { + CxfTypeSafeClientBuilder builder = new CxfTypeSafeClientBuilder(); + String baseUrl = getBaseUrl(); + try { + return builder.baseUrl(new URL(baseUrl)).build(clientInterface); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("The value of URL was invalid " + baseUrl); + } + } + + @Override + public void destroy(Object instance, CreationalContext<Object> creationalContext) { + + } + + @Override + public Set<Type> getTypes() { + return Collections.singleton(clientInterface); + } + + @Override + public Set<Annotation> getQualifiers() { + return Collections.singleton(RestClient.LITERAL); + } + + @Override + public Class<? extends Annotation> getScope() { + return scope; + } + + @Override + public String getName() { + return clientInterface.getName(); + } + + @Override + public Set<Class<? extends Annotation>> getStereotypes() { + return Collections.emptySet(); + } + + @Override + public boolean isAlternative() { + return false; + } + + private String getBaseUrl() { + String property = String.format(REST_URL_FORMAT, clientInterface.getName()); + return config.getValue(property, String.class); + } + + private Class<? extends Annotation> readScope() { + // first check to see if the value is set + String property = String.format(REST_SCOPE_FORMAT, clientInterface.getName()); + String configuredScope = config.getOptionalValue(property, String.class).orElse(null); + if (configuredScope != null) { + try { + return (Class<? extends Annotation>)Class.forName(configuredScope); + } catch (Exception e) { + throw new IllegalArgumentException("The scope " + configuredScope + " is invalid", e); + } + } + + List<Annotation> possibleScopes = new ArrayList<>(); + Annotation[] annotations = clientInterface.getDeclaredAnnotations(); + for (Annotation annotation : annotations) { + if (beanManager.isScope(annotation.annotationType())) { + possibleScopes.add(annotation); + } + } + if (possibleScopes.isEmpty()) { + return Dependent.class; + } else if (possibleScopes.size() == 1) { + return possibleScopes.get(0).annotationType(); + } else { + throw new IllegalArgumentException("The client interface " + clientInterface + + " has multiple scopes defined " + possibleScopes); + } + } +} diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/RestClientExtension.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/RestClientExtension.java new file mode 100644 index 0000000..1d8f677 --- /dev/null +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/cdi/RestClientExtension.java @@ -0,0 +1,53 @@ +/** + * 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.cxf.microprofile.client.cdi; + +import java.util.LinkedHashSet; +import java.util.Set; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.AfterBeanDiscovery; +import javax.enterprise.inject.spi.AfterDeploymentValidation; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.Extension; +import javax.enterprise.inject.spi.ProcessAnnotatedType; +import javax.enterprise.inject.spi.WithAnnotations; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +public class RestClientExtension implements Extension { + private static Set<Class<?>> restClientClasses = new LinkedHashSet<>(); + private static Set<Throwable> errors = new LinkedHashSet<>(); + public void findClients(@Observes @WithAnnotations({RegisterRestClient.class}) ProcessAnnotatedType<?> pat) { + Class<?> restClient = pat.getAnnotatedType().getJavaClass(); + if (restClient.isInterface()) { + restClientClasses.add(restClient); + pat.veto(); + } else { + errors.add(new IllegalArgumentException("The class " + restClient + + " is not an interface")); + } + } + + public void registerClientBeans(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) { + restClientClasses.stream().map(c -> new RestClientBean(c, beanManager)).forEach(afterBeanDiscovery::addBean); + } + + public void registerErrors(@Observes AfterDeploymentValidation afterDeploymentValidation) { + errors.forEach(afterDeploymentValidation::addDeploymentProblem); + } +} diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/spi/CxfRestClientBuilderResolver.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/spi/CxfRestClientBuilderResolver.java new file mode 100644 index 0000000..cf7f612 --- /dev/null +++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/spi/CxfRestClientBuilderResolver.java @@ -0,0 +1,31 @@ +/** + * 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.cxf.microprofile.client.spi; + +import javax.enterprise.inject.Vetoed; +import org.apache.cxf.microprofile.client.CxfTypeSafeClientBuilder; +import org.eclipse.microprofile.rest.client.spi.RestClientBuilderResolver; + +@Vetoed +public class CxfRestClientBuilderResolver extends RestClientBuilderResolver { + @Override + public CxfTypeSafeClientBuilder newBuilder() { + return new CxfTypeSafeClientBuilder(); + } +} diff --git a/rt/rs/microprofile-client/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/rt/rs/microprofile-client/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension new file mode 100644 index 0000000..eadea9b --- /dev/null +++ b/rt/rs/microprofile-client/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension @@ -0,0 +1 @@ +org.apache.cxf.microprofile.client.cdi.RestClientExtension \ No newline at end of file diff --git a/rt/rs/microprofile-client/src/main/resources/META-INF/services/org.eclipse.microprofile.rest.client.spi.RestClientBuilderResolver b/rt/rs/microprofile-client/src/main/resources/META-INF/services/org.eclipse.microprofile.rest.client.spi.RestClientBuilderResolver new file mode 100644 index 0000000..bd7696b --- /dev/null +++ b/rt/rs/microprofile-client/src/main/resources/META-INF/services/org.eclipse.microprofile.rest.client.spi.RestClientBuilderResolver @@ -0,0 +1 @@ +org.apache.cxf.microprofile.client.spi.CxfRestClientBuilderResolver \ No newline at end of file diff --git a/rt/rs/pom.xml b/rt/rs/pom.xml index 9edd71c..4578434 100644 --- a/rt/rs/pom.xml +++ b/rt/rs/pom.xml @@ -44,5 +44,6 @@ <module>sse</module> <module>description-openapi-v3</module> <module>description-swagger-ui</module> + <module>microprofile-client</module> </modules> </project> -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
