1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 package org.apache.axis2.jaxbri;
21
22 import com.sun.codemodel.JCodeModel;
23 import com.sun.codemodel.writer.FileCodeWriter;
24 import com.sun.tools.xjc.api.ErrorListener;
25 import com.sun.tools.xjc.api.Mapping;
26 import com.sun.tools.xjc.api.Property;
27 import com.sun.tools.xjc.api.S2JJAXBModel;
28 import com.sun.tools.xjc.api.SchemaCompiler;
29 import com.sun.tools.xjc.api.XJC;
30 import com.sun.tools.xjc.BadCommandLineException;
31 import org.apache.axis2.description.AxisMessage;
32 import org.apache.axis2.description.AxisOperation;
33 import org.apache.axis2.description.AxisService;
34 import org.apache.axis2.util.SchemaUtil;
35 import org.apache.axis2.util.URLProcessor;
36 import org.apache.axis2.util.XMLUtils;
37 import org.apache.axis2.wsdl.WSDLConstants;
38 import org.apache.axis2.wsdl.WSDLUtil;
39 import org.apache.axis2.wsdl.codegen.CodeGenConfiguration;
40 import org.apache.axis2.wsdl.databinding.DefaultTypeMapper;
41 import org.apache.axis2.wsdl.databinding.JavaTypeMapper;
42 import org.apache.axis2.wsdl.databinding.TypeMapper;
43 import org.apache.axis2.wsdl.util.Constants;
44 import org.apache.commons.logging.Log;
45 import org.apache.commons.logging.LogFactory;
46 import org.apache.ws.commons.schema.XmlSchema;
47 import org.w3c.dom.Document;
48 import org.w3c.dom.Element;
49 import org.xml.sax.EntityResolver;
50 import org.xml.sax.InputSource;
51 import org.xml.sax.SAXException;
52 import org.xml.sax.SAXParseException;
53
54 import javax.xml.namespace.QName;
55 import javax.xml.transform.Result;
56 import javax.xml.transform.Transformer;
57 import javax.xml.transform.TransformerFactory;
58 import javax.xml.transform.dom.DOMSource;
59 import javax.xml.transform.stream.StreamResult;
60 import java.io;
61 import java.util;
62 import java.net.URLClassLoader;
63 import java.net.URL;
64
65 public class CodeGenerationUtility {
66 private static final Log log = LogFactory.getLog(CodeGenerationUtility.class);
67
68 public static final String BINDING_FILE_NAME = "bindingFileName";
69
70 /**
71 * @param additionalSchemas
72 * @throws RuntimeException
73 */
74 public static TypeMapper processSchemas(final List schemas,
75 Element[] additionalSchemas,
76 CodeGenConfiguration cgconfig)
77 throws RuntimeException {
78 try {
79
80 //check for the imported types. Any imported types are supposed to be here also
81 if (schemas == null || schemas.isEmpty()) {
82 //there are no types to be code generated
83 //However if the type mapper is left empty it will be a problem for the other
84 //processes. Hence the default type mapper is set to the configuration
85 return new DefaultTypeMapper();
86 }
87
88 final Map schemaToInputSourceMap = new HashMap();
89
90 //create the type mapper
91 JavaTypeMapper mapper = new JavaTypeMapper();
92
93 String baseURI = cgconfig.getBaseURI();
94 if (!baseURI.endsWith("/")){
95 baseURI = baseURI + "/";
96 }
97
98
99 for (int i = 0; i < schemas.size(); i++) {
100 XmlSchema schema = (XmlSchema)schemas.get(i);
101 InputSource inputSource =
102 new InputSource(new StringReader(getSchemaAsString(schema)));
103 //here we have to set a proper system ID. otherwise when processing the
104 // included schaemas for this schema we have a problem
105 // it creates the system ID using this target namespace value
106
107 inputSource.setSystemId(baseURI + "xsd" + i + ".xsd");
108 inputSource.setPublicId(schema.getTargetNamespace());
109 schemaToInputSourceMap.put(schema,inputSource);
110 }
111
112 File outputDir = new File(cgconfig.getOutputLocation(), "src");
113 outputDir.mkdir();
114
115 Map nsMap = cgconfig.getUri2PackageNameMap();
116 EntityResolver resolver = new EntityResolver() {
117 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
118 InputSource returnInputSource = null;
119 XmlSchema key = null;
120 for (Iterator iter = schemaToInputSourceMap.keySet().iterator();iter.hasNext();) {
121 key = (XmlSchema) iter.next();
122 String nsp = key.getTargetNamespace();
123 if (nsp != null && nsp.equals(publicId)) {
124
125 // when returning the input stream we have to always return a new
126 // input stream.
127 // sinc jaxbri internally consumes the input stream it gives an
128 // exception.
129 returnInputSource = new InputSource(new StringReader(getSchemaAsString(key)));
130 InputSource existingInputSource = (InputSource) schemaToInputSourceMap.get(key);
131 returnInputSource.setSystemId(existingInputSource.getSystemId());
132 returnInputSource.setPublicId(existingInputSource.getPublicId());
133 break;
134 }
135 }
136 if (returnInputSource == null){
137 // then we have to find this using the file system
138 if (systemId != null){
139 returnInputSource = new InputSource(systemId);
140 returnInputSource.setSystemId(systemId);
141 }
142 }
143 return returnInputSource;
144 }
145 };
146
147
148 Map properties = cgconfig.getProperties();
149 String bindingFileName = (String) properties.get(BINDING_FILE_NAME);
150
151 XmlSchema key = null;
152 for (Iterator schemaIter = schemaToInputSourceMap.keySet().iterator();
153 schemaIter.hasNext();) {
154
155 SchemaCompiler sc = XJC.createSchemaCompiler();
156 if (bindingFileName != null){
157 if (bindingFileName.endsWith(".jar")) {
158 scanEpisodeFile(new File(bindingFileName), sc);
159 } else {
160 InputSource inputSoruce = new InputSource(new FileInputStream(bindingFileName));
161 inputSoruce.setSystemId(new File(bindingFileName).toURI().toString());
162 sc.getOptions().addBindFile(inputSoruce);
163 }
164
165 }
166
167 key = (XmlSchema) schemaIter.next();
168
169 if (nsMap != null) {
170 Iterator iterator = nsMap.entrySet().iterator();
171 while(iterator.hasNext()){
172 Map.Entry entry = (Map.Entry) iterator.next();
173 String namespace = (String) entry.getKey();
174 String pkg = (String)nsMap.get(namespace);
175 registerNamespace(sc, namespace, pkg);
176 }
177 }
178
179 sc.setEntityResolver(resolver);
180
181 sc.setErrorListener(new ErrorListener(){
182 public void error(SAXParseException saxParseException) {
183 log.error(saxParseException.getMessage());
184 log.debug(saxParseException.getMessage(), saxParseException);
185 }
186
187 public void fatalError(SAXParseException saxParseException) {
188 log.error(saxParseException.getMessage());
189 log.debug(saxParseException.getMessage(), saxParseException);
190 }
191
192 public void warning(SAXParseException saxParseException) {
193 log.warn(saxParseException.getMessage());
194 log.debug(saxParseException.getMessage(), saxParseException);
195 }
196
197 public void info(SAXParseException saxParseException) {
198 log.info(saxParseException.getMessage());
199 log.debug(saxParseException.getMessage(), saxParseException);
200 }
201 });
202
203 sc.parseSchema((InputSource) schemaToInputSourceMap.get(key));
204
205 // Bind the XML
206 S2JJAXBModel jaxbModel = sc.bind();
207
208 if(jaxbModel == null){
209 throw new RuntimeException("Unable to generate code using jaxbri");
210 }
211
212 // Emit the code artifacts
213 JCodeModel codeModel = jaxbModel.generateCode(null, null);
214 FileCodeWriter writer = new FileCodeWriter(outputDir);
215 codeModel.build(writer);
216
217 Collection mappings = jaxbModel.getMappings();
218
219 Iterator iter = mappings.iterator();
220
221 while (iter.hasNext()) {
222 Mapping mapping = (Mapping)iter.next();
223 QName qn = mapping.getElement();
224 String typeName = mapping.getType().getTypeClass().fullName();
225
226 mapper.addTypeMappingName(qn, typeName);
227 }
228
229 //process the unwrapped parameters
230 if (!cgconfig.isParametersWrapped()) {
231 //figure out the unwrapped operations
232 List axisServices = cgconfig.getAxisServices();
233 for (Iterator servicesIter = axisServices.iterator(); servicesIter.hasNext();) {
234 AxisService axisService = (AxisService)servicesIter.next();
235 for (Iterator operations = axisService.getOperations();
236 operations.hasNext();) {
237 AxisOperation op = (AxisOperation)operations.next();
238
239 if (WSDLUtil.isInputPresentForMEP(op.getMessageExchangePattern())) {
240 AxisMessage message = op.getMessage(
241 WSDLConstants.MESSAGE_LABEL_IN_VALUE);
242 if (message != null &&
243 message.getParameter(Constants.UNWRAPPED_KEY) != null) {
244
245 Mapping mapping = jaxbModel.get(message.getElementQName());
246 List elementProperties = mapping.getWrapperStyleDrilldown();
247 for(int j = 0; j < elementProperties.size(); j++){
248 Property elementProperty = (Property) elementProperties.get(j);
249
250 QName partQName =
251 WSDLUtil.getPartQName(op.getName().getLocalPart(),
252 WSDLConstants.INPUT_PART_QNAME_SUFFIX,
253 elementProperty.elementName().getLocalPart());
254 //this type is based on a primitive type- use the
255 //primitive type name in this case
256 String fullJaveName =
257 elementProperty.type().fullName();
258 if (elementProperty.type().isArray()) {
259 fullJaveName = fullJaveName.concat("[]");
260 }
261 mapper.addTypeMappingName(partQName, fullJaveName);
262
263 if (elementProperty.type().isPrimitive()) {
264 mapper.addTypeMappingStatus(partQName, Boolean.TRUE);
265 }
266 if (elementProperty.type().isArray()) {
267 mapper.addTypeMappingStatus(partQName,
268 Constants.ARRAY_TYPE);
269 }
270 }
271 }
272 }
273
274 if (WSDLUtil.isOutputPresentForMEP(op.getMessageExchangePattern())) {
275 AxisMessage message = op.getMessage(
276 WSDLConstants.MESSAGE_LABEL_OUT_VALUE);
277 if (message != null &&
278 message.getParameter(Constants.UNWRAPPED_KEY) != null) {
279
280 Mapping mapping = jaxbModel.get(message.getElementQName());
281 List elementProperties = mapping.getWrapperStyleDrilldown();
282 for(int j = 0; j < elementProperties.size(); j++){
283 Property elementProperty = (Property) elementProperties.get(j);
284
285 QName partQName =
286 WSDLUtil.getPartQName(op.getName().getLocalPart(),
287 WSDLConstants.OUTPUT_PART_QNAME_SUFFIX,
288 elementProperty.elementName().getLocalPart());
289 //this type is based on a primitive type- use the
290 //primitive type name in this case
291 String fullJaveName =
292 elementProperty.type().fullName();
293 if (elementProperty.type().isArray()) {
294 fullJaveName = fullJaveName.concat("[]");
295 }
296 mapper.addTypeMappingName(partQName, fullJaveName);
297
298 if (elementProperty.type().isPrimitive()) {
299 mapper.addTypeMappingStatus(partQName, Boolean.TRUE);
300 }
301 if (elementProperty.type().isArray()) {
302 mapper.addTypeMappingStatus(partQName,
303 Constants.ARRAY_TYPE);
304 }
305 }
306 }
307 }
308 }
309 }
310 }
311 }
312
313 // Return the type mapper
314 return mapper;
315
316 } catch (Exception e) {
317 throw new RuntimeException(e);
318 }
319 }
320
321 private static void scanEpisodeFile(File jar, SchemaCompiler sc)
322 throws BadCommandLineException, IOException {
323
324 URLClassLoader ucl = new URLClassLoader(new URL[]{jar.toURL()});
325 Enumeration<URL> resources = ucl.findResources("META-INF/sun-jaxb.episode");
326 while (resources.hasMoreElements()) {
327 URL url = resources.nextElement();
328 sc.getOptions().addBindFile(new InputSource(url.toExternalForm()));
329 }
330
331 }
332
333
334 private static void registerNamespace(SchemaCompiler sc, String namespace, String pkgName) throws Exception {
335 Document doc = XMLUtils.newDocument();
336 Element rootElement = doc.createElement("schema");
337 rootElement.setAttribute("xmlns", "http://www.w3.org/2001/XMLSchema");
338 rootElement.setAttribute("xmlns:jaxb", "http://java.sun.com/xml/ns/jaxb");
339 rootElement.setAttribute("jaxb:version", "2.0");
340 rootElement.setAttribute("targetNamespace", namespace);
341 Element annoElement = doc.createElement("annotation");
342 Element appInfo = doc.createElement("appinfo");
343 Element schemaBindings = doc.createElement("jaxb:schemaBindings");
344 Element pkgElement = doc.createElement("jaxb:package");
345 pkgElement.setAttribute("name", pkgName);
346 annoElement.appendChild(appInfo);
347 appInfo.appendChild(schemaBindings);
348 schemaBindings.appendChild(pkgElement);
349 rootElement.appendChild(annoElement);
350 File file = File.createTempFile("customized",".xsd");
351 FileOutputStream stream = new FileOutputStream(file);
352 try {
353 Result result = new StreamResult(stream);
354 Transformer xformer = TransformerFactory.newInstance().newTransformer();
355 xformer.transform(new DOMSource(rootElement), result);
356 stream.flush();
357 stream.close();
358 } catch (Exception e) {
359 e.printStackTrace();
360 }
361 InputSource ins = new InputSource(file.toURI().toString());
362 sc.parseSchema(ins);
363 file.delete();
364 }
365
366 private static String extractNamespace(XmlSchema schema) {
367 String pkg;
368 pkg = schema.getTargetNamespace();
369 if (pkg == null) {
370 XmlSchema[] schemas2 = SchemaUtil.getAllSchemas(schema);
371 for (int j = 0; schemas2 != null && j < schemas2.length; j++) {
372 pkg = schemas2[j].getTargetNamespace();
373 if (pkg != null)
374 break;
375 }
376 }
377 if (pkg == null) {
378 pkg = URLProcessor.DEFAULT_PACKAGE;
379 }
380 pkg = URLProcessor.makePackageName(pkg);
381 return pkg;
382 }
383
384 private static String getSchemaAsString(XmlSchema schema) {
385 ByteArrayOutputStream baos = new ByteArrayOutputStream();
386 schema.write(baos);
387 return baos.toString();
388 }
389 }