/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.deployers;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import org.teiid.adminapi.Translator;
import org.teiid.adminapi.impl.VDBMetaData;
import org.teiid.adminapi.impl.VDBTranslatorMetaData;
import org.teiid.core.CorePlugin;
import org.teiid.core.TeiidException;
import org.teiid.core.TeiidRuntimeException;
import org.teiid.core.util.StringUtil;
import org.teiid.dqp.internal.datamgr.ConnectorManagerRepository.ConnectorManagerException;
import org.teiid.dqp.internal.datamgr.ConnectorManagerRepository.ExecutionFactoryProvider;
import org.teiid.dqp.internal.datamgr.TranslatorRepository;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.metadata.ExtensionMetadataProperty;
import org.teiid.query.QueryPlugin;
import org.teiid.runtime.RuntimePlugin;
import org.teiid.translator.DelegatingExecutionFactory;
import org.teiid.translator.ExecutionFactory;
import org.teiid.translator.MetadataProcessor;
import org.teiid.translator.TranslatorProperty;
import org.teiid.translator.TranslatorProperty.PropertyType;
public class TranslatorUtil {
public static final String DEPLOYMENT_NAME = "deployment-name"; //$NON-NLS-1$
static Map<Method, TranslatorProperty> getTranslatorProperties(Class<?> attachmentClass) {
Map<Method, TranslatorProperty> props = new TreeMap<Method, TranslatorProperty>(new Comparator<Method>() {
@Override
public int compare(Method o1, Method o2) {
return o1.getName().compareTo(o2.getName());
}
});
buildTranslatorProperties(attachmentClass, props);
return props;
}
static Map<Field, ExtensionMetadataProperty> getExtensionMetadataProperties(Class<?> attachmentClass) {
Map<Field, ExtensionMetadataProperty> props = new TreeMap<Field, ExtensionMetadataProperty>(new Comparator<Field>() {
@Override
public int compare(Field o1, Field o2) {
return o1.getName().compareTo(o2.getName());
}
});
buildExtensionMetadataProperties(attachmentClass, props);
return props;
}
private static void buildTranslatorProperties(Class<?> attachmentClass, Map<Method, TranslatorProperty> props){
Class<?>[] baseInterfaces = attachmentClass.getInterfaces();
for (Class<?> clazz:baseInterfaces) {
buildTranslatorProperties(clazz, props);
}
Class<?> superClass = attachmentClass.getSuperclass();
if (superClass != null) {
buildTranslatorProperties(superClass, props);
}
Method[] methods = attachmentClass.getMethods();
for (Method m:methods) {
TranslatorProperty tp = m.getAnnotation(TranslatorProperty.class);
if (tp != null) {
props.put(m, tp);
}
}
}
private static void buildExtensionMetadataProperties(Class<?> attachmentClass, Map<Field, ExtensionMetadataProperty> props){
Class<?>[] baseInterfaces = attachmentClass.getInterfaces();
for (Class<?> clazz:baseInterfaces) {
buildExtensionMetadataProperties(clazz, props);
}
Class<?> superClass = attachmentClass.getSuperclass();
if (superClass != null) {
buildExtensionMetadataProperties(superClass, props);
}
Field[] fields = attachmentClass.getDeclaredFields();
for (Field f:fields) {
ExtensionMetadataProperty tp = f.getAnnotation(ExtensionMetadataProperty.class);
if (tp != null) {
f.setAccessible(true);
props.put(f, tp);
}
}
}
public static ExecutionFactory buildExecutionFactory(VDBTranslatorMetaData data) throws TeiidException {
ExecutionFactory executionFactory;
try {
Class<?> executionClass = data.getExecutionFactoryClass();
Object o = executionClass.newInstance();
if(!(o instanceof ExecutionFactory)) {
throw new TeiidException(RuntimePlugin.Event.TEIID40024, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40024, executionClass));
}
executionFactory = (ExecutionFactory)o;
synchronized (executionFactory) {
injectProperties(executionFactory, data);
ClassLoader orginalCL = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(executionFactory.getClass().getClassLoader());
executionFactory.start();
} finally {
Thread.currentThread().setContextClassLoader(orginalCL);
}
}
return executionFactory;
} catch (InvocationTargetException e) {
throw new TeiidException(RuntimePlugin.Event.TEIID40025, e);
} catch (IllegalAccessException e) {
throw new TeiidException(RuntimePlugin.Event.TEIID40026, e);
} catch (InstantiationException e) {
throw new TeiidException(CorePlugin.Event.TEIID10036, e);
}
}
public static ExecutionFactory<Object, Object> buildDelegateAwareExecutionFactory(
VDBTranslatorMetaData translator, ExecutionFactoryProvider provider)
throws ConnectorManagerException {
ExecutionFactory<Object, Object> ef = null;
try {
ef = buildExecutionFactory(translator);
} catch (TeiidException e) {
throw new ConnectorManagerException(e);
}
if (ef instanceof DelegatingExecutionFactory) {
DelegatingExecutionFactory delegator = (DelegatingExecutionFactory)ef;
String delegateName = delegator.getDelegateName();
if (delegateName != null) {
ExecutionFactory<Object, Object> delegate = provider.getExecutionFactory(delegateName);
if (delegate == null) {
throw new ConnectorManagerException(
RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40155, delegateName));
}
((DelegatingExecutionFactory<Object, Object>) ef).setDelegate(delegate);
}
}
return ef;
}
private static void injectProperties(ExecutionFactory ef, final VDBTranslatorMetaData data) throws InvocationTargetException, IllegalAccessException, TeiidException{
Map<Method, TranslatorProperty> props = TranslatorUtil.getTranslatorProperties(ef.getClass());
Map<String, String> p = data.getPropertiesMap();
TreeMap<String, String> caseInsensitiveProps = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
/*
VDBTranslatorMetaData parent = data.getParent();
while (parent != null) {
for (Map.Entry<String, String> entry : parent.getPropertiesMap().entrySet()) {
if (!caseInsensitiveProps.containsKey(entry.getKey()) && entry.getValue() != null) {
caseInsensitiveProps.put(entry.getKey(), entry.getValue());
}
}
parent = parent.getParent();
}
*/
synchronized (p) {
caseInsensitiveProps.putAll(p);
}
caseInsensitiveProps.remove(DEPLOYMENT_NAME);
for (Method method:props.keySet()) {
TranslatorProperty tp = props.get(method);
String propertyName = getPropertyName(method);
String value = caseInsensitiveProps.remove(propertyName);
if (value != null) {
Method setterMethod = getSetter(ef.getClass(), method);
setterMethod.invoke(ef, convert(value, method.getReturnType()));
} else if (tp.required()) {
throw new TeiidException(RuntimePlugin.Event.TEIID40027, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40027, tp.display()));
}
}
caseInsensitiveProps.remove(Translator.EXECUTION_FACTORY_CLASS);
if (!caseInsensitiveProps.isEmpty()) {
LogManager.logWarning(LogConstants.CTX_RUNTIME, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40001, caseInsensitiveProps.keySet(), data.getName()));
}
}
public static String getPropertyName(Method method) {
String result = method.getName();
if (result.startsWith("get")) { //$NON-NLS-1$
return result.substring(3);
}
else if (result.startsWith("is")) { //$NON-NLS-1$
return result.substring(2);
}
return result;
}
public static Method getSetter(Class<?> clazz, Method method) throws SecurityException, TeiidException {
String setter = method.getName();
if (method.getName().startsWith("get")) { //$NON-NLS-1$
setter = "set"+setter.substring(3);//$NON-NLS-1$
}
else if (method.getName().startsWith("is")) { //$NON-NLS-1$
setter = "set"+setter.substring(2); //$NON-NLS-1$
}
else {
setter = "set"+method.getName().substring(0,1).toUpperCase()+method.getName().substring(1); //$NON-NLS-1$
}
try {
return clazz.getMethod(setter, method.getReturnType());
} catch (NoSuchMethodException e) {
try {
return clazz.getMethod(method.getName(), method.getReturnType());
} catch (NoSuchMethodException e1) {
throw new TeiidException(RuntimePlugin.Event.TEIID40028, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40028, setter, method.getName()));
}
}
}
private static Object convert(Object value, Class<?> type) {
if(value.getClass() == type) {
return value;
}
if (value instanceof String) {
String str = (String)value;
return StringUtil.valueOf(str, type);
}
return value;
}
public static String getTranslatorName(ExecutionFactory factory) {
org.teiid.translator.Translator translator = factory.getClass().getAnnotation(org.teiid.translator.Translator.class);
if (translator == null) {
return null;
}
return translator.name();
}
public static VDBTranslatorMetaData buildTranslatorMetadata(ExecutionFactory factory, String moduleName) {
org.teiid.translator.Translator translator = factory.getClass().getAnnotation(org.teiid.translator.Translator.class);
if (translator == null) {
return null;
}
VDBTranslatorMetaData metadata = new VDBTranslatorMetaData();
String see = translator.deprecated();
if (see != null && see.length() > 0) {
metadata.addProperty("deprecated", see); //$NON-NLS-1$
}
metadata.setName(translator.name());
metadata.setDescription(translator.description());
metadata.setExecutionFactoryClass(factory.getClass());
metadata.setModuleName(moduleName);
ExtendedPropertyMetadataList propertyDefns = new ExtendedPropertyMetadataList();
try {
Object instance = factory.getClass().newInstance();
buildTranslatorProperties(factory, metadata, propertyDefns, instance);
buildExtensionMetadataProperties(factory, metadata, propertyDefns, instance);
} catch (InstantiationException e) {
// ignore
} catch (IllegalAccessException e) {
// ignore
}
metadata.addAttchment(ExtendedPropertyMetadataList.class, propertyDefns);
return metadata;
}
private static void buildExtensionMetadataProperties(ExecutionFactory factory, VDBTranslatorMetaData metadata, ExtendedPropertyMetadataList propertyDefns, Object instance) {
Class clazz = factory.getClass();
readExtensionPropertyMetadataAsExtendedMetadataProperties(propertyDefns, clazz);
MetadataProcessor metadataProcessor = factory.getMetadataProcessor();
if (metadataProcessor != null) {
clazz = metadataProcessor.getClass();
readExtensionPropertyMetadataAsExtendedMetadataProperties(propertyDefns, clazz);
}
}
private static void buildTranslatorProperties(ExecutionFactory factory, VDBTranslatorMetaData metadata, ExtendedPropertyMetadataList propertyDefns, Object instance) {
Class clazz = factory.getClass();
readTranslatorPropertyAsExtendedMetadataProperties(metadata, propertyDefns, instance, clazz);
MetadataProcessor metadataProcessor = factory.getMetadataProcessor();
if (metadataProcessor != null) {
clazz = metadataProcessor.getClass();
readTranslatorPropertyAsExtendedMetadataProperties(metadata, propertyDefns, metadataProcessor, clazz);
}
}
private static void readExtensionPropertyMetadataAsExtendedMetadataProperties(
ExtendedPropertyMetadataList propertyDefns, Class clazz) {
Map<Field, ExtensionMetadataProperty> tps = TranslatorUtil.getExtensionMetadataProperties(clazz);
for (Field f:tps.keySet()) {
ExtensionMetadataProperty tp = tps.get(f);
ExtendedPropertyMetadata epm = new ExtendedPropertyMetadata();
epm.category = PropertyType.EXTENSION_METADATA.name();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < tp.applicable().length; i++) {
sb.append(tp.applicable()[i].getName());
if (tp.applicable().length-1 > i) {
sb.append(","); //$NON-NLS-1$
}
}
epm.owner = sb.toString();
try {
epm.name = (String)f.get(null);
} catch (IllegalArgumentException e) {
continue;
} catch (IllegalAccessException e) {
continue;
}
epm.description = tp.description();
epm.displayName = tp.display();
epm.required = tp.required();
epm.dataType = tp.datatype().getName();
// allowed values
if (tp.allowed() != null && !tp.allowed().isEmpty()) {
epm.allowed = new ArrayList<String>();
StringTokenizer st = new StringTokenizer(tp.allowed(), ","); //$NON-NLS-1$
while (st.hasMoreTokens()) {
epm.allowed.add(st.nextToken());
}
}
propertyDefns.add(epm);
}
}
private static void readTranslatorPropertyAsExtendedMetadataProperties(
VDBTranslatorMetaData metadata,
ExtendedPropertyMetadataList propertyDefns, Object instance,
Class clazz) {
Map<Method, TranslatorProperty> tps = TranslatorUtil.getTranslatorProperties(clazz);
for (Method m:tps.keySet()) {
Object defaultValue = getDefaultValue(instance, m, tps.get(m));
TranslatorProperty tp = tps.get(m);
boolean importProperty = tp.category()==TranslatorProperty.PropertyType.IMPORT;
if (defaultValue != null && !importProperty) {
metadata.addProperty(getPropertyName(m), defaultValue.toString());
}
ExtendedPropertyMetadata epm = new ExtendedPropertyMetadata();
epm.category = tp.category().name();
epm.name = importProperty?"importer."+getPropertyName(m):getPropertyName(m); //$NON-NLS-1$
epm.description = tp.description();
epm.advanced = tp.advanced();
if (defaultValue != null) {
epm.defaultValue = defaultValue.toString();
}
epm.displayName = tp.display();
epm.masked = tp.masked();
epm.required = tp.required();
epm.dataType = m.getReturnType().getCanonicalName();
// allowed values
if (m.getReturnType().isEnum()) {
epm.allowed = new ArrayList<String>();
Object[] constants = m.getReturnType().getEnumConstants();
for( int i=0; i<constants.length; i++ ) {
epm.allowed.add(((Enum<?>)constants[i]).name());
}
epm.dataType = "java.lang.String"; //$NON-NLS-1$
}
propertyDefns.add(epm);
}
}
private static Object convert(Object instance, Method method, TranslatorProperty prop) {
Class<?> type = method.getReturnType();
String[] allowedValues = null;
Method getter = null;
boolean readOnly = prop.readOnly();
if (type == Void.TYPE) { //check for setter
Class<?>[] types = method.getParameterTypes();
if (types.length != 1) {
throw new TeiidRuntimeException(RuntimePlugin.Event.TEIID40029, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40029, method));
}
type = types[0];
try {
getter = instance.getClass().getMethod("get" + method.getName(), (Class[])null); //$NON-NLS-1$
} catch (Exception e) {
try {
getter = instance.getClass().getMethod("get" + method.getName().substring(3), (Class[])null); //$NON-NLS-1$
} catch (Exception e1) {
//can't find getter, won't set the default value
}
}
} else if (method.getParameterTypes().length != 0) {
throw new TeiidRuntimeException(RuntimePlugin.Event.TEIID40029, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40029, method));
} else {
getter = method;
try {
TranslatorUtil.getSetter(instance.getClass(), method);
} catch (Exception e) {
if (!readOnly) {
throw new TeiidRuntimeException(RuntimePlugin.Event.TEIID40146, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40146, method));
}
}
}
Object defaultValue = null;
if (prop.required()) {
if (prop.advanced()) {
throw new TeiidRuntimeException(RuntimePlugin.Event.TEIID40031, RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40031,method));
}
} else if (getter != null) {
try {
defaultValue = getter.invoke(instance, (Object[])null);
} catch (Exception e) {
//no simple default value
}
}
if (type.isEnum()) {
Object[] constants = type.getEnumConstants();
allowedValues = new String[constants.length];
for( int i=0; i<constants.length; i++ ) {
allowedValues[i] = ((Enum<?>)constants[i]).name();
}
type = String.class;
if (defaultValue != null) {
defaultValue = ((Enum<?>)defaultValue).name();
}
}
if (!(defaultValue instanceof Serializable)) {
defaultValue = null; //TODO
}
return defaultValue;
}
public static Object getDefaultValue(Object instance, Method method, TranslatorProperty prop) {
return convert(instance, method, prop);
}
@SuppressWarnings({"rawtypes","unchecked"})
public static ExecutionFactory<Object, Object> getExecutionFactory(String name, TranslatorRepository vdbRepo, TranslatorRepository repo, VDBMetaData deployment, IdentityHashMap<Translator, ExecutionFactory<Object, Object>> map, HashSet<String> building) throws ConnectorManagerException {
if (!building.add(name)) {
throw new ConnectorManagerException(RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40138, building));
}
VDBTranslatorMetaData translator = vdbRepo.getTranslatorMetaData(name);
if (translator == null) {
translator = repo.getTranslatorMetaData(name);
}
if (translator == null) {
return null;
}
ExecutionFactory<Object, Object> ef = map.get(translator);
if ( ef == null) {
try {
ef = TranslatorUtil.buildExecutionFactory(translator);
} catch (TeiidException e) {
throw new ConnectorManagerException(e);
}
if (ef instanceof DelegatingExecutionFactory) {
DelegatingExecutionFactory delegator = (DelegatingExecutionFactory)ef;
String delegateName = delegator.getDelegateName();
if (delegateName != null) {
ExecutionFactory<Object, Object> delegate = getExecutionFactory(delegateName, vdbRepo, repo, deployment, map, building);
if (delegate == null) {
if (deployment != null) {
throw new ConnectorManagerException(QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31146, deployment.getName(), deployment.getVersion(), delegateName));
}
throw new ConnectorManagerException(RuntimePlugin.Util.gs(RuntimePlugin.Event.TEIID40136, delegateName));
}
((DelegatingExecutionFactory<Object, Object>) ef).setDelegate(delegate);
}
}
map.put(translator, ef);
}
return ef;
}
}