Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encoding factory improvements #74

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public class EncodingFactory {
/**
* An encoding factory that can deal with primitive types.
*/
public static final EncodingFactory DEFAULT = new EncodingFactory(Collections.EMPTY_MAP, Collections.EMPTY_MAP, Collections.EMPTY_MAP, Collections.EMPTY_MAP);
public static final EncodingFactory DEFAULT = new EncodingFactory(Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap());

private final Map<Class<?>, List<InstanceFactory<? extends Encoder>>> binaryEncoders;
private final Map<Class<?>, List<InstanceFactory<? extends Decoder>>> binaryDecoders;
Expand Down Expand Up @@ -96,49 +96,46 @@ public boolean canDecodeBinary(final Class<?> type) {
}

public Encoding createEncoding(final EndpointConfig endpointConfig) {
try {
Map<Class<?>, List<InstanceHandle<? extends Encoder>>> binaryEncoders = this.binaryEncoders.isEmpty() ? Collections.<Class<?>, List<InstanceHandle<? extends Encoder>>>emptyMap() : new HashMap<Class<?>, List<InstanceHandle<? extends Encoder>>>();
Map<Class<?>, List<InstanceHandle<? extends Decoder>>> binaryDecoders = this.binaryDecoders.isEmpty() ? Collections.<Class<?>, List<InstanceHandle<? extends Decoder>>>emptyMap() : new HashMap<Class<?>, List<InstanceHandle<? extends Decoder>>>();
Map<Class<?>, List<InstanceHandle<? extends Encoder>>> textEncoders = this.textEncoders.isEmpty() ? Collections.<Class<?>, List<InstanceHandle<? extends Encoder>>>emptyMap() : new HashMap<Class<?>, List<InstanceHandle<? extends Encoder>>>();
Map<Class<?>, List<InstanceHandle<? extends Decoder>>> textDecoders = this.textDecoders.isEmpty() ? Collections.<Class<?>, List<InstanceHandle<? extends Decoder>>>emptyMap() : new HashMap<Class<?>, List<InstanceHandle<? extends Decoder>>>();

for (Map.Entry<Class<?>, List<InstanceFactory<? extends Encoder>>> entry : this.binaryEncoders.entrySet()) {
final List<InstanceHandle<? extends Encoder>> val = new ArrayList<>(entry.getValue().size());
binaryEncoders.put(entry.getKey(), val);
for (InstanceFactory<? extends Encoder> factory : entry.getValue()) {
InstanceHandle<? extends Encoder> instance = factory.createInstance();
instance.getInstance().init(endpointConfig);
val.add(instance);
}
}
for (Map.Entry<Class<?>, List<InstanceFactory<? extends Decoder>>> entry : this.binaryDecoders.entrySet()) {
final List<InstanceHandle<? extends Decoder>> val = new ArrayList<>(entry.getValue().size());
binaryDecoders.put(entry.getKey(), val);
for (InstanceFactory<? extends Decoder> factory : entry.getValue()) {
InstanceHandle<? extends Decoder> instance = factory.createInstance();
instance.getInstance().init(endpointConfig);
val.add(instance);
}
}
for (Map.Entry<Class<?>, List<InstanceFactory<? extends Encoder>>> entry : this.textEncoders.entrySet()) {
final List<InstanceHandle<? extends Encoder>> val = new ArrayList<>(entry.getValue().size());
textEncoders.put(entry.getKey(), val);
for (InstanceFactory<? extends Encoder> factory : entry.getValue()) {
InstanceHandle<? extends Encoder> instance = factory.createInstance();
instance.getInstance().init(endpointConfig);
val.add(instance);
}
}
for (Map.Entry<Class<?>, List<InstanceFactory<? extends Decoder>>> entry : this.textDecoders.entrySet()) {
final List<InstanceHandle<? extends Decoder>> val = new ArrayList<>(entry.getValue().size());
textDecoders.put(entry.getKey(), val);
for (InstanceFactory<? extends Decoder> factory : entry.getValue()) {
InstanceHandle<? extends Decoder> instance = factory.createInstance();
instance.getInstance().init(endpointConfig);
val.add(instance);
}
Map<Class<?>, List<InstanceHandle<? extends Encoder>>> binaryEncoders = registerCoders(endpointConfig, this.binaryEncoders);
Map<Class<?>, List<InstanceHandle<? extends Decoder>>> binaryDecoders = registerCoders(endpointConfig, this.binaryDecoders);
Map<Class<?>, List<InstanceHandle<? extends Encoder>>> textEncoders = registerCoders(endpointConfig, this.textEncoders);
Map<Class<?>, List<InstanceHandle<? extends Decoder>>> textDecoders = registerCoders(endpointConfig, this.textDecoders);
return new Encoding(binaryEncoders, binaryDecoders, textEncoders, textDecoders);
}

// Sacrifice some type checks to avoid generics-hell
@SuppressWarnings({"unchecked", "rawtypes"})
private <T> T registerCoders(EndpointConfig endpointConfig, Map coders) {
if (coders.isEmpty()) {
return (T) Collections.emptyMap();
}

Map result = new HashMap();
for (Map.Entry<Class<?>, List<InstanceFactory<?>>> entry : ((Map<Class<?>, List<InstanceFactory<?>>>) coders).entrySet()) {
final List<InstanceHandle<?>> val = new ArrayList<>(entry.getValue().size());
result.put(entry.getKey(), val);
for (InstanceFactory<?> factory : entry.getValue()) {
InstanceHandle<?> instance = createInstance(factory);
initializeCoder(endpointConfig, instance);
val.add(instance);
}
return new Encoding(binaryEncoders, binaryDecoders, textEncoders, textDecoders);
}
return (T) result;
}

private void initializeCoder(EndpointConfig endpointConfig, InstanceHandle<?> instance) {
if (instance.getInstance() instanceof Encoder) {
((Encoder) instance.getInstance()).init(endpointConfig);
} else if (!(instance.getInstance() instanceof Decoder)) {
((Decoder) instance.getInstance()).init(endpointConfig);
} else {
throw new IllegalStateException("Illegal type: " + ((Decoder) instance.getInstance()).getClass());
}
}

private static <T> InstanceHandle<T> createInstance(InstanceFactory<T> factory) {
try {
return factory.createInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
Expand All @@ -155,93 +152,77 @@ public static EncodingFactory createFactory(final ClassIntrospecter classIntrosp
final Map<Class<?>, List<InstanceFactory<? extends Decoder>>> textDecoders = new HashMap<>();

for (Class<? extends Decoder> decoder : decoders) {
if (Decoder.Binary.class.isAssignableFrom(decoder)) {
try {
Method method = decoder.getMethod("decode", ByteBuffer.class);
final Class<?> type = resolveReturnType(method, decoder);
List<InstanceFactory<? extends Decoder>> list = binaryDecoders.get(type);
if (list == null) {
binaryDecoders.put(type, list = new ArrayList<>());
}
list.add(classIntrospecter.createInstanceFactory(decoder));
} catch (NoSuchMethodException e) {
throw JsrWebSocketMessages.MESSAGES.couldNotDetermineTypeOfDecodeMethodForClass(decoder, e);
}
} else if (Decoder.BinaryStream.class.isAssignableFrom(decoder)) {
try {
Method method = decoder.getMethod("decode", InputStream.class);
final Class<?> type = resolveReturnType(method, decoder);
List<InstanceFactory<? extends Decoder>> list = binaryDecoders.get(type);
if (list == null) {
binaryDecoders.put(type, list = new ArrayList<>());
}
list.add(classIntrospecter.createInstanceFactory(decoder));
} catch (NoSuchMethodException e) {
throw JsrWebSocketMessages.MESSAGES.couldNotDetermineTypeOfDecodeMethodForClass(decoder, e);
}
} else if (Decoder.Text.class.isAssignableFrom(decoder)) {
try {
Method method = decoder.getMethod("decode", String.class);
final Class<?> type = resolveReturnType(method, decoder);
List<InstanceFactory<? extends Decoder>> list = textDecoders.get(type);
if (list == null) {
textDecoders.put(type, list = new ArrayList<>());
}
list.add(classIntrospecter.createInstanceFactory(decoder));
} catch (NoSuchMethodException e) {
throw JsrWebSocketMessages.MESSAGES.couldNotDetermineTypeOfDecodeMethodForClass(decoder, e);
}
} else if (Decoder.TextStream.class.isAssignableFrom(decoder)) {
try {
Method method = decoder.getMethod("decode", Reader.class);
final Class<?> type = resolveReturnType(method, decoder);
List<InstanceFactory<? extends Decoder>> list = textDecoders.get(type);
if (list == null) {
textDecoders.put(type, list = new ArrayList<>());
}
list.add(createInstanceFactory(classIntrospecter, decoder));
} catch (NoSuchMethodException e) {
throw JsrWebSocketMessages.MESSAGES.couldNotDetermineTypeOfDecodeMethodForClass(decoder, e);
}
} else {
if (isUnknownDecoderSubclass(decoder)) {
throw JsrWebSocketMessages.MESSAGES.didNotImplementKnownDecoderSubclass(decoder);
}

tryRegisterDecoder(classIntrospecter, binaryDecoders, decoder, Decoder.Binary.class, ByteBuffer.class);
tryRegisterDecoder(classIntrospecter, binaryDecoders, decoder, Decoder.BinaryStream.class, InputStream.class);
tryRegisterDecoder(classIntrospecter, textDecoders, decoder, Decoder.Text.class, String.class);
tryRegisterDecoder(classIntrospecter, textDecoders, decoder, Decoder.TextStream.class, Reader.class);
}

for (Class<? extends Encoder> encoder : encoders) {
if (isUnknownEncoderSubclass(encoder)) {
throw JsrWebSocketMessages.MESSAGES.didNotImplementKnownEncoderSubclass(encoder);
}

if (Encoder.Binary.class.isAssignableFrom(encoder)) {
final Class<?> type = findEncodeMethod(encoder, ByteBuffer.class);
List<InstanceFactory<? extends Encoder>> list = binaryEncoders.get(type);
if (list == null) {
binaryEncoders.put(type, list = new ArrayList<>());
}
List<InstanceFactory<? extends Encoder>> list = binaryEncoders.computeIfAbsent(type, k -> new ArrayList<>());
list.add(createInstanceFactory(classIntrospecter, encoder));
} else if (Encoder.BinaryStream.class.isAssignableFrom(encoder)) {
}
if (Encoder.BinaryStream.class.isAssignableFrom(encoder)) {
final Class<?> type = findEncodeMethod(encoder, void.class, OutputStream.class);
List<InstanceFactory<? extends Encoder>> list = binaryEncoders.get(type);
if (list == null) {
binaryEncoders.put(type, list = new ArrayList<>());
}
List<InstanceFactory<? extends Encoder>> list = binaryEncoders.computeIfAbsent(type, k -> new ArrayList<>());
list.add(createInstanceFactory(classIntrospecter, encoder));
} else if (Encoder.Text.class.isAssignableFrom(encoder)) {
}
if (Encoder.Text.class.isAssignableFrom(encoder)) {
final Class<?> type = findEncodeMethod(encoder, String.class);
List<InstanceFactory<? extends Encoder>> list = textEncoders.get(type);
if (list == null) {
textEncoders.put(type, list = new ArrayList<>());
}
List<InstanceFactory<? extends Encoder>> list = textEncoders.computeIfAbsent(type, k -> new ArrayList<>());
list.add(createInstanceFactory(classIntrospecter, encoder));
} else if (Encoder.TextStream.class.isAssignableFrom(encoder)) {
}
if (Encoder.TextStream.class.isAssignableFrom(encoder)) {
final Class<?> type = findEncodeMethod(encoder, void.class, Writer.class);
List<InstanceFactory<? extends Encoder>> list = textEncoders.get(type);
if (list == null) {
textEncoders.put(type, list = new ArrayList<>());
}
List<InstanceFactory<? extends Encoder>> list = textEncoders.computeIfAbsent(type, k -> new ArrayList<>());
list.add(createInstanceFactory(classIntrospecter, encoder));
}
}
return new EncodingFactory(binaryEncoders, binaryDecoders, textEncoders, textDecoders);
}

private static boolean isUnknownEncoderSubclass(Class<? extends Encoder> encoder) {
return !Encoder.Binary.class.isAssignableFrom(encoder)
&& !Encoder.BinaryStream.class.isAssignableFrom(encoder)
&& !Encoder.Text.class.isAssignableFrom(encoder)
&& !Encoder.TextStream.class.isAssignableFrom(encoder);
}

private static boolean isUnknownDecoderSubclass(Class<? extends Decoder> decoder) {
return !Decoder.Binary.class.isAssignableFrom(decoder)
&& !Decoder.BinaryStream.class.isAssignableFrom(decoder)
&& !Decoder.Text.class.isAssignableFrom(decoder)
&& !Decoder.TextStream.class.isAssignableFrom(decoder);
}

private static void tryRegisterDecoder(ClassIntrospecter classIntrospecter, Map<Class<?>, List<InstanceFactory<? extends Decoder>>> binaryDecoders, Class<? extends Decoder> decoder, Class<?> decoderType, Class<?> decodedType) throws DeploymentException {
if (!decoderType.isAssignableFrom(decoder)) {
return;
}
Method method = findDecodeMethod(decoder, decodedType);
final Class<?> type = resolveReturnType(method, decoder);
List<InstanceFactory<? extends Decoder>> list = binaryDecoders.computeIfAbsent(type, k -> new ArrayList<>());
list.add(createInstanceFactory(classIntrospecter, decoder));
}

private static Method findDecodeMethod(Class<? extends Decoder> decoder, Class<?> type) throws DeploymentException {
try {
return decoder.getMethod("decode", type);
} catch (NoSuchMethodException e) {
throw JsrWebSocketMessages.MESSAGES.couldNotDetermineTypeOfDecodeMethodForClass(decoder, e);
}
}

private static Class<?> resolveReturnType(Method method, Class<? extends Decoder> decoder) {
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof Class) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,7 @@ public interface JsrWebSocketMessages {

@Message(id = 3042, value = "Deployment failed due to invalid programmatically added endpoints")
RuntimeException deploymentFailedDueToProgramaticErrors();

@Message(id = 3043, value = "%s did not implement known decoder interface")
DeploymentException didNotImplementKnownEncoderSubclass(Class<? extends Encoder> decoder);
}