/* * Copyright 2013 gerald. * * 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.hawt.jndi; import io.hawt.util.MBeanSupport; import java.lang.reflect.Proxy; import java.util.*; import javax.naming.*; /** * JNDI Facade MX Bean implementation */ public class JndiFacade extends MBeanSupport implements JndiFacadeMXBean { /** * Context name/path from initial context. * null means initial context */ private String contextName; /** * List of known datatypes */ private final List<Class> dataTypes=new ArrayList<Class>(); /** * List of simple types */ private final Set<Class> simpleTypes=new HashSet<Class>(Arrays.asList(String.class,Integer.class,Long.class,Boolean.class)); /** * JNDI Environment */ private Hashtable<String,?> environment; /** * Default constructor */ public JndiFacade() { addDataTypes(Context.class, Reference.class); addDataTypes(simpleTypes); addDataTypes( // JDBC "javax.sql.DataSource", // Mail "javax.mail.Session", // JTA "javax.transaction.UserTransaction", "javax.transaction.TransactionManager", "javax.transaction.TransactionSynchronizationRegistry", // JMS "javax.jms.ConnectionFactory", "javax.jms.Queue", "javax.jms.Topic", "javax.jms.Destination", // Validation "javax.validation.ValidatorFactory" ); } /** * Execute something with a JNDI connection. * Inspired by Spring JndiTemplate * @param <T> Return type * @param callback Callback executed within a JNDI context * @return Callback result */ protected <T> T execute(ContextCallback<T> callback) throws NamingException { InitialContext initialContext=null; try { initialContext= new InitialContext(environment); Context context; if (contextName==null) { context = initialContext; } else { context=(Context) initialContext.lookup(contextName); } return callback.doInJndi(context); } finally { if (initialContext!=null) { initialContext.close(); } } } /** * Callback used by {@link #execute(io.hawt.jndi.JndiFacade.ContextCallback) } method */ protected interface ContextCallback<T> { T doInJndi(Context context) throws NamingException; } /** * Add types to known data types */ public final void addDataTypes(Collection<Class> aDataTypes) { dataTypes.addAll(aDataTypes); } /** * Add types to known data types */ public final void addDataTypes(Class... aDataTypes) { addDataTypes(Arrays.asList(aDataTypes)); } /** * Add types to known data types */ public final void addDataTypes(String... aDataTypeNames) { for(String dataTypeName:aDataTypeNames) { try { dataTypes.add(Class.forName(dataTypeName)); } catch (ClassNotFoundException classNotFoundException) { } } } /** * Try to get Class from class name * @param typeName Class name * @return Class or null if not found */ private Class getType(String typeName) { Class type; try { type= Class.forName(typeName); } catch (ClassNotFoundException classNotFoundException) { type= null; } return type; } /** * Try to get known type from any type * @param type Any type * @return Known type or null if unknown */ private List<Class> getDataTypes(Class type) { List<Class> result=new ArrayList<Class>(1); for(Class dataType:dataTypes) { if (dataType.isAssignableFrom(type)) { result.add(dataType); } } return result; } private JndiDTO createData(Context context, String fullName, String simpleName, String className, Object value) { Class clazz=null; List<Class> dataTypes=Collections.emptyList(); boolean proxy=false; if (value!=null) { clazz=value.getClass(); } else if (className!=null) { clazz=getType(className); } if (clazz!=null) { dataTypes=getDataTypes(clazz); proxy=Proxy.isProxyClass(clazz); } if (simpleName==null) { int slashPos=fullName.lastIndexOf('/'); simpleName=slashPos<0?fullName:fullName.substring(slashPos+1); } if (value!=null) { for(Class dataType:dataTypes) { if (simpleTypes.contains(dataType)) { try { value = context.lookup(fullName); } catch (NamingException namingException) { } break; } } } List<String> dataTypeNames=new ArrayList<String>(dataTypes.size()+1); for(Class dataType:dataTypes) { dataTypeNames.add(dataType.getName()); } if (proxy) { dataTypeNames.add("$Proxy"); } String[] dataTypeNamesArray=dataTypeNames.isEmpty()? null: dataTypeNames.toArray(new String[dataTypeNames.size()]); JndiDTO namingData=new JndiDTO( fullName, simpleName, className, dataTypeNamesArray, value==null?null:value.toString() ); return namingData; } private String fixName(String name) { return name==null?"":name.trim(); } @Override public JndiDTO[] list(final String name) throws NamingException { return execute(new ContextCallback<JndiDTO[]>() { @Override public JndiDTO[] doInJndi(Context context) throws NamingException { NamingEnumeration<NameClassPair> namingEnum=null; try { namingEnum=context.list(fixName(name)); String prefix=name==null||name.isEmpty()?"":name+"/"; List<JndiDTO> namingDatas=new ArrayList<JndiDTO>(); while(namingEnum.hasMore()) { NameClassPair nameClassPair=namingEnum.next(); String simpleName=nameClassPair.getName(); String fullName=prefix+simpleName; if (nameClassPair instanceof Binding) { Binding binding=(Binding) nameClassPair; namingDatas.add(createData(context, fullName, simpleName, nameClassPair.getClassName(), binding.getObject())); } else { namingDatas.add(createData(context, fullName, simpleName, nameClassPair.getClassName(), null)); } } return namingDatas.toArray(new JndiDTO[namingDatas.size()]); } finally { if (namingEnum!=null) { namingEnum.close(); } } } }); } @Override public JndiDTO lookup(final String name) throws NamingException { return execute(new ContextCallback<JndiDTO>() { @Override public JndiDTO doInJndi(Context context) throws NamingException { String fullName=fixName(name); String simpleName; Object object; if (fullName.isEmpty()) { object = context; simpleName = ""; } else { object=context.lookup(fullName); simpleName=null; } if (object==null) { return null; } return createData(context, fullName, simpleName, object.getClass().getName(), object); } }); } @Override public JndiDTO[] getData() throws NamingException { return list(""); } @Override protected String getDefaultObjectName() { String name; if (contextName==null) { name="initial"; } else { name=contextName.replaceAll("[:/]+", "."); if (name.endsWith(".")) { name=name.substring(0, name.length()-1); } } return "hawtio:type=JndiFacade,name="+name; } /** * Get context name * @return null stands for initial context */ @Override public String getContextName() { return contextName; } public void setContextName(String contextName) { this.contextName = contextName; } public Hashtable<String, ?> getEnvironment() { return environment; } public void setEnvironment(Hashtable<String, ?> environment) { this.environment = environment; } }