/*
* Copyright 2015 Kevin Herron
*
* 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 com.digitalpetri.opcua.stack.core.serialization;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import com.digitalpetri.opcua.stack.core.Stack;
import com.digitalpetri.opcua.stack.core.StatusCodes;
import com.digitalpetri.opcua.stack.core.UaSerializationException;
import com.digitalpetri.opcua.stack.core.types.builtin.NodeId;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ClassInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DelegateRegistry {
private static final Map<Class<?>, EncoderDelegate<?>> encodersByClass = Maps.newConcurrentMap();
private static final Map<NodeId, EncoderDelegate<?>> encodersById = Maps.newConcurrentMap();
private static final Map<Class<?>, DecoderDelegate<?>> decodersByClass = Maps.newConcurrentMap();
private static final Map<NodeId, DecoderDelegate<?>> decodersById = Maps.newConcurrentMap();
public static <T> void registerEncoder(EncoderDelegate<T> delegate, Class<T> clazz, NodeId... ids) {
encodersByClass.put(clazz, delegate);
if (ids != null) {
Arrays.stream(ids).forEach(id -> encodersById.put(id, delegate));
}
}
public static <T> void registerDecoder(DecoderDelegate<T> delegate, Class<T> clazz, NodeId... ids) {
decodersByClass.put(clazz, delegate);
if (ids != null) {
Arrays.stream(ids).forEach(id -> decodersById.put(id, delegate));
}
}
@SuppressWarnings("unchecked")
public static <T> EncoderDelegate<T> getEncoder(Object t) throws UaSerializationException {
try {
return (EncoderDelegate<T>) encodersByClass.get(t.getClass());
} catch (NullPointerException e) {
throw new UaSerializationException(StatusCodes.Bad_EncodingError,
"no encoder registered for class=" + t);
}
}
@SuppressWarnings("unchecked")
public static <T> EncoderDelegate<T> getEncoder(Class<?> clazz) throws UaSerializationException {
try {
return (EncoderDelegate<T>) encodersByClass.get(clazz);
} catch (NullPointerException e) {
throw new UaSerializationException(StatusCodes.Bad_EncodingError,
"no encoder registered for class=" + clazz);
}
}
@SuppressWarnings("unchecked")
public static <T> EncoderDelegate<T> getEncoder(NodeId encodingId) throws UaSerializationException {
try {
return (EncoderDelegate<T>) encodersById.get(encodingId);
} catch (NullPointerException e) {
throw new UaSerializationException(StatusCodes.Bad_EncodingError,
"no encoder registered for encodingId=" + encodingId);
}
}
@SuppressWarnings("unchecked")
public static <T> DecoderDelegate<T> getDecoder(T t) throws UaSerializationException {
try {
return (DecoderDelegate<T>) decodersByClass.get(t.getClass());
} catch (NullPointerException e) {
throw new UaSerializationException(StatusCodes.Bad_DecodingError,
"no decoder registered for class=" + t);
}
}
@SuppressWarnings("unchecked")
public static <T> DecoderDelegate<T> getDecoder(Class<T> clazz) throws UaSerializationException {
try {
return (DecoderDelegate<T>) decodersByClass.get(clazz);
} catch (NullPointerException e) {
throw new UaSerializationException(StatusCodes.Bad_DecodingError,
"no decoder registered for class=" + clazz);
}
}
@SuppressWarnings("unchecked")
public static <T> DecoderDelegate<T> getDecoder(NodeId encodingId) {
DecoderDelegate<T> decoder = (DecoderDelegate<T>) decodersById.get(encodingId);
if (decoder == null) {
throw new UaSerializationException(StatusCodes.Bad_DecodingError,
"no decoder registered for encodingId=" + encodingId);
}
return decoder;
}
static {
/*
* Reflect-o-magically find all generated structured and enumerated types and force their static initialization
* blocks to run, registering their encode/decode methods with the delegate registry.
*/
Logger logger = LoggerFactory.getLogger(DelegateRegistry.class);
ClassLoader classLoader = Stack.getCustomClassLoader()
.orElse(DelegateRegistry.class.getClassLoader());
try {
loadGeneratedClasses(classLoader);
} catch (Exception e1) {
// Temporarily set the thread context ClassLoader to our
// ClassLoader and try loading the classes one more time.
Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
thread.setContextClassLoader(classLoader);
try {
loadGeneratedClasses(classLoader);
} catch (Exception e2) {
logger.error("Error loading generated classes.", e2);
} finally {
thread.setContextClassLoader(contextClassLoader);
}
}
}
private static void loadGeneratedClasses(ClassLoader classLoader) throws IOException, ClassNotFoundException {
ClassPath classPath = ClassPath.from(classLoader);
ImmutableSet<ClassInfo> structures =
classPath.getTopLevelClasses("com.digitalpetri.opcua.stack.core.types.structured");
ImmutableSet<ClassInfo> enumerations =
classPath.getTopLevelClasses("com.digitalpetri.opcua.stack.core.types.enumerated");
for (ClassInfo classInfo : Sets.union(structures, enumerations)) {
Class<?> clazz = classInfo.load();
Class.forName(clazz.getName(), true, classLoader);
}
}
}