001: /**
002: *
003: * Licensed to the Apache Software Foundation (ASF) under one or more
004: * contributor license agreements. See the NOTICE file distributed with
005: * this work for additional information regarding copyright ownership.
006: * The ASF licenses this file to You under the Apache License, Version 2.0
007: * (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package org.apache.geronimo.openejb.deployment;
018:
019: import org.apache.geronimo.common.DeploymentException;
020: import org.apache.geronimo.deployment.service.EnvironmentBuilder;
021: import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
022: import org.apache.geronimo.kernel.repository.Artifact;
023: import org.apache.geronimo.kernel.repository.Dependency;
024: import org.apache.geronimo.kernel.repository.Environment;
025: import org.apache.geronimo.openejb.xbeans.ejbjar.OpenejbEjbJarDocument;
026: import org.apache.geronimo.openejb.xbeans.ejbjar.OpenejbGeronimoEjbJarType;
027: import org.apache.geronimo.schema.SchemaConversionUtils;
028: import org.apache.geronimo.xbeans.javaee.EjbJarDocument;
029: import org.apache.geronimo.xbeans.javaee.EjbJarType;
030: import org.apache.openejb.jee.EjbJar;
031: import org.apache.openejb.jee.EnterpriseBean;
032: import org.apache.openejb.jee.PersistenceContextRef;
033: import org.apache.openejb.jee.PersistenceContextType;
034: import org.apache.openejb.jee.oejb2.ArtifactType;
035: import org.apache.openejb.jee.oejb2.DependencyType;
036: import org.apache.openejb.jee.oejb2.EnvironmentType;
037: import org.apache.openejb.jee.oejb2.GeronimoEjbJarType;
038: import org.apache.openejb.jee.oejb2.ImportType;
039: import org.apache.openejb.jee.oejb2.JaxbOpenejbJar2;
040: import org.apache.xmlbeans.XmlCursor;
041: import org.apache.xmlbeans.XmlDocumentProperties;
042: import org.apache.xmlbeans.XmlException;
043: import org.apache.xmlbeans.XmlObject;
044:
045: import javax.xml.bind.JAXBContext;
046: import javax.xml.bind.JAXBElement;
047: import javax.xml.bind.JAXBException;
048: import javax.xml.bind.Marshaller;
049: import javax.xml.bind.ValidationEvent;
050: import javax.xml.namespace.QName;
051: import java.io.ByteArrayOutputStream;
052: import java.io.File;
053: import java.io.FileOutputStream;
054: import java.io.InputStream;
055: import java.io.IOException;
056:
057: public final class XmlUtil {
058: public static final QName OPENEJBJAR_QNAME = OpenejbEjbJarDocument.type
059: .getDocumentElementName();
060: private static final QName CMP_VERSION = new QName(
061: SchemaConversionUtils.J2EE_NAMESPACE, "cmp-version");
062:
063: private XmlUtil() {
064: }
065:
066: public static <T> String marshal(T object)
067: throws DeploymentException {
068: try {
069: Class type = object.getClass();
070:
071: if (object instanceof JAXBElement) {
072: JAXBElement element = (JAXBElement) object;
073: type = element.getValue().getClass();
074: }
075:
076: JAXBContext ctx = JAXBContext.newInstance(type);
077: Marshaller marshaller = ctx.createMarshaller();
078:
079: ByteArrayOutputStream baos = new ByteArrayOutputStream();
080: marshaller.marshal(object, baos);
081:
082: String xml = new String(baos.toByteArray());
083: return xml;
084: } catch (JAXBException e) {
085: throw new DeploymentException(e);
086: }
087: }
088:
089: public static EjbJarType convertToXmlbeans(EjbJar ejbJar)
090: throws DeploymentException {
091: //
092: // it would be nice if Jaxb had a way to convert the object to a
093: // sax reader that could be fed directly into xmlbeans
094: //
095:
096: // the geronimo xml beans tree is totally broken... fix some obvious stuff here
097: for (EnterpriseBean enterpriseBean : ejbJar
098: .getEnterpriseBeans()) {
099: for (PersistenceContextRef ref : enterpriseBean
100: .getPersistenceContextRef()) {
101: if (ref.getPersistenceContextType() == PersistenceContextType.TRANSACTION) {
102: ref.setPersistenceContextType(null);
103: }
104: }
105: }
106:
107: // marshal to xml
108: String xml = marshal(ejbJar);
109: try {
110: // parse the xml
111: EjbJarDocument ejbJarDoc = convertToEJBSchema(XmlBeansUtil
112: .parse(xml));
113: EjbJarType ejbJarType = ejbJarDoc.getEjbJar();
114: return ejbJarType;
115: } catch (XmlException e) {
116: throw new DeploymentException("Error parsing ejb-jar.xml",
117: e);
118: }
119:
120: }
121:
122: public static OpenejbGeronimoEjbJarType convertToXmlbeans(
123: GeronimoEjbJarType geronimoEjbJarType)
124: throws DeploymentException {
125: //
126: // it would be nice if Jaxb had a way to convert the object to a
127: // sax reader that could be fed directly into xmlbeans
128: //
129: JAXBElement root = new JAXBElement(
130: new QName(
131: "http://geronimo.apache.org/xml/ns/j2ee/ejb/openejb-2.0",
132: "ejb-jar"), GeronimoEjbJarType.class,
133: geronimoEjbJarType);
134:
135: // marshal to xml
136:
137: String xml = marshal(root);
138:
139: try {
140: XmlObject xmlObject = XmlBeansUtil.parse(xml);
141:
142: OpenejbGeronimoEjbJarType geronimoOpenejb = (OpenejbGeronimoEjbJarType) SchemaConversionUtils
143: .fixGeronimoSchema(xmlObject, OPENEJBJAR_QNAME,
144: OpenejbGeronimoEjbJarType.type);
145: return geronimoOpenejb;
146: } catch (Throwable e) {
147: String filePath = "<error: could not be written>";
148: try {
149: File tempFile = File.createTempFile("openejb-jar-",
150: ".xml");
151: try {
152: FileOutputStream out = new FileOutputStream(
153: tempFile);
154: out.write(xml.getBytes());
155: out.close();
156: } catch (Exception weTried) {
157: }
158: filePath = tempFile.getAbsolutePath();
159: } catch (IOException notImportant) {
160: }
161:
162: throw new DeploymentException(
163: "Error parsing geronimo-openejb.xml with xmlbeans. For debug purposes, XML content written to: "
164: + filePath, e);
165: }
166: }
167:
168: public static Environment buildEnvironment(
169: EnvironmentType environmentType,
170: Environment defaultEnvironment) {
171: Environment environment = new Environment();
172: if (environmentType != null) {
173: if (environmentType.getModuleId() != null) {
174: environment.setConfigId(toArtifact(environmentType
175: .getModuleId(), null));
176: }
177:
178: if (environmentType.getDependencies() != null) {
179: for (DependencyType dependencyType : environmentType
180: .getDependencies().getDependency()) {
181: Dependency dependency = toDependency(dependencyType);
182: environment.addDependency(dependency);
183: }
184: }
185: environment.setInverseClassLoading(environmentType
186: .isInverseClassloading());
187: environment.setSuppressDefaultEnvironment(environmentType
188: .isSuppressDefaultEnvironment());
189: if (environmentType.getHiddenClasses() != null) {
190: environment.setHiddenClasses(environmentType
191: .getHiddenClasses().getFilter());
192: }
193: if (environmentType.getNonOverridableClasses() != null) {
194: environment.setNonOverrideableClasses(environmentType
195: .getNonOverridableClasses().getFilter());
196: }
197: }
198: if (!environment.isSuppressDefaultEnvironment()) {
199: EnvironmentBuilder.mergeEnvironments(environment,
200: defaultEnvironment);
201: }
202:
203: return environment;
204: }
205:
206: private static Dependency toDependency(DependencyType dependencyType) {
207: Artifact artifact = toArtifact(dependencyType, null);
208: if (ImportType.CLASSES.equals(dependencyType.getImport())) {
209: return new Dependency(
210: artifact,
211: org.apache.geronimo.kernel.repository.ImportType.CLASSES);
212: } else if (ImportType.SERVICES.equals(dependencyType
213: .getImport())) {
214: return new Dependency(
215: artifact,
216: org.apache.geronimo.kernel.repository.ImportType.SERVICES);
217: } else if (dependencyType.getImport() == null) {
218: return new Dependency(
219: artifact,
220: org.apache.geronimo.kernel.repository.ImportType.ALL);
221: } else {
222: throw new IllegalArgumentException("Unknown import type: "
223: + dependencyType.getImport());
224: }
225: }
226:
227: private static Artifact toArtifact(ArtifactType artifactType,
228: String defaultType) {
229: String groupId = artifactType.getGroupId();
230: String type = artifactType.getType();
231: if (type == null)
232: type = defaultType;
233: String artifactId = artifactType.getArtifactId();
234: String version = artifactType.getVersion();
235: return new Artifact(groupId, artifactId, version, type);
236: }
237:
238: public static GeronimoEjbJarType createDefaultPlan(String name,
239: EjbJar ejbJar) {
240: String id = ejbJar.getId();
241: if (id == null) {
242: id = name;
243: if (id.endsWith(".jar")) {
244: id = id.substring(0, id.length() - 4);
245: }
246: if (id.endsWith("/")) {
247: id = id.substring(0, id.length() - 1);
248: }
249: }
250:
251: ArtifactType artifactType = new ArtifactType();
252: artifactType.setArtifactId(id);
253:
254: EnvironmentType environmentType = new EnvironmentType();
255: environmentType.setModuleId(artifactType);
256:
257: GeronimoEjbJarType geronimoEjbJarType = new GeronimoEjbJarType();
258: geronimoEjbJarType.setEnvironment(environmentType);
259:
260: return geronimoEjbJarType;
261: }
262:
263: public static String getJ2eeStringValue(
264: org.apache.geronimo.xbeans.javaee.String string) {
265: if (string == null) {
266: return null;
267: }
268: return string.getStringValue();
269: }
270:
271: public static class ValidationEventHandler implements
272: javax.xml.bind.ValidationEventHandler {
273: public boolean handleEvent(ValidationEvent validationEvent) {
274: System.out.println(validationEvent.getMessage());
275: return true;
276: }
277: }
278:
279: // TODO I don't think we need this since openejb will always generate the newest spec,
280: // but this code is doing more than just schema conversion, it is also converting message
281: // driven properties to activation-config
282: // coerce to newest spec... this shouldn't be necessary as the jaxb tree always creates the newest spec
283: public static EjbJarDocument convertToEJBSchema(XmlObject xmlObject)
284: throws XmlException {
285: if (EjbJarDocument.type.equals(xmlObject.schemaType())) {
286: // XmlBeansUtil.validateDD(xmlObject);
287: return (EjbJarDocument) xmlObject;
288: }
289: XmlCursor cursor = xmlObject.newCursor();
290: XmlCursor moveable = xmlObject.newCursor();
291: //cursor is intially located before the logical STARTDOC token
292: try {
293: cursor.toFirstChild();
294: if (EjbJarDocument.type.getDocumentElementName()
295: .getNamespaceURI().equals(
296: cursor.getName().getNamespaceURI())) {
297: XmlObject result = xmlObject
298: .changeType(EjbJarDocument.type);
299: // XmlBeansUtil.validateDD(result);
300: return (EjbJarDocument) result;
301: }
302: // deployment descriptor is probably in EJB 1.1 or 2.0 format
303: XmlDocumentProperties xmlDocumentProperties = cursor
304: .documentProperties();
305: String publicId = xmlDocumentProperties
306: .getDoctypePublicId();
307: String cmpVersion;
308: if ("-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"
309: .equals(publicId)) {
310: cmpVersion = "1.x";
311: } else if ("-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
312: .equals(publicId)) {
313: cmpVersion = null;//2.x is the default "2.x";
314: } else {
315: throw new XmlException("Unrecognized document type: "
316: + publicId);
317: }
318: String schemaLocationURL = "http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd";
319: String version = "2.1";
320: SchemaConversionUtils.convertToSchema(cursor,
321: SchemaConversionUtils.J2EE_NAMESPACE,
322: schemaLocationURL, version);
323: //play with message-driven
324: cursor.toStartDoc();
325: convertBeans(cursor, moveable, cmpVersion);
326: } finally {
327: cursor.dispose();
328: moveable.dispose();
329: }
330: XmlObject result = xmlObject.changeType(EjbJarDocument.type);
331: if (result != null) {
332: XmlBeansUtil.validateDD(result);
333: return (EjbJarDocument) result;
334: }
335: XmlBeansUtil.validateDD(xmlObject);
336: return (EjbJarDocument) xmlObject;
337: }
338:
339: private static void convertBeans(XmlCursor cursor,
340: XmlCursor moveable, String cmpVersion) {
341: cursor.toChild(SchemaConversionUtils.J2EE_NAMESPACE, "ejb-jar");
342: cursor.toChild(SchemaConversionUtils.J2EE_NAMESPACE,
343: "enterprise-beans");
344: if (cursor.toFirstChild()) {
345: //there's at least one ejb...
346: do {
347: cursor.push();
348: String type = cursor.getName().getLocalPart();
349: if ("session".equals(type)) {
350: cursor.toChild(
351: SchemaConversionUtils.J2EE_NAMESPACE,
352: "transaction-type");
353: cursor.toNextSibling();
354: SchemaConversionUtils
355: .convertToJNDIEnvironmentRefsGroup(
356: SchemaConversionUtils.J2EE_NAMESPACE,
357: cursor, moveable);
358: } else if ("entity".equals(type)) {
359: cursor.toChild(
360: SchemaConversionUtils.J2EE_NAMESPACE,
361: "persistence-type");
362: String persistenceType = cursor.getTextValue();
363: //reentrant is the last required tag before jndiEnvironmentRefsGroup
364: cursor.toNextSibling(
365: SchemaConversionUtils.J2EE_NAMESPACE,
366: "reentrant");
367: //Convert 2.0 True/False to true/false for 2.1
368: cursor.setTextValue(cursor.getTextValue()
369: .toLowerCase());
370: if (cmpVersion != null
371: && !cursor.toNextSibling(CMP_VERSION)
372: && "Container".equals(persistenceType)) {
373: cursor.toNextSibling();
374: cursor.insertElementWithText(CMP_VERSION,
375: cmpVersion);
376: }
377:
378: cursor.toNextSibling(
379: SchemaConversionUtils.J2EE_NAMESPACE,
380: "abstract-schema-name");
381: while (cursor.toNextSibling(
382: SchemaConversionUtils.J2EE_NAMESPACE,
383: "cmp-field")) {
384: }
385: cursor.toNextSibling(
386: SchemaConversionUtils.J2EE_NAMESPACE,
387: "primkey-field");
388: cursor.toNextSibling();
389: SchemaConversionUtils
390: .convertToJNDIEnvironmentRefsGroup(
391: SchemaConversionUtils.J2EE_NAMESPACE,
392: cursor, moveable);
393: } else if ("message-driven".equals(type)) {
394: cursor.toFirstChild();
395: if (cursor.toNextSibling(
396: SchemaConversionUtils.J2EE_NAMESPACE,
397: "messaging-type")) {
398: cursor.toNextSibling(
399: SchemaConversionUtils.J2EE_NAMESPACE,
400: "transaction-type");
401: } else {
402: cursor.toNextSibling(
403: SchemaConversionUtils.J2EE_NAMESPACE,
404: "transaction-type");
405: //insert messaging-type (introduced in EJB 2.1 spec) before transaction-type
406: cursor.insertElementWithText("messaging-type",
407: SchemaConversionUtils.J2EE_NAMESPACE,
408: "javax.jms.MessageListener");
409: //cursor still on transaction-type
410: }
411: if (!cursor.toNextSibling(
412: SchemaConversionUtils.J2EE_NAMESPACE,
413: "activation-config")) {
414: //skip transaction-type
415: cursor.toNextSibling();
416: //convert EJB 2.0 elements to activation-config-properties.
417: moveable.toCursor(cursor);
418: cursor.push();
419: cursor.beginElement("activation-config",
420: SchemaConversionUtils.J2EE_NAMESPACE);
421: boolean hasProperties = addActivationConfigProperty(
422: moveable, cursor, "message-selector",
423: "messageSelector");
424: hasProperties |= addActivationConfigProperty(
425: moveable, cursor, "acknowledge-mode",
426: "acknowledgeMode");
427: if (new QName(
428: SchemaConversionUtils.J2EE_NAMESPACE,
429: "message-driven-destination")
430: .equals(moveable.getName())
431: || moveable
432: .toNextSibling(
433: SchemaConversionUtils.J2EE_NAMESPACE,
434: "message-driven-destination")) {
435: moveable.push();
436: moveable.toFirstChild();
437: hasProperties |= addActivationConfigProperty(
438: moveable, cursor,
439: "destination-type",
440: "destinationType");
441: hasProperties |= addActivationConfigProperty(
442: moveable, cursor,
443: "subscription-durability",
444: "subscriptionDurability");
445: moveable.pop();
446: moveable.removeXml();
447: }
448: cursor.pop();
449: if (!hasProperties) {
450: //the activation-config element that we created is empty so delete it
451: cursor.toPrevSibling();
452: cursor.removeXml();
453: //cursor should now be at first element in JNDIEnvironmentRefsGroup
454: }
455: } else {
456: //cursor pointing at activation-config
457: cursor.toNextSibling();
458: //cursor should now be at first element in JNDIEnvironmentRefsGroup
459: }
460: SchemaConversionUtils
461: .convertToJNDIEnvironmentRefsGroup(
462: SchemaConversionUtils.J2EE_NAMESPACE,
463: cursor, moveable);
464: }
465: cursor.pop();
466: } while (cursor.toNextSibling());
467: }
468: }
469:
470: private static boolean addActivationConfigProperty(
471: XmlCursor moveable, XmlCursor cursor, String elementName,
472: String propertyName) {
473: QName name = new QName(SchemaConversionUtils.J2EE_NAMESPACE,
474: elementName);
475: if (name.equals(moveable.getName())
476: || moveable.toNextSibling(name)) {
477: cursor.push();
478: cursor.beginElement("activation-config-property",
479: SchemaConversionUtils.J2EE_NAMESPACE);
480: cursor.insertElementWithText(
481: "activation-config-property-name",
482: SchemaConversionUtils.J2EE_NAMESPACE, propertyName);
483: cursor.insertElementWithText(
484: "activation-config-property-value",
485: SchemaConversionUtils.J2EE_NAMESPACE, moveable
486: .getTextValue());
487: moveable.removeXml();
488: cursor.pop();
489: cursor.toNextSibling();
490: return true;
491: }
492: return false;
493: }
494: }
|