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 }