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