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