1 package com.soapuser.soap.server.util;
2
3 import java.lang.reflect.*;
4 import java.util.*;
5 import java.io.*;
6
7 /**
8 * This class generates a DeploymentDescriptor xml file for SOAP 2.0
9 * It takes a rather crude option to generate bean mapping entries
10 * for all classes that are NOT in the java.lang package. This may be
11 * refined later
12 */
13 public class DDGenerator {
14
15 private static StringBuffer replace( StringBuffer original, String toBeReplaced, String by )
16 throws Exception
17 {
18 String s = original.toString();
19 int start = s.indexOf( toBeReplaced );
20 if ( start == -1 )
21 throw new Exception( "Could not find string " + toBeReplaced + " in " + s );
22 return original.replace( start, start+toBeReplaced.length(), by );
23 }
24
25 private static StringBuffer readTemplate( String fileName )
26 throws Exception
27 {
28 File file = new File( fileName );
29 InputStream is = new BufferedInputStream( new FileInputStream( file ) );
30 byte[] buffer = new byte[ (int)file.length() ];
31 is.read( buffer );
32 is.close();
33 return new StringBuffer( new String( buffer ) );
34 }
35
36 private static void writeDD( String fileName, String content )
37 throws Exception
38 {
39 File file = new File( fileName );
40 OutputStream os = new BufferedOutputStream( new FileOutputStream( file ) );
41 os.write( content.getBytes() );
42 os.close();
43 }
44
45 private static String getLastNamePart( String className ) {
46 int dot = className.lastIndexOf( '.' );
47 if ( dot == -1 )
48 return className;
49 return className.substring( dot+1 );
50 }
51
52 /**
53 * utility:
54 * - if clazz is an array, loops to find the base type
55 * - if clazz is 'a proper bean to be serialized', returns it
56 * - otherwise return null
57 */
58 private static Class getGenuineClass( Class clazz ) {
59 while ( clazz.isArray() )
60 clazz = clazz.getComponentType();
61 if ( clazz.isPrimitive() || clazz.getName().startsWith( "java.lang." ) )
62 return null;
63 return clazz;
64 }
65
66 /**
67 * returns a String corresponding to the mapping of a particular bean
68 */
69 private static String generateMapping(
70 String template, Class service, Class beanClass, Set beansDone )
71 throws Exception
72 {
73 if ( beanClass == null || beansDone.contains( beanClass ) )
74 return "";
75 StringBuffer work = new StringBuffer( template ); // clone
76 replace( work, "%serviceName%", getLastNamePart( service.getName() ) );
77 replace( work, "%fullyQualifiedBeanClassName%", beanClass.getName() );
78 replace( work, "%fullyQualifiedBeanClassName%", beanClass.getName() ); // x2!
79 beansDone.add( beanClass );
80 return work.toString();
81 }
82
83 /**
84 * returns a String corresponding to the mapping of all the bean's subtypes
85 */
86 private static String generateSubtypeMappings(
87 String template, Class service, Class beanClass, Set beansDone )
88 throws Exception
89 {
90 if ( beanClass == null )
91 return "";
92 Field[] fields = beanClass.getDeclaredFields();
93 StringBuffer b = new StringBuffer();
94 Class subBeanClass;
95
96 //For each field in the bean
97 for ( int i=0; i<fields.length; i++ )
98 {
99 //take note of the field type,
100 subBeanClass = getGenuineClass( fields[i].getType() );
101
102 //generate mapping for it
103 b.append( generateMapping( template, service, subBeanClass, beansDone ) );
104
105 //and (recursively) for its subtypes
106 b.append( generateSubtypeMappings( template, service, subBeanClass, beansDone ) );
107 }
108 return b.toString();
109 }
110
111 /**
112 * returns a String corresponding to the mapping of all the service's beans
113 */
114 private static String generateMappings( String template, Class service )
115 throws Exception
116 {
117 Method[] methods = service.getMethods();
118 Set beansDone = new HashSet();
119 StringBuffer b = new StringBuffer();
120 Class beanClass;
121
122 //For each method
123 for ( int i=0; i<methods.length; i++ )
124 {
125 //take note of the return type,
126 beanClass = getGenuineClass( methods[i].getReturnType() );
127
128 //generate mapping for it,
129 b.append( generateMapping( template, service, beanClass, beansDone ) );
130
131 //and (recursively) for its subtypes.
132 b.append( generateSubtypeMappings( template, service, beanClass, beansDone ) );
133
134 //For each parameter of the method
135 for ( int j=0; j<methods[i].getParameterTypes().length; j++ )
136 {
137 //take note of the parameter type
138 beanClass = getGenuineClass( methods[i].getParameterTypes()[j] );
139
140 //generate a mapping for it
141 b.append( generateMapping( template, service, beanClass, beansDone ) );
142
143 //and (recursively for its subtypes
144 b.append( generateSubtypeMappings( template, service, beanClass, beansDone ) );
145 }
146 }
147 return b.toString();
148 }
149
150 /**
151 * uses DD xml templates where variables to be expanded are surrounded by %
152 * - by definition, all public methods of the claa passed are exported
153 * - by definition, all Objects passed to or returned from these methods are exported
154 */
155 public static void generateDD(
156 String serviceName,
157 String implementationName,
158 String ddFileName )
159 throws Exception
160 {
161 String ddTemplateFileName = "DeploymentDescriptorTemplate.xml";
162 String ddMappingEntryTemplateFileName = "DDMappingEntryTemplate.xml";
163 Class service = Class.forName( serviceName );
164 Class implementation = Class.forName( implementationName );
165 // find out methods
166 Method[] methods = service.getMethods();
167 StringBuffer spaceDelimitedMethodNameList = new StringBuffer();
168 for ( int i=0; i<methods.length; i++ )
169 spaceDelimitedMethodNameList.append( methods[i].getName() ).append( ' ' );
170
171 // produce the header
172 StringBuffer dd = readTemplate( ddTemplateFileName );
173 replace( dd, "%serviceName%", getLastNamePart( service.getName() ) );
174 replace( dd, "%spaceDelimitedMethodNameList%", spaceDelimitedMethodNameList.toString() );
175 replace( dd, "%fullyQualifiedImplementationClassName%", implementation.getName() );
176
177 // find out exchanged beans: we have as many beans as distinct returned or passed classes
178 StringBuffer dm = readTemplate( ddMappingEntryTemplateFileName );
179 String dmOut = generateMappings( dm.toString(), service );
180
181 // produce the big string
182 replace( dd, "%mappingEntryList%", dmOut.toString() );
183
184 // write the DeploymentDescriptor
185 writeDD( ddFileName, dd.toString() );
186
187 // echo the string for convenience
188 System.out.println( dd.toString() );
189 System.out.println( "Wrote " + ddFileName );
190 }
191
192 public static void main( String[] args )
193 throws Exception
194 {
195 if ( args.length < 3 )
196 System.out.println( "Pass <intf. class name> <impl. class name> <output_file_name>" );
197 else {
198 generateDD( args[0], args[1], args[2] );
199 if ( args.length >= 4 ) {
200 org.apache.soap.server.ServiceManagerClient.main(
201 new String[] {
202 "http://localhost:8080/soap/servlet/rpcrouter",
203 "deploy",
204 args[2]
205 }
206 );
207 System.out.println( "Deployed " + args[2] );
208 }
209 }
210 }
211 }