Skip to content

Commit

Permalink
added jaxb-jakarta module on the basis of jaxb module (#2014)
Browse files Browse the repository at this point in the history
  • Loading branch information
FloLei authored Apr 14, 2023
1 parent 29c25f6 commit 9b01564
Show file tree
Hide file tree
Showing 18 changed files with 1,409 additions and 0 deletions.
63 changes: 63 additions & 0 deletions jaxb-jakarta/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2012-2023 The Feign Authors
Licensed 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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.github.openfeign</groupId>
<artifactId>parent</artifactId>
<version>12.4-SNAPSHOT</version>
</parent>

<artifactId>feign-jaxb-jakarta</artifactId>
<name>Feign JAXB Jakarta</name>
<description>Feign JAXB Jakarta</description>

<properties>
<main.basedir>${project.basedir}/..</main.basedir>
</properties>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>feign-core</artifactId>
</dependency>

<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>feign-core</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.0</version>
</dependency>

<!-- Test -->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
17 changes: 17 additions & 0 deletions jaxb-jakarta/src/main/java/feign/jaxb/JAXBContextCacheKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2012-2023 The Feign Authors
*
* Licensed 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 feign.jaxb;

/** Encapsulate data used to build the cache key of JAXBContext. */
interface JAXBContextCacheKey {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2012-2023 The Feign Authors
*
* Licensed 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 feign.jaxb;

import java.util.Objects;

/** Encapsulate data used to build the cache key of JAXBContext when created using class mode. */
final class JAXBContextClassCacheKey implements JAXBContextCacheKey {

private final Class<?> clazz;

JAXBContextClassCacheKey(Class<?> clazz) {
this.clazz = clazz;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
JAXBContextClassCacheKey that = (JAXBContextClassCacheKey) o;
return clazz.equals(that.clazz);
}

@Override
public int hashCode() {
return Objects.hash(clazz);
}
}
177 changes: 177 additions & 0 deletions jaxb-jakarta/src/main/java/feign/jaxb/JAXBContextFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* Copyright 2012-2023 The Feign Authors
*
* Licensed 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 feign.jaxb;

import jakarta.xml.bind.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

/**
* Creates and caches JAXB contexts as well as creates Marshallers and Unmarshallers for each
* context. Since JAXB contexts creation can be an expensive task, JAXB context can be preloaded on
* factory creation otherwise they will be created and cached dynamically when needed.
*/
public final class JAXBContextFactory {

private final ConcurrentHashMap<JAXBContextCacheKey, JAXBContext> jaxbContexts =
new ConcurrentHashMap<>(64);
private final Map<String, Object> properties;
private final JAXBContextInstantationMode jaxbContextInstantationMode;

private JAXBContextFactory(
Map<String, Object> properties, JAXBContextInstantationMode jaxbContextInstantationMode) {
this.properties = properties;
this.jaxbContextInstantationMode = jaxbContextInstantationMode;
}

/** Creates a new {@link jakarta.xml.bind.Unmarshaller} that handles the supplied class. */
public Unmarshaller createUnmarshaller(Class<?> clazz) throws JAXBException {
return getContext(clazz).createUnmarshaller();
}

/** Creates a new {@link jakarta.xml.bind.Marshaller} that handles the supplied class. */
public Marshaller createMarshaller(Class<?> clazz) throws JAXBException {
Marshaller marshaller = getContext(clazz).createMarshaller();
setMarshallerProperties(marshaller);
return marshaller;
}

private void setMarshallerProperties(Marshaller marshaller) throws PropertyException {
for (Entry<String, Object> en : properties.entrySet()) {
marshaller.setProperty(en.getKey(), en.getValue());
}
}

private JAXBContext getContext(Class<?> clazz) throws JAXBException {
JAXBContextCacheKey cacheKey = jaxbContextInstantationMode.getJAXBContextCacheKey(clazz);
JAXBContext jaxbContext = this.jaxbContexts.get(cacheKey);

if (jaxbContext == null) {
jaxbContext = jaxbContextInstantationMode.getJAXBContext(clazz);
this.jaxbContexts.putIfAbsent(cacheKey, jaxbContext);
}
return jaxbContext;
}

/**
* Will preload factory's cache with JAXBContext for provided classes
*
* @param classes
* @throws jakarta.xml.bind.JAXBException
*/
private void preloadContextCache(List<Class<?>> classes) throws JAXBException {
if (classes != null && !classes.isEmpty()) {
for (Class<?> clazz : classes) {
getContext(clazz);
}
}
}

/** Creates instances of {@link JAXBContextFactory}. */
public static class Builder {

private final Map<String, Object> properties = new HashMap<>(10);

private JAXBContextInstantationMode jaxbContextInstantationMode =
JAXBContextInstantationMode.CLASS;

/** Sets the jaxb.encoding property of any Marshaller created by this factory. */
public Builder withMarshallerJAXBEncoding(String value) {
properties.put(Marshaller.JAXB_ENCODING, value);
return this;
}

/** Sets the jaxb.schemaLocation property of any Marshaller created by this factory. */
public Builder withMarshallerSchemaLocation(String value) {
properties.put(Marshaller.JAXB_SCHEMA_LOCATION, value);
return this;
}

/**
* Sets the jaxb.noNamespaceSchemaLocation property of any Marshaller created by this factory.
*/
public Builder withMarshallerNoNamespaceSchemaLocation(String value) {
properties.put(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, value);
return this;
}

/** Sets the jaxb.formatted.output property of any Marshaller created by this factory. */
public Builder withMarshallerFormattedOutput(Boolean value) {
properties.put(Marshaller.JAXB_FORMATTED_OUTPUT, value);
return this;
}

/** Sets the jaxb.fragment property of any Marshaller created by this factory. */
public Builder withMarshallerFragment(Boolean value) {
properties.put(Marshaller.JAXB_FRAGMENT, value);
return this;
}

/**
* Sets the given property of any Marshaller created by this factory.
*
* <p>Example : <br>
* <br>
* <code>
* new JAXBContextFactory.Builder()
* .withProperty("com.sun.xml.internal.bind.xmlHeaders", "&lt;!DOCTYPE Example SYSTEM \&quot;example.dtd\&quot;&gt;")
* .build();
* </code>
*/
public Builder withProperty(String key, Object value) {
properties.put(key, value);
return this;
}

/**
* Provide an instantiation mode for JAXB Contexts, can be class or package, default is class if
* this method is not called.
*
* <p>Example : <br>
* <br>
* <code>
* new JAXBContextFactory.Builder()
* .withJAXBContextInstantiationMode(JAXBContextInstantationMode.PACKAGE)
* .build();
* </code>
*/
public Builder withJAXBContextInstantiationMode(
JAXBContextInstantationMode jaxbContextInstantiationMode) {
this.jaxbContextInstantationMode = jaxbContextInstantiationMode;
return this;
}

/** Creates a new {@link JAXBContextFactory} instance with a lazy loading cached context */
public JAXBContextFactory build() {
return new JAXBContextFactory(properties, jaxbContextInstantationMode);
}

/**
* Creates a new {@link JAXBContextFactory} instance. Pre-loads context cache with given classes
*
* @param classes
* @return ContextFactory with a pre-populated JAXBContext cache
* @throws jakarta.xml.bind.JAXBException if provided classes can't be used for JAXBContext
* generation most likely due to missing JAXB annotations
*/
public JAXBContextFactory build(List<Class<?>> classes) throws JAXBException {
JAXBContextFactory factory = new JAXBContextFactory(properties, jaxbContextInstantationMode);
factory.preloadContextCache(classes);
return factory;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2012-2023 The Feign Authors
*
* Licensed 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 feign.jaxb;

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;

/** Provides differents ways to instantiate a JAXB Context. */
public enum JAXBContextInstantationMode {
CLASS {
@Override
JAXBContextCacheKey getJAXBContextCacheKey(Class<?> clazz) {
return new JAXBContextClassCacheKey(clazz);
}

@Override
JAXBContext getJAXBContext(Class<?> clazz) throws JAXBException {
return JAXBContext.newInstance(clazz);
}
},

PACKAGE {
@Override
JAXBContextCacheKey getJAXBContextCacheKey(Class<?> clazz) {
return new JAXBContextPackageCacheKey(clazz.getPackage().getName(), clazz.getClassLoader());
}

@Override
JAXBContext getJAXBContext(Class<?> clazz) throws JAXBException {
return JAXBContext.newInstance(clazz.getPackage().getName(), clazz.getClassLoader());
}
};

abstract JAXBContextCacheKey getJAXBContextCacheKey(Class<?> clazz);

abstract JAXBContext getJAXBContext(Class<?> clazz) throws JAXBException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2012-2023 The Feign Authors
*
* Licensed 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 feign.jaxb;

import java.util.Objects;

/** Encapsulate data used to build the cache key of JAXBContext when created using package mode. */
final class JAXBContextPackageCacheKey implements JAXBContextCacheKey {

private final String packageName;

private final ClassLoader classLoader;

JAXBContextPackageCacheKey(String packageName, ClassLoader classLoader) {
this.packageName = packageName;
this.classLoader = classLoader;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
JAXBContextPackageCacheKey that = (JAXBContextPackageCacheKey) o;
return packageName.equals(that.packageName) && classLoader.equals(that.classLoader);
}

@Override
public int hashCode() {
return Objects.hash(packageName, classLoader);
}
}
Loading

0 comments on commit 9b01564

Please sign in to comment.