/*
 * Copyright 2004,2004 The Apache Software Foundation.
 * 
 * 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 org.apache.bsf.util.event.generator;

import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/** EventAdapterGenerator
  *
  * Generate an "Event Adapter" dynamically during program execution
  *
  **/
public class EventAdapterGenerator
{
  public static AdapterClassLoader ldr = new AdapterClassLoader();
  static Class  EVENTLISTENER          = null;
  static String CLASSPACKAGE           = "org/apache/bsf/util/event/adapters/";
  static String WRITEDIRECTORY         = null;

  // starting 8 bytes of all Java Class files
  static byte   CLASSHEADER[];
  // constant pool items found in all event adapters
  static short  BASECPCOUNT; // number of cp items + 1 ( cp item # 0 reserved for JVM )
  static byte   BASECP[];    //
  // some bytes in the middle of the class file (see below)
  static byte   FIXEDCLASSBYTES[];
  // the initialization method, noargs constructor
  static byte   INITMETHOD[];

  private static Log logger;

  /* The static initializer */
  static
  {
	logger = LogFactory.getLog(
				(org.apache.bsf.util.event.generator.EventAdapterGenerator.class).getName());

	String USERCLASSPACKAGE = System.getProperty("DynamicEventClassPackage",
												 "");

	if (!USERCLASSPACKAGE.equals(""))
	{
	  CLASSPACKAGE = USERCLASSPACKAGE;
	}

	if(CLASSPACKAGE.length() > 0 )
	{
	  CLASSPACKAGE = CLASSPACKAGE.replace('\\','/');
	  if(!CLASSPACKAGE.endsWith("/"))
	  { CLASSPACKAGE = CLASSPACKAGE+"/"; }
	}
	WRITEDIRECTORY = System.getProperty("DynamicEventClassWriteDirectory",CLASSPACKAGE);
	if(WRITEDIRECTORY.length() > 0 )
	{
	  WRITEDIRECTORY = WRITEDIRECTORY.replace('\\','/');
	  if(!WRITEDIRECTORY.endsWith("/"))
	  { WRITEDIRECTORY = WRITEDIRECTORY+"/"; }
	}
	try
	// { EVENTLISTENER = Class.forName("java.util.EventListener"); }
	{ EVENTLISTENER = Thread.currentThread().getContextClassLoader().loadClass ("java.util.EventListener"); } // rgf, 2006-01-05
	catch(ClassNotFoundException ex)
	{
            System.err.println(ex.getMessage());
            ex.printStackTrace();
        }


	// start of the Java Class File
	CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(byte)0xCA);  // magic
	CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(byte)0xFE);  // magic
	CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(byte)0xBA);  // magic
	CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(byte)0xBE);  // magic
	CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(short)3);    // minor version
	CLASSHEADER = ByteUtility.addBytes(CLASSHEADER,(short)45);   // major version

	// Start the constant pool for base items in all event adapter classes
	BASECPCOUNT = 17; // number of cp items + 1 ( cp item # 0 reserved for JVM )

	// cp item 01
	BASECP = Bytecode.addUtf8(BASECP,"()V");

	// cp item 02
	BASECP = Bytecode.addUtf8(BASECP,"<init>");

	// cp item 03
	BASECP = Bytecode.addUtf8(BASECP,"Code");

	// cp item 04
	BASECP = Bytecode.addUtf8(BASECP,"eventProcessor");

	// cp item 05
	BASECP = Bytecode.addUtf8(BASECP,"java/lang/Object");

	// cp item 06
	BASECP = Bytecode.addUtf8(BASECP,"org/apache/bsf/util/event/EventAdapterImpl");

	// cp item 07
	BASECP = Bytecode.addUtf8(BASECP,"org/apache/bsf/util/event/EventProcessor");

	// cp item 08
	BASECP = Bytecode.addUtf8(BASECP,"(Ljava/lang/String;[Ljava/lang/Object;)V");

	// cp item 09
	BASECP = Bytecode.addUtf8(BASECP,"Lorg/apache/bsf/util/event/EventProcessor;");

	// cp item 10
	BASECP = Bytecode.addClass(BASECP,(short)5); // Class "java/lang/Object"

	// cp item 11
	BASECP = Bytecode.addClass(BASECP,(short)6); // Class "org/apache/bsf/util/event/EventAdapterImpl"

	// cp item 12
	BASECP = Bytecode.addClass(BASECP,(short)7); // Class "org/apache/bsf/util/event/EventProcessor"

	// cp item 13
	BASECP = Bytecode.addNameAndType(BASECP,(short)2,(short)1); // "<init>" "()V"

	// cp item 14
	BASECP = Bytecode.addNameAndType(BASECP,(short)4,(short)9); // "eventProcessor" "Lorg/apache/bsf/util/event/EventProcessor;"

	// cp item 15
	BASECP = Bytecode.addFieldRef(BASECP,(short)11,(short)14);

	// cp item 16
	BASECP = Bytecode.addMethodRef(BASECP,(short)11,(short)13);

	// fixed bytes in middle of class file
	FIXEDCLASSBYTES = ByteUtility.addBytes(FIXEDCLASSBYTES,(short)0x21); // access_flags        (fixed)
	FIXEDCLASSBYTES = ByteUtility.addBytes(FIXEDCLASSBYTES,(short)20);   // this_class          (fixed)
	FIXEDCLASSBYTES = ByteUtility.addBytes(FIXEDCLASSBYTES,(short)11);   // super_class         (fixed)
	FIXEDCLASSBYTES = ByteUtility.addBytes(FIXEDCLASSBYTES,(short)1);    // interface_count     (fixed)
	FIXEDCLASSBYTES = ByteUtility.addBytes(FIXEDCLASSBYTES,(short)19);   // interfaces          (fixed)
	FIXEDCLASSBYTES = ByteUtility.addBytes(FIXEDCLASSBYTES,(short)0);    // field_count         (fixed)

	// initialization method, constructor
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)1);              // access_flags
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)2);              // name_index "<init>"
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)1);              // descriptor_index "()V"
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)1);              // attribute_count
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)3);              // attribute_name_index "Code"
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(long)17);              // attribute_length
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)1);              // max_stack
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)1);              // max_locals
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(long)5);               // code_length
	//code
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(byte)0x2A);            // aload_0
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(byte)0xB7);            // invokespecial
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)16);             // method_ref index
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(byte)0xB1);            // return
	// exception table
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)0);              // exception_table_length
	INITMETHOD = ByteUtility.addBytes(INITMETHOD,(short)0);              // attributes_count

  }

  /* methods that take an EventListener Class Type to create an EventAdapterClass */
  public static Class makeEventAdapterClass(Class listenerType,boolean writeClassFile)
  {
      logger.info("EventAdapterGenerator");

        if( EVENTLISTENER.isAssignableFrom(listenerType) )
	{
	  boolean exceptionable    = false;
	  boolean nonExceptionable = false;
	  byte    constantPool[]   = null;
	  short   cpBaseIndex;
	  short   cpCount          = 0;
	  short   cpExceptionBaseIndex;
	  short   exceptionableCount;
	  short   nonExceptionableCount;

	  /* Derive Names */
	  String listenerTypeName      = listenerType.getName();
          logger.info("ListenerTypeName: "+listenerTypeName);
	  String adapterClassName      =
		CLASSPACKAGE+
		(listenerTypeName.endsWith("Listener")
		 ? listenerTypeName.substring(0, listenerTypeName.length() - 8)
		 : listenerTypeName).replace('.', '_') +
		"Adapter";
	  String finalAdapterClassName = adapterClassName;
	  Class  cached                = null;
	  int    suffixIndex           = 0;

	  do
	  {
		if (null != (cached = ldr.getLoadedClass(finalAdapterClassName)))
		{
                    logger.info("cached:  "+cached);
		  try
		  {
			if (!listenerType.isAssignableFrom(cached))
			  finalAdapterClassName = adapterClassName + "_" + suffixIndex++;
			else
			  return cached;
		  }
		  catch(VerifyError ex)
		  {
                      System.err.println(ex.getMessage());
                      ex.printStackTrace();
                      return cached;
		  }
		}
	  }
	  while (cached != null);

	  String eventListenerName = listenerTypeName.replace('.', '/');

	  /* method stuff */
	  java.lang.reflect.Method lms[] = listenerType.getMethods();

	  /* ****************************************************************************************** */
	  // Listener interface
	  // Class name
	  cpCount += 4;

	  // cp item 17
	  constantPool = Bytecode.addUtf8(constantPool,eventListenerName);

	  // cp item 18
	  constantPool = Bytecode.addUtf8(constantPool,finalAdapterClassName);

	  // cp item 19
	  constantPool = Bytecode.addClass(constantPool,(short)17);

	  // cp item 20
	  constantPool = Bytecode.addClass(constantPool,(short)18);

	  // do we have nonExceptionalble event, exceptionable or both
	  for (int i = 0 ; i < lms.length ; ++i)
	  {
		Class exceptionTypes[] = lms[i].getExceptionTypes();
		if( 0 < exceptionTypes.length)
		{ exceptionable = true; }
		else
		{ nonExceptionable = true; }
	  }/* End for*/

	  /* ****************************************************************************************** */
	  // optional inclusion of nonexceptional events affects exceptional events indices

	  nonExceptionableCount = 0;
	  if(nonExceptionable)
	  {
		nonExceptionableCount = 3;
		cpCount += nonExceptionableCount;

		// cp item 21
		constantPool = Bytecode.addUtf8(constantPool,"processEvent");

		// cp item 22
		constantPool = Bytecode.addNameAndType(constantPool,(short)21,(short)8);


		// cp item 23
		constantPool = Bytecode.addInterfaceMethodRef(constantPool,(short)12,(short)22);
	  }

	  /* ****************************************************************************************** */
	  // optional inclusion of exceptional events affects CP Items which follow for specific methods

	  exceptionableCount = 0;
	  if(exceptionable)
	  {
		int classIndex = BASECPCOUNT + cpCount + 1;
		int nameIndex  = BASECPCOUNT + cpCount + 0;
		int natIndex   = BASECPCOUNT + cpCount + 3;

		exceptionableCount = 5;
		cpCount += exceptionableCount;

		// cp item 24 or 21
		constantPool = Bytecode.addUtf8(constantPool,"processExceptionableEvent");

		// cp item 25 or 22
		constantPool = Bytecode.addUtf8(constantPool,"java/lang/Exception");

		// cp item 26 or 23
		constantPool = Bytecode.addClass(constantPool,(short)classIndex);

		// cp item 27 or 24
		constantPool = Bytecode.addNameAndType(constantPool,(short)nameIndex,(short)8);

		// cp item 28 or 25
		constantPool = Bytecode.addInterfaceMethodRef(constantPool,(short)12,(short)natIndex);

	  }

	  // base index for method cp references
	  cpBaseIndex = (short)(BASECPCOUNT + cpCount);
          logger.debug("cpBaseIndex: " + cpBaseIndex);

	  for (int i = 0 ; i < lms.length ; ++i)
	  {
		String eventMethodName = lms[i].getName();
		String eventName = lms[i].getParameterTypes()[0].getName().replace('.','/');
		cpCount += 3;
		// cp items for event methods
		constantPool = Bytecode.addUtf8(constantPool,eventMethodName);
		constantPool = Bytecode.addUtf8(constantPool,("(L" + eventName + ";)V"));
		constantPool = Bytecode.addString(constantPool,(short)(BASECPCOUNT+cpCount-3));
	  }/* End for*/

	  boolean propertyChangeFlag[] = new boolean[lms.length];
	  int cpIndexPCE = 0;
	  for (int i = 0 ; i < lms.length ; ++i)
	  {
		String eventName = lms[i].getParameterTypes()[0].getName().replace('.','/');
		// cp items for PropertyChangeEvent special handling
		if(eventName.equalsIgnoreCase("java/beans/PropertyChangeEvent"))
		{
		  propertyChangeFlag[i] = true;
		  if( 0 == cpIndexPCE )
		  {
			constantPool = Bytecode.addUtf8(constantPool,eventName);
			constantPool = Bytecode.addUtf8(constantPool,"getPropertyName");
			constantPool = Bytecode.addUtf8(constantPool,"()Ljava/lang/String;");
			constantPool = Bytecode.addClass(constantPool,(short)(BASECPCOUNT + cpCount));
			constantPool = Bytecode.addNameAndType(constantPool,
												   (short)(BASECPCOUNT + cpCount + 1),
												   (short)(BASECPCOUNT + cpCount + 2));
			constantPool = Bytecode.addMethodRef(constantPool,
												 (short)(BASECPCOUNT + cpCount + 3),
												 (short)(BASECPCOUNT + cpCount + 4));
			cpCount += 6;
			cpIndexPCE = BASECPCOUNT + cpCount - 1;

		  }
		}
		else
		{ propertyChangeFlag[i] = false; }
	  }/* End for*/

	  cpExceptionBaseIndex = (short)(BASECPCOUNT + cpCount);
          logger.debug("cpExceptionBaseIndex: " + cpExceptionBaseIndex);

	  int excpIndex[][] = new int[lms.length][];
	  for (int i = 0 ; i < lms.length ; ++i)
	  {
		Class exceptionTypes[] = lms[i].getExceptionTypes();
		excpIndex[i] = new int[exceptionTypes.length];
		for ( int j = 0 ; j < exceptionTypes.length ; j++)
		{
		  constantPool = Bytecode.addUtf8(constantPool,exceptionTypes[j].getName().replace('.', '/'));
		  constantPool = Bytecode.addClass(constantPool,(short)(BASECPCOUNT+cpCount));
		  excpIndex[i][j] = BASECPCOUNT + cpCount + 1;
		  cpCount += 2;
		}
	  }/* End for*/
	  /* end constant pool */

	  /* ************************************************************************************************ */
	  // put the Class byte array together

	  /* start */
	  byte newClass[] = CLASSHEADER;                                   // magic, version      (fixed)
	  short count = (short)(BASECPCOUNT + cpCount);
	  newClass = ByteUtility.addBytes(newClass,count);                 // constant_pool_count (variable)
	  newClass = ByteUtility.addBytes(newClass,BASECP);                // constant_pool       (fixed)
	  newClass = ByteUtility.addBytes(newClass,constantPool);          // constant_pool       (variable)
	  newClass = ByteUtility.addBytes(newClass,FIXEDCLASSBYTES);       // see FIXEDCLASSBYTES (fixed)
	  newClass = ByteUtility.addBytes(newClass,(short)(lms.length+1)); // method_count        (variable)
	  newClass = ByteUtility.addBytes(newClass,INITMETHOD);            // constructor <init>  (fixed)
	  // methods

	  /* ****************************************************************************************** */
	  /* loop over listener methods from listenerType */
	  for (int i = 0 ; i < lms.length ; ++i)
	  {
		newClass = ByteUtility.addBytes(newClass,(short)1);                   // access_flags             (fixed)
		newClass = ByteUtility.addBytes(newClass,(short)(cpBaseIndex+3*i+0)); // name_index               (variable)
		newClass = ByteUtility.addBytes(newClass,(short)(cpBaseIndex+3*i+1)); // descriptor_index         (variable)
		newClass = ByteUtility.addBytes(newClass,(short)1);                   // attribute_count          (fixed)
		newClass = ByteUtility.addBytes(newClass,(short)3);                   // attribute_name_index code(fixed)

		// Code Attribute Length
		int length = 32;
		if( 0 < excpIndex[i].length )
		{ length += 5 + 8 * ( 1 + excpIndex[i].length ); }
		if(propertyChangeFlag[i])
		{ length += 2; }
		newClass = ByteUtility.addBytes(newClass,(long)length);               // attribute_length         (variable)

		// start code attribute
		newClass = ByteUtility.addBytes(newClass,(short)6);                   // max_stack                (fixed)
		newClass = ByteUtility.addBytes(newClass,(short)3);                   // max_locals               (fixed)

		// Code Length
		length = 20;
		if(exceptionable && 0 < excpIndex[i].length)
		{ length += 5; }
		if(propertyChangeFlag[i])
		{ length += 2; }
		newClass = ByteUtility.addBytes(newClass,(long)length);               // code_length              (variable)

		// start code
		newClass = ByteUtility.addBytes(newClass,(byte)0x2A);                 // aload_0                  (fixed)
		newClass = ByteUtility.addBytes(newClass,(byte)0xB4);                 // getfield                 (fixed)
		newClass = ByteUtility.addBytes(newClass,(short)15);                  // index                    (fixed)


		if(propertyChangeFlag[i])
		{ // the propertyName is passed as the first parameter
		  newClass = ByteUtility.addBytes(newClass,(byte)0x2B);               // aload_1                  (fixed)
		  newClass = ByteUtility.addBytes(newClass,(byte)0xB6);               // invokevirtual            (fixed)
		  newClass = ByteUtility.addBytes(newClass,(short)cpIndexPCE);        // methodref                (variable)
		}
		else
		{ // the eventMethodName is passed as the first parameter
		  // Target for method invocation.
		  newClass = ByteUtility.addBytes(newClass,(byte)0x12);                 // ldc                    (fixed)
		  newClass = ByteUtility.addBytes(newClass,(byte)(cpBaseIndex+3*i+2));  // index (byte)           (variable)
		}

		newClass = ByteUtility.addBytes(newClass,(byte)0x04);                 // iconst_1                 (fixed)
		newClass = ByteUtility.addBytes(newClass,(byte)0xBD);                 // anewarray                (fixed)
		newClass = ByteUtility.addBytes(newClass,(short)10);                  // Class java/lang/Object   (fixed)
		newClass = ByteUtility.addBytes(newClass,(byte)0x59);                 // dup                      (fixed)
		newClass = ByteUtility.addBytes(newClass,(byte)0x03);                 // iconst_0                 (fixed)
		newClass = ByteUtility.addBytes(newClass,(byte)0x2B);                 // aload_1                  (fixed)
		newClass = ByteUtility.addBytes(newClass,(byte)0x53);                 // aastore                  (fixed)
		newClass = ByteUtility.addBytes(newClass,(byte)0xB9);                 // invokeinterface          (fixed)

		// index to processEvent or processExceptionableEvent method
		length = 23; // actually an index into cp
		if(exceptionable && nonExceptionable)
		{ // interface method index
		  if( 0 < lms[i].getExceptionTypes().length )
		  { length += 5; }
		}
		else if(exceptionable)
		{ length += 2; }
		newClass = ByteUtility.addBytes(newClass,(short)length);              // index (process??????...) (variable)

		newClass = ByteUtility.addBytes(newClass,(byte)0x03);                 // iconst_0                 (fixed)
		newClass = ByteUtility.addBytes(newClass,(byte)0x00);                 // noop                     (fixed)
		newClass = ByteUtility.addBytes(newClass,(byte)0xB1);                 // return                   (fixed)

		if(exceptionable && 0 < excpIndex[i].length)
		{ // exception code
		  newClass = ByteUtility.addBytes(newClass,(byte)0x4D);               // astore_2                 (fixed)
		  newClass = ByteUtility.addBytes(newClass,(byte)0x2C);               // aload_2                  (fixed)
		  newClass = ByteUtility.addBytes(newClass,(byte)0xBF);               // athrow                   (fixed)
		  newClass = ByteUtility.addBytes(newClass,(byte)0x57);               // pop                      (fixed)
		  newClass = ByteUtility.addBytes(newClass,(byte)0xB1);               // return                   (fixed)
		// end code

		  // exception table
		  length = excpIndex[i].length;
		  newClass = ByteUtility.addBytes(newClass,(short)(1+length));        // exception_table_length   (variable)
		  for( int j = 0 ; j < length ; j++ )
		  { // catch exception types and rethrow
			newClass = ByteUtility.addBytes(newClass,(short)0);               // start_pc                 (fixed)
			if(propertyChangeFlag[i])
			{
			  newClass = ByteUtility.addBytes(newClass,(short)21);            // end_pc                   (fixed)
			  newClass = ByteUtility.addBytes(newClass,(short)22);            // handler_pc               (fixed)
			}
			else
			{
			  newClass = ByteUtility.addBytes(newClass,(short)19);            // end_pc                   (fixed)
			  newClass = ByteUtility.addBytes(newClass,(short)20);            // handler_pc               (fixed)
			}
			newClass = ByteUtility.addBytes(newClass,(short)excpIndex[i][j]); // catch_type               (variable)
		  }
		  // catch "exception" and trap it
		  newClass = ByteUtility.addBytes(newClass,(short)0);                 // start_pc                 (fixed)
		  if(propertyChangeFlag[i])
		  {
			newClass = ByteUtility.addBytes(newClass,(short)21);              // end_pc                   (fixed)
			newClass = ByteUtility.addBytes(newClass,(short)25);              // handler_pc               (fixed)
		  }
		  else
		  {
			newClass = ByteUtility.addBytes(newClass,(short)19);              // end_pc                   (fixed)
			newClass = ByteUtility.addBytes(newClass,(short)23);              // handler_pc               (fixed)
		  }
		  if(nonExceptionable)
		  { newClass = ByteUtility.addBytes(newClass,(short)26); }            // catch_type               (fixed)
		  else                                                                                            // or
		  { newClass = ByteUtility.addBytes(newClass,(short)23); }            // catch_type               (fixed)
		}
		else
		{ newClass = ByteUtility.addBytes(newClass,(short)0); }               // exception_table_length   (fixed)
		// attributes on the code attribute (none)
		newClass = ByteUtility.addBytes(newClass,(short)0);                   // attribute_count          (fixed)
		// end code attribute


	  }/* End for*/
	  // Class Attributes (none for this)
	  newClass = ByteUtility.addBytes(newClass,(short)0);                     // attribute_count          (fixed)
	  /* done */

          logger.debug("adapterName: " + finalAdapterClassName);
          logger.debug("cpCount: " + count + " = " + BASECPCOUNT + " + " +  cpCount);
          logger.debug("methodCount: " + (lms.length+1));
	  // output to disk class file
	  /* ****************************************************************************************** */

	  // now create the class and load it
	  // return the Class.

	  if (writeClassFile)
	  {
		try
		{
                    // removed "WRITEDIRECTORY+", as this path is already part of 'finalAdapterClassName'
		  FileOutputStream fos =  new FileOutputStream(finalAdapterClassName+".class");
		  fos.write(newClass);
		  fos.close();
		}
		catch(IOException ex)
		{
                    System.err.println(ex.getMessage());
                    ex.printStackTrace();
                }

		try
		{
		  Class ret = ldr.loadClass(finalAdapterClassName);
		  logger.debug("EventAdapterGenerator: " +
							 ret.getName() +
							 " dynamically generated");
		  return ret;
		}
		catch (ClassNotFoundException ex)
		{
                    System.err.println(ex.getMessage());
                    ex.printStackTrace();
                }
	  }

	  try
	  {
		Class ret = ldr.defineClass(finalAdapterClassName,newClass);
		logger.debug("EventAdapterGenerator: " +
                                       ret.getName() +
                                       " dynamically generated");
		return ret;
	  }
	  catch(Exception ex)
	  {
              System.err.println(ex.getMessage());
              ex.printStackTrace();
          }
	}
	return null;
  }
}
