mirror of https://github.com/jwtk/jjwt.git
Merge pull request #496 from jwtk/service-loader
Replace hardcoded class names and reflection with ServiceLoader Fixes: #458
This commit is contained in:
commit
c21c30a025
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken.lang;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* Helper class for loading services from the classpath, using a {@link ServiceLoader}. Decouples loading logic for
|
||||
* better separation of concerns and testability.
|
||||
*/
|
||||
public final class Services {
|
||||
|
||||
private Services() {}
|
||||
|
||||
/**
|
||||
* Loads and instantiates all service implementation of the given SPI class and returns them as a List.
|
||||
*
|
||||
* @param spi The class of the Service Provider Interface
|
||||
* @param <T> The type of the SPI
|
||||
* @return An unmodifiable list with an instance of all available implementations of the SPI. No guarantee is given
|
||||
* on the order of implementations, if more than one.
|
||||
*/
|
||||
public static <T> List<T> loadAll(Class<T> spi) {
|
||||
Assert.notNull(spi, "Parameter 'spi' must not be null.");
|
||||
ServiceLoader<T> serviceLoader = ServiceLoader.load(spi);
|
||||
|
||||
List<T> implementations = new ArrayList<>();
|
||||
|
||||
for (T implementation : serviceLoader) {
|
||||
implementations.add(implementation);
|
||||
}
|
||||
|
||||
// fail if no implementations were found
|
||||
if (implementations.isEmpty()) {
|
||||
throw new UnavailableImplementationException(spi);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(implementations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the first available implementation the given SPI class from the classpath. Uses the {@link ServiceLoader}
|
||||
* to find implementations. When multiple implementations are available it will return the first one that it
|
||||
* encounters. There is no guarantee with regard to ordering.
|
||||
*
|
||||
* @param spi The class of the Service Provider Interface
|
||||
* @param <T> The type of the SPI
|
||||
* @return A new instance of the service.
|
||||
* @throws UnavailableImplementationException When no implementation the SPI is available on the classpath.
|
||||
*/
|
||||
public static <T> T loadFirst(Class<T> spi) {
|
||||
Assert.notNull(spi, "Parameter 'spi' must not be null.");
|
||||
ServiceLoader<T> serviceLoader = ServiceLoader.load(spi);
|
||||
if (serviceLoader.iterator().hasNext()) {
|
||||
return serviceLoader.iterator().next();
|
||||
} else {
|
||||
throw new UnavailableImplementationException(spi);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken.lang;
|
||||
|
||||
/**
|
||||
* Exception indicating that no implementation of an jjwt-api SPI was found on the classpath.
|
||||
* @since 0.11.0
|
||||
*/
|
||||
public final class UnavailableImplementationException extends RuntimeException {
|
||||
|
||||
private static final String DEFAULT_NOT_FOUND_MESSAGE = "Unable to find an implementation for %s using java.util.ServiceLoader. Ensure you include a backing implementation .jar in the classpath, for example jjwt-impl.jar, or your own .jar for custom implementations.";
|
||||
|
||||
UnavailableImplementationException(final Class klass) {
|
||||
super(String.format(DEFAULT_NOT_FOUND_MESSAGE, klass));
|
||||
}
|
||||
}
|
|
@ -48,16 +48,17 @@ public class UnknownClassException extends RuntimeException {
|
|||
public UnknownClassException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructs a new UnknownClassException.
|
||||
*
|
||||
* @param message the reason for the exception
|
||||
* @param cause the underlying Throwable that caused this exception to be thrown.
|
||||
*
|
||||
*/
|
||||
public UnknownClassException(String message, Throwable cause) {
|
||||
// TODO: remove in v1.0, this constructor is only exposed to allow for backward compatible behavior
|
||||
super(message, cause);
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2014 jsonwebtoken.io
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -13,12 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl.io;
|
||||
package io.jsonwebtoken
|
||||
|
||||
/**
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public interface InstanceLocator<T> {
|
||||
|
||||
T getInstance();
|
||||
class DefaultStubService implements StubService {
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken
|
||||
|
||||
interface StubService {
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken.lang
|
||||
|
||||
import io.jsonwebtoken.DefaultStubService
|
||||
import io.jsonwebtoken.StubService
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest
|
||||
import org.powermock.modules.junit4.PowerMockRunner
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.assertNotNull
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest([Services])
|
||||
class ServicesTest {
|
||||
|
||||
@Test
|
||||
void testSuccessfulLoading() {
|
||||
def factory = Services.loadFirst(StubService)
|
||||
assertNotNull factory
|
||||
assertEquals(DefaultStubService, factory.class)
|
||||
}
|
||||
|
||||
@Test(expected = UnavailableImplementationException)
|
||||
void testLoadFirstUnavailable() {
|
||||
NoServicesClassLoader.runWith {
|
||||
Services.loadFirst(StubService.class)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = UnavailableImplementationException)
|
||||
void testLoadAllUnavailable() {
|
||||
NoServicesClassLoader.runWith {
|
||||
Services.loadAll(StubService.class)
|
||||
}
|
||||
}
|
||||
|
||||
static class NoServicesClassLoader extends ClassLoader {
|
||||
private NoServicesClassLoader(ClassLoader parent) {
|
||||
super(parent)
|
||||
}
|
||||
|
||||
@Override
|
||||
Enumeration<URL> getResources(String name) throws IOException {
|
||||
if (name.startsWith("META-INF/services/")) {
|
||||
return java.util.Collections.emptyEnumeration()
|
||||
} else {
|
||||
return super.getResources(name)
|
||||
}
|
||||
}
|
||||
|
||||
static void runWith(Closure closure) {
|
||||
ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader()
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(new NoServicesClassLoader(originalClassloader))
|
||||
closure.run()
|
||||
} finally {
|
||||
if (originalClassloader != null) {
|
||||
Thread.currentThread().setContextClassLoader(originalClassloader)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.DefaultStubService
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.gson.io.GsonDeserializer
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.gson.io.GsonSerializer
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.jackson.io.JacksonDeserializer
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.jackson.io.JacksonSerializer
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.orgjson.io.OrgJsonDeserializer
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.orgjson.io.OrgJsonSerializer
|
|
@ -24,14 +24,13 @@ import io.jsonwebtoken.JwtParser;
|
|||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.impl.crypto.DefaultJwtSigner;
|
||||
import io.jsonwebtoken.impl.crypto.JwtSigner;
|
||||
import io.jsonwebtoken.impl.io.InstanceLocator;
|
||||
import io.jsonwebtoken.impl.lang.LegacyServices;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.io.Encoder;
|
||||
import io.jsonwebtoken.io.Encoders;
|
||||
import io.jsonwebtoken.io.SerializationException;
|
||||
import io.jsonwebtoken.io.Serializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
import io.jsonwebtoken.security.InvalidKeyException;
|
||||
|
@ -295,10 +294,10 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
|||
public String compact() {
|
||||
|
||||
if (this.serializer == null) {
|
||||
//try to find one based on the runtime environment:
|
||||
InstanceLocator<Serializer<Map<String,?>>> locator =
|
||||
Classes.newInstance("io.jsonwebtoken.impl.io.RuntimeClasspathSerializerLocator");
|
||||
this.serializer = locator.getInstance();
|
||||
// try to find one based on the services available
|
||||
// TODO: This util class will throw a UnavailableImplementationException here to retain behavior of previous version, remove in v1.0
|
||||
// use the previous commented out line instead
|
||||
this.serializer = LegacyServices.loadFirst(Serializer.class);
|
||||
}
|
||||
|
||||
if (payload == null && Collections.isEmpty(claims)) {
|
||||
|
|
|
@ -39,13 +39,12 @@ import io.jsonwebtoken.UnsupportedJwtException;
|
|||
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver;
|
||||
import io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator;
|
||||
import io.jsonwebtoken.impl.crypto.JwtSignatureValidator;
|
||||
import io.jsonwebtoken.impl.io.InstanceLocator;
|
||||
import io.jsonwebtoken.impl.lang.LegacyServices;
|
||||
import io.jsonwebtoken.io.Decoder;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.io.DeserializationException;
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
import io.jsonwebtoken.lang.DateFormats;
|
||||
import io.jsonwebtoken.lang.Objects;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
@ -107,13 +106,6 @@ public class DefaultJwtParser implements JwtParser {
|
|||
this.base64UrlDecoder = base64UrlDecoder;
|
||||
this.deserializer = deserializer;
|
||||
this.compressionCodecResolver = compressionCodecResolver;
|
||||
|
||||
if (this.deserializer == null) {
|
||||
//try to find one based on the runtime environment:
|
||||
InstanceLocator<Deserializer<Map<String, ?>>> locator =
|
||||
Classes.newInstance("io.jsonwebtoken.impl.io.RuntimeClasspathDeserializerLocator");
|
||||
this.deserializer = locator.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -255,12 +247,12 @@ public class DefaultJwtParser implements JwtParser {
|
|||
@Override
|
||||
public Jwt parse(String jwt) throws ExpiredJwtException, MalformedJwtException, SignatureException {
|
||||
|
||||
// TODO move this to constructor before 1.0
|
||||
// TODO, this logic is only need for a now deprecated code path
|
||||
// remove this block in v1.0 (the equivalent is already in DefaultJwtParserBuilder)
|
||||
if (this.deserializer == null) {
|
||||
//try to find one based on the runtime environment:
|
||||
InstanceLocator<Deserializer<Map<String, ?>>> locator =
|
||||
Classes.newInstance("io.jsonwebtoken.impl.io.RuntimeClasspathDeserializerLocator");
|
||||
this.deserializer = locator.getInstance();
|
||||
// try to find one based on the services available
|
||||
// TODO: This util class will throw a UnavailableImplementationException here to retain behavior of previous version, remove in v1.0
|
||||
this.deserializer = LegacyServices.loadFirst(Deserializer.class);
|
||||
}
|
||||
|
||||
Assert.hasText(jwt, "JWT String argument cannot be null or empty.");
|
||||
|
|
|
@ -26,6 +26,8 @@ import io.jsonwebtoken.io.Decoder;
|
|||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Services;
|
||||
|
||||
import java.security.Key;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
@ -170,6 +172,14 @@ public class DefaultJwtParserBuilder implements JwtParserBuilder {
|
|||
|
||||
@Override
|
||||
public JwtParser build() {
|
||||
|
||||
// Only lookup the deserializer IF it is null. It is possible a Deserializer implementation was set
|
||||
// that is NOT exposed as a service and no other implementations are available for lookup.
|
||||
if (this.deserializer == null) {
|
||||
// try to find one based on the services available:
|
||||
this.deserializer = Services.loadFirst(Deserializer.class);
|
||||
}
|
||||
|
||||
return new ImmutableJwtParser(
|
||||
new DefaultJwtParser(signingKeyResolver,
|
||||
key,
|
||||
|
|
|
@ -17,11 +17,17 @@ package io.jsonwebtoken.impl.compression;
|
|||
|
||||
import io.jsonwebtoken.CompressionCodec;
|
||||
import io.jsonwebtoken.CompressionCodecResolver;
|
||||
import io.jsonwebtoken.CompressionCodecs;
|
||||
import io.jsonwebtoken.CompressionException;
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Services;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link CompressionCodecResolver} that supports the following:
|
||||
* <p>
|
||||
|
@ -45,6 +51,22 @@ import io.jsonwebtoken.lang.Strings;
|
|||
*/
|
||||
public class DefaultCompressionCodecResolver implements CompressionCodecResolver {
|
||||
|
||||
private static final String MISSING_COMPRESSION_MESSAGE = "Unable to find an implementation for compression algorithm [%s] using java.util.ServiceLoader. Ensure you include a backing implementation .jar in the classpath, for example jjwt-impl.jar, or your own .jar for custom implementations.";
|
||||
|
||||
private final Map<String, CompressionCodec> codecs;
|
||||
|
||||
public DefaultCompressionCodecResolver() {
|
||||
Map<String, CompressionCodec> codecMap = new HashMap<>();
|
||||
for (CompressionCodec codec : Services.loadAll(CompressionCodec.class)) {
|
||||
codecMap.put(codec.getAlgorithmName().toUpperCase(), codec);
|
||||
}
|
||||
|
||||
codecMap.put(CompressionCodecs.DEFLATE.getAlgorithmName().toUpperCase(), CompressionCodecs.DEFLATE);
|
||||
codecMap.put(CompressionCodecs.GZIP.getAlgorithmName().toUpperCase(), CompressionCodecs.GZIP);
|
||||
|
||||
codecs = Collections.unmodifiableMap(codecMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompressionCodec resolveCompressionCodec(Header header) {
|
||||
String cmpAlg = getAlgorithmFromHeader(header);
|
||||
|
@ -54,14 +76,7 @@ public class DefaultCompressionCodecResolver implements CompressionCodecResolver
|
|||
if (!hasCompressionAlgorithm) {
|
||||
return null;
|
||||
}
|
||||
if (io.jsonwebtoken.CompressionCodecs.DEFLATE.getAlgorithmName().equalsIgnoreCase(cmpAlg)) {
|
||||
return io.jsonwebtoken.CompressionCodecs.DEFLATE;
|
||||
}
|
||||
if (io.jsonwebtoken.CompressionCodecs.GZIP.getAlgorithmName().equalsIgnoreCase(cmpAlg)) {
|
||||
return io.jsonwebtoken.CompressionCodecs.GZIP;
|
||||
}
|
||||
|
||||
throw new CompressionException("Unsupported compression algorithm '" + cmpAlg + "'");
|
||||
return byName(cmpAlg);
|
||||
}
|
||||
|
||||
private String getAlgorithmFromHeader(Header header) {
|
||||
|
@ -69,4 +84,15 @@ public class DefaultCompressionCodecResolver implements CompressionCodecResolver
|
|||
|
||||
return header.getCompressionAlgorithm();
|
||||
}
|
||||
|
||||
private CompressionCodec byName(String name) {
|
||||
Assert.hasText(name, "'name' must not be empty");
|
||||
|
||||
CompressionCodec codec = codecs.get(name.toUpperCase());
|
||||
if (codec == null) {
|
||||
throw new CompressionException(String.format(MISSING_COMPRESSION_MESSAGE, name));
|
||||
}
|
||||
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken.impl.io;
|
||||
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public class RuntimeClasspathDeserializerLocator<T> implements InstanceLocator<Deserializer<T>> {
|
||||
|
||||
private static final AtomicReference<Deserializer> DESERIALIZER = new AtomicReference<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Deserializer<T> getInstance() {
|
||||
Deserializer<T> deserializer = DESERIALIZER.get();
|
||||
if (deserializer == null) {
|
||||
deserializer = locate();
|
||||
Assert.state(deserializer != null, "locate() cannot return null.");
|
||||
if (!compareAndSet(deserializer)) {
|
||||
deserializer = DESERIALIZER.get();
|
||||
}
|
||||
}
|
||||
Assert.state(deserializer != null, "deserializer cannot be null.");
|
||||
return deserializer;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") //to allow testing override
|
||||
protected Deserializer<T> locate() {
|
||||
if (isAvailable("io.jsonwebtoken.jackson.io.JacksonDeserializer")) {
|
||||
return Classes.newInstance("io.jsonwebtoken.jackson.io.JacksonDeserializer");
|
||||
} else if (isAvailable("io.jsonwebtoken.orgjson.io.OrgJsonDeserializer")) {
|
||||
return Classes.newInstance("io.jsonwebtoken.orgjson.io.OrgJsonDeserializer");
|
||||
} else if (isAvailable("io.jsonwebtoken.gson.io.GsonDeserializer")) {
|
||||
return Classes.newInstance("io.jsonwebtoken.gson.io.GsonDeserializer");
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to discover any JSON Deserializer implementations on the classpath.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") //to allow testing override
|
||||
protected boolean compareAndSet(Deserializer<T> d) {
|
||||
return DESERIALIZER.compareAndSet(null, d);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") //to allow testing override
|
||||
protected boolean isAvailable(String fqcn) {
|
||||
return Classes.isAvailable(fqcn);
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken.impl.io;
|
||||
|
||||
import io.jsonwebtoken.io.Serializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public class RuntimeClasspathSerializerLocator implements InstanceLocator<Serializer> {
|
||||
|
||||
private static final AtomicReference<Serializer<Object>> SERIALIZER = new AtomicReference<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Serializer<Object> getInstance() {
|
||||
Serializer<Object> serializer = SERIALIZER.get();
|
||||
if (serializer == null) {
|
||||
serializer = locate();
|
||||
Assert.state(serializer != null, "locate() cannot return null.");
|
||||
if (!compareAndSet(serializer)) {
|
||||
serializer = SERIALIZER.get();
|
||||
}
|
||||
}
|
||||
Assert.state(serializer != null, "serializer cannot be null.");
|
||||
return serializer;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") //to allow testing override
|
||||
protected Serializer<Object> locate() {
|
||||
if (isAvailable("io.jsonwebtoken.jackson.io.JacksonSerializer")) {
|
||||
return Classes.newInstance("io.jsonwebtoken.jackson.io.JacksonSerializer");
|
||||
} else if (isAvailable("io.jsonwebtoken.orgjson.io.OrgJsonSerializer")) {
|
||||
return Classes.newInstance("io.jsonwebtoken.orgjson.io.OrgJsonSerializer");
|
||||
} else if (isAvailable("io.jsonwebtoken.gson.io.GsonSerializer")) {
|
||||
return Classes.newInstance("io.jsonwebtoken.gson.io.GsonSerializer");
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to discover any JSON Serializer implementations on the classpath.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") //to allow testing override
|
||||
protected boolean compareAndSet(Serializer<Object> s) {
|
||||
return SERIALIZER.compareAndSet(null, s);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") //to allow testing override
|
||||
protected boolean isAvailable(String fqcn) {
|
||||
return Classes.isAvailable(fqcn);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package io.jsonwebtoken.impl.lang;
|
||||
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
import io.jsonwebtoken.lang.Services;
|
||||
import io.jsonwebtoken.lang.UnavailableImplementationException;
|
||||
import io.jsonwebtoken.lang.UnknownClassException;
|
||||
|
||||
/**
|
||||
* A backward compatibility {@link Services} utility to help migrate away from {@link Classes#newInstance(String)}.
|
||||
* TODO: remove before v1.0
|
||||
* @deprecated use {@link Services} directly
|
||||
*/
|
||||
@Deprecated
|
||||
public final class LegacyServices {
|
||||
|
||||
/**
|
||||
* Wraps {@code Services.loadFirst} and throws a {@link UnknownClassException} instead of a
|
||||
* {@link UnavailableImplementationException} to retain the previous behavior. This method should be used when
|
||||
* to retain the previous behavior of methods that throw an unchecked UnknownClassException.
|
||||
*/
|
||||
public static <T> T loadFirst(Class<T> spi) {
|
||||
try {
|
||||
return Services.loadFirst(spi);
|
||||
} catch (UnavailableImplementationException e) {
|
||||
throw new UnknownClassException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
io.jsonwebtoken.impl.compression.DeflateCompressionCodec
|
||||
io.jsonwebtoken.impl.compression.GzipCompressionCodec
|
|
@ -19,8 +19,9 @@ import io.jsonwebtoken.impl.DefaultHeader
|
|||
import io.jsonwebtoken.impl.DefaultJwsHeader
|
||||
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver
|
||||
import io.jsonwebtoken.impl.compression.GzipCompressionCodec
|
||||
import io.jsonwebtoken.impl.io.RuntimeClasspathSerializerLocator
|
||||
import io.jsonwebtoken.io.Encoders
|
||||
import io.jsonwebtoken.io.Serializer
|
||||
import io.jsonwebtoken.lang.Services
|
||||
import io.jsonwebtoken.lang.Strings
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import io.jsonwebtoken.security.WeakKeyException
|
||||
|
@ -43,7 +44,7 @@ class DeprecatedJwtsTest {
|
|||
}
|
||||
|
||||
protected static String toJson(o) {
|
||||
def serializer = new RuntimeClasspathSerializerLocator().getInstance()
|
||||
def serializer = Services.loadFirst(Serializer)
|
||||
byte[] bytes = serializer.serialize(o)
|
||||
return new String(bytes, Strings.UTF_8)
|
||||
}
|
||||
|
|
|
@ -19,8 +19,9 @@ import io.jsonwebtoken.impl.DefaultHeader
|
|||
import io.jsonwebtoken.impl.DefaultJwsHeader
|
||||
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver
|
||||
import io.jsonwebtoken.impl.compression.GzipCompressionCodec
|
||||
import io.jsonwebtoken.impl.io.RuntimeClasspathSerializerLocator
|
||||
import io.jsonwebtoken.io.Encoders
|
||||
import io.jsonwebtoken.io.Serializer
|
||||
import io.jsonwebtoken.lang.Services
|
||||
import io.jsonwebtoken.lang.Strings
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import io.jsonwebtoken.security.WeakKeyException
|
||||
|
@ -43,7 +44,7 @@ class JwtsTest {
|
|||
}
|
||||
|
||||
protected static String toJson(o) {
|
||||
def serializer = new RuntimeClasspathSerializerLocator().getInstance()
|
||||
def serializer = Services.loadFirst(Serializer)
|
||||
byte[] bytes = serializer.serialize(o)
|
||||
return new String(bytes, Strings.UTF_8)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken.impl.compression
|
||||
|
||||
import io.jsonwebtoken.CompressionCodec
|
||||
import io.jsonwebtoken.CompressionException
|
||||
import io.jsonwebtoken.impl.DefaultHeader
|
||||
import io.jsonwebtoken.impl.io.FakeServiceDescriptorClassLoader
|
||||
import io.jsonwebtoken.lang.Services
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
import io.jsonwebtoken.CompressionCodecs
|
||||
|
||||
import static org.hamcrest.CoreMatchers.hasItem
|
||||
import static org.hamcrest.CoreMatchers.instanceOf
|
||||
import static org.hamcrest.CoreMatchers.is
|
||||
import static org.hamcrest.CoreMatchers.nullValue
|
||||
import static org.hamcrest.MatcherAssert.assertThat
|
||||
|
||||
class DefaultCompressionCodecResolverTest {
|
||||
|
||||
@Test
|
||||
void resolveHeaderTest() {
|
||||
assertThat new DefaultCompressionCodecResolver().resolveCompressionCodec(
|
||||
new DefaultHeader()), nullValue()
|
||||
assertThat new DefaultCompressionCodecResolver().resolveCompressionCodec(
|
||||
new DefaultHeader().setCompressionAlgorithm("def")), is(CompressionCodecs.DEFLATE)
|
||||
assertThat new DefaultCompressionCodecResolver().resolveCompressionCodec(
|
||||
new DefaultHeader().setCompressionAlgorithm("gzip")), is(CompressionCodecs.GZIP)
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidCompressionNameTest() {
|
||||
try {
|
||||
new DefaultCompressionCodecResolver().resolveCompressionCodec(
|
||||
new DefaultHeader().setCompressionAlgorithm("expected-missing"))
|
||||
Assert.fail("Expected CompressionException to be thrown")
|
||||
} catch (CompressionException e) {
|
||||
assertThat e.message, is(String.format(DefaultCompressionCodecResolver.MISSING_COMPRESSION_MESSAGE, "expected-missing"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void overrideDefaultCompressionImplTest() {
|
||||
FakeServiceDescriptorClassLoader.runWithFake "io.jsonwebtoken.io.compression.CompressionCodec.test.override", {
|
||||
|
||||
// first make sure the service loader actually resolves the test class
|
||||
assertThat Services.loadAll(CompressionCodec), hasItem(instanceOf(YagCompressionCodec))
|
||||
|
||||
// now we know the class is loadable, make sure we ALWAYS return the GZIP impl
|
||||
assertThat new DefaultCompressionCodecResolver().byName("gzip"), instanceOf(GzipCompressionCodec)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyCompressionAlgInHeaderTest() {
|
||||
try {
|
||||
new DefaultCompressionCodecResolver().byName("")
|
||||
Assert.fail("Expected IllegalArgumentException to be thrown")
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken.impl.compression
|
||||
|
||||
import io.jsonwebtoken.CompressionCodec
|
||||
import io.jsonwebtoken.CompressionException
|
||||
|
||||
/**
|
||||
* Yet Another GZIP CompressionCodec. This codec has the same name as the Official GZIP impl. The DefaultCompressionCodecResolver will NOT resolve this class.
|
||||
*/
|
||||
class YagCompressionCodec implements CompressionCodec {
|
||||
|
||||
@Override
|
||||
String getAlgorithmName() {
|
||||
return new GzipCompressionCodec().getAlgorithmName();
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] compress(byte[] payload) throws CompressionException {
|
||||
return new byte[0]
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] decompress(byte[] compressed) throws CompressionException {
|
||||
return new byte[0]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken.impl.io
|
||||
|
||||
class FakeServiceDescriptorClassLoader extends ClassLoader {
|
||||
private String serviceDescriptor
|
||||
|
||||
FakeServiceDescriptorClassLoader(ClassLoader parent, String serviceDescriptor) {
|
||||
super(parent)
|
||||
this.serviceDescriptor = serviceDescriptor
|
||||
}
|
||||
|
||||
@Override
|
||||
Enumeration<URL> getResources(String name) throws IOException {
|
||||
if (name.startsWith("META-INF/services/")) {
|
||||
return super.getResources(serviceDescriptor)
|
||||
} else {
|
||||
return super.getResources(name)
|
||||
}
|
||||
}
|
||||
|
||||
static void runWithFake(String fakeDescriptor, Closure closure) {
|
||||
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader()
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(new FakeServiceDescriptorClassLoader(originalClassLoader, fakeDescriptor))
|
||||
closure.run()
|
||||
} finally {
|
||||
if(originalClassLoader != null) {
|
||||
Thread.currentThread().setContextClassLoader(originalClassLoader)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken.impl.io
|
||||
|
||||
class NoServiceDescriptorClassLoader extends ClassLoader {
|
||||
NoServiceDescriptorClassLoader(ClassLoader parent) {
|
||||
super(parent)
|
||||
}
|
||||
|
||||
@Override
|
||||
Enumeration<URL> getResources(String name) throws IOException {
|
||||
if (name.startsWith("META-INF/services/")) {
|
||||
return Collections.emptyEnumeration()
|
||||
} else {
|
||||
return super.getResources(name)
|
||||
}
|
||||
}
|
||||
|
||||
static void runWith(Closure closure) {
|
||||
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader()
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(new NoServiceDescriptorClassLoader(originalClassLoader))
|
||||
closure.run()
|
||||
} finally {
|
||||
if(originalClassLoader != null) {
|
||||
Thread.currentThread().setContextClassLoader(originalClassLoader)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken.impl.io
|
||||
|
||||
|
||||
import io.jsonwebtoken.io.Deserializer
|
||||
import io.jsonwebtoken.jackson.io.JacksonDeserializer
|
||||
import io.jsonwebtoken.orgjson.io.OrgJsonDeserializer
|
||||
import io.jsonwebtoken.gson.io.GsonDeserializer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
import static org.easymock.EasyMock.createMock
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class RuntimeClasspathDeserializerLocatorTest {
|
||||
|
||||
@Before
|
||||
void setUp() {
|
||||
RuntimeClasspathDeserializerLocator.DESERIALIZER.set(null)
|
||||
}
|
||||
|
||||
@After
|
||||
void teardown() {
|
||||
RuntimeClasspathDeserializerLocator.DESERIALIZER.set(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClassIsNotAvailable() {
|
||||
def locator = new RuntimeClasspathDeserializerLocator() {
|
||||
@Override
|
||||
protected boolean isAvailable(String fqcn) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
try {
|
||||
locator.getInstance()
|
||||
} catch (Exception ex) {
|
||||
assertEquals 'Unable to discover any JSON Deserializer implementations on the classpath.', ex.message
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompareAndSetFalse() {
|
||||
Deserializer deserializer = createMock(Deserializer)
|
||||
def locator = new RuntimeClasspathDeserializerLocator() {
|
||||
@Override
|
||||
protected boolean compareAndSet(Deserializer d) {
|
||||
RuntimeClasspathDeserializerLocator.DESERIALIZER.set(deserializer)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
def returned = locator.getInstance()
|
||||
assertSame deserializer, returned
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException)
|
||||
void testLocateReturnsNull() {
|
||||
def locator = new RuntimeClasspathDeserializerLocator() {
|
||||
@Override
|
||||
protected Deserializer locate() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
locator.getInstance()
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException)
|
||||
void testCompareAndSetFalseWithNullReturn() {
|
||||
def locator = new RuntimeClasspathDeserializerLocator() {
|
||||
@Override
|
||||
protected boolean compareAndSet(Deserializer d) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
locator.getInstance()
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJackson() {
|
||||
def deserializer = new RuntimeClasspathDeserializerLocator().getInstance()
|
||||
assertTrue deserializer instanceof JacksonDeserializer
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOrgJson() {
|
||||
def locator = new RuntimeClasspathDeserializerLocator() {
|
||||
@Override
|
||||
protected boolean isAvailable(String fqcn) {
|
||||
if (JacksonDeserializer.class.getName().equals(fqcn)) {
|
||||
return false; //skip it to allow the OrgJson impl to be created
|
||||
}
|
||||
return super.isAvailable(fqcn)
|
||||
}
|
||||
}
|
||||
|
||||
def deserializer = locator.getInstance()
|
||||
assertTrue deserializer instanceof OrgJsonDeserializer
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGson() {
|
||||
def locator = new RuntimeClasspathDeserializerLocator() {
|
||||
@Override
|
||||
protected boolean isAvailable(String fqcn) {
|
||||
if (JacksonDeserializer.class.getName().equals(fqcn)) {
|
||||
return false; //skip it to allow the Gson impl to be created
|
||||
}
|
||||
if (OrgJsonDeserializer.class.getName().equals(fqcn)) {
|
||||
return false; //skip it to allow the Gson impl to be created
|
||||
}
|
||||
return super.isAvailable(fqcn)
|
||||
}
|
||||
}
|
||||
|
||||
def deserializer = locator.getInstance()
|
||||
assertTrue deserializer instanceof GsonDeserializer
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken.impl.io
|
||||
|
||||
import io.jsonwebtoken.io.Serializer
|
||||
import io.jsonwebtoken.jackson.io.JacksonSerializer
|
||||
import io.jsonwebtoken.orgjson.io.OrgJsonSerializer
|
||||
import io.jsonwebtoken.gson.io.GsonSerializer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
import static org.easymock.EasyMock.createMock
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class RuntimeClasspathSerializerLocatorTest {
|
||||
|
||||
@Before
|
||||
void setUp() {
|
||||
RuntimeClasspathSerializerLocator.SERIALIZER.set(null)
|
||||
}
|
||||
|
||||
@After
|
||||
void teardown() {
|
||||
RuntimeClasspathSerializerLocator.SERIALIZER.set(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClassIsNotAvailable() {
|
||||
def locator = new RuntimeClasspathSerializerLocator() {
|
||||
@Override
|
||||
protected boolean isAvailable(String fqcn) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
try {
|
||||
locator.getInstance()
|
||||
} catch (Exception ex) {
|
||||
assertEquals 'Unable to discover any JSON Serializer implementations on the classpath.', ex.message
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompareAndSetFalse() {
|
||||
Serializer serializer = createMock(Serializer)
|
||||
def locator = new RuntimeClasspathSerializerLocator() {
|
||||
@Override
|
||||
protected boolean compareAndSet(Serializer s) {
|
||||
RuntimeClasspathSerializerLocator.SERIALIZER.set(serializer)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
def returned = locator.getInstance()
|
||||
assertSame serializer, returned
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException)
|
||||
void testLocateReturnsNull() {
|
||||
def locator = new RuntimeClasspathSerializerLocator() {
|
||||
@Override
|
||||
protected Serializer<Object> locate() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
locator.getInstance()
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException)
|
||||
void testCompareAndSetFalseWithNullReturn() {
|
||||
def locator = new RuntimeClasspathSerializerLocator() {
|
||||
@Override
|
||||
protected boolean compareAndSet(Serializer<Object> s) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
locator.getInstance()
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJackson() {
|
||||
def serializer = new RuntimeClasspathSerializerLocator().getInstance()
|
||||
assertTrue serializer instanceof JacksonSerializer
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOrgJson() {
|
||||
def locator = new RuntimeClasspathSerializerLocator() {
|
||||
@Override
|
||||
protected boolean isAvailable(String fqcn) {
|
||||
if (JacksonSerializer.class.getName().equals(fqcn)) {
|
||||
return false //skip it to allow the OrgJson impl to be created
|
||||
}
|
||||
return super.isAvailable(fqcn)
|
||||
}
|
||||
}
|
||||
|
||||
def serializer = locator.getInstance()
|
||||
assertTrue serializer instanceof OrgJsonSerializer
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGson() {
|
||||
def locator = new RuntimeClasspathSerializerLocator() {
|
||||
@Override
|
||||
protected boolean isAvailable(String fqcn) {
|
||||
if (JacksonSerializer.class.getName().equals(fqcn)) {
|
||||
return false //skip it to allow the Gson impl to be created
|
||||
}
|
||||
if (OrgJsonSerializer.class.getName().equals(fqcn)) {
|
||||
return false //skip it to allow the Gson impl to be created
|
||||
}
|
||||
return super.isAvailable(fqcn)
|
||||
}
|
||||
}
|
||||
|
||||
def serializer = locator.getInstance()
|
||||
assertTrue serializer instanceof GsonSerializer
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.jsonwebtoken.impl.lang
|
||||
|
||||
import io.jsonwebtoken.lang.UnknownClassException
|
||||
import org.junit.Test
|
||||
|
||||
class LegacyServicesTest {
|
||||
|
||||
@Test(expected = UnknownClassException)
|
||||
void serviceNotFoundTest() {
|
||||
// try to load a class that will NOT have any services, i.e. this test class.
|
||||
LegacyServices.loadFirst(LegacyServicesTest)
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.gson.io.GsonDeserializer
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.orgjson.io.OrgJsonDeserializer
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.gson.io.GsonSerializer
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.orgjson.io.OrgJsonSerializer
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.impl.compression.YagCompressionCodec
|
Loading…
Reference in New Issue