From 2516f5229156bbc3514026e9d69dbea923f6a474 Mon Sep 17 00:00:00 2001 From: David Ezzio Date: Wed, 23 Jun 2010 20:23:28 +0000 Subject: [PATCH] OpenJPA-1610: Added XMLVersionParser from trunk and logic in PersistenceProductDerivation to use it. This allows OpenJPA 1.1.x to play well with JPA 2.0 providers by accepting that it cannot service any persistence unit that uses a persistence.xml whose version is not "1.0". Also altered POMs to decline external and dated Xerces parser dependency and use instead the Xerces parser of the JDK for the OpenJPA test cases. git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/1.1.x@957329 13f79535-47bb-0310-9956-ffa450edef68 --- openjpa-examples/pom.xml | 7 ++ .../openjpa/lib/meta/XMLVersionParser.java | 85 ++++++++++++++ openjpa-persistence-jdbc/pom.xml | 7 ++ .../openjpa/persistence/CaptureStream.java | 48 ++++++++ .../openjpa/persistence/ViewStream.java | 58 +++++++++ .../xml/TestSchemaVersionValidation.java | 111 ++++++++++++++++++ .../openjpa/persistence/xml/orm_2_0.xml | 47 ++++++++ .../xml/persistence-1_0-orm-2_0.xml | 30 +++++ .../persistence/xml/persistence-2_0.xml | 29 +++++ .../xml/persistence-no-version.xml | 29 +++++ .../PersistenceProductDerivation.java | 91 +++++++++++++- .../persistence/PersistenceProviderImpl.java | 2 +- .../openjpa/persistence/localizer.properties | 7 +- 13 files changed, 544 insertions(+), 7 deletions(-) create mode 100644 openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/XMLVersionParser.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/CaptureStream.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/ViewStream.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xml/TestSchemaVersionValidation.java create mode 100644 openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/orm_2_0.xml create mode 100644 openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/persistence-1_0-orm-2_0.xml create mode 100644 openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/persistence-2_0.xml create mode 100644 openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/persistence-no-version.xml diff --git a/openjpa-examples/pom.xml b/openjpa-examples/pom.xml index 1d8e5b78c..71cc41fb7 100644 --- a/openjpa-examples/pom.xml +++ b/openjpa-examples/pom.xml @@ -48,6 +48,13 @@ commons-dbcp commons-dbcp test + + + + xerces + xerces + + commons-collections diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/XMLVersionParser.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/XMLVersionParser.java new file mode 100644 index 000000000..1ae225f1b --- /dev/null +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/meta/XMLVersionParser.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.lib.meta; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +/** + * Custom non-validating SAX parser which can be used to get the version and + * schema location attributes from the root node. + * + * @author Jeremy Bauer + * @nojavadoc + */ +public class XMLVersionParser extends XMLMetaDataParser { + + public static final String VERSION_1_0 = "1.0"; + public static final String VERSION_2_0 = "2.0"; + + static private final String VERSION_ATTR = "version"; + static private final String XSI_NS = + "http://www.w3.org/2001/XMLSchema-instance"; + static private final String SCHEMA_LOCATION = "schemaLocation"; + + private String _rootElement; + private String _version; + private String _schemaLocation; + + public XMLVersionParser(String rootElement) { + _rootElement = rootElement; + setCaching(false); + setValidating(false); + setParseText(false); + } + + @Override + protected void endElement(String name) throws SAXException { + + } + + @Override + protected boolean startElement(String name, Attributes attrs) + throws SAXException { + if (name.equals(_rootElement)) { + // save the version and schema location attributes + _version = attrs.getValue("", VERSION_ATTR); + _schemaLocation = attrs.getValue(XSI_NS, SCHEMA_LOCATION); + // ignore remaining content + ignoreContent(true); + } + return false; + } + + /** + * Get the string value of the version attribute on the root element + * @return doc version + */ + public String getVersion() { + return _version; + } + + /** + * Get the string value of the schema location attribute on the root element + * @return doc schema location + */ + public String getSchemaLocation() { + return _schemaLocation; + } +} diff --git a/openjpa-persistence-jdbc/pom.xml b/openjpa-persistence-jdbc/pom.xml index 0d47e4218..7e97ea47a 100644 --- a/openjpa-persistence-jdbc/pom.xml +++ b/openjpa-persistence-jdbc/pom.xml @@ -254,6 +254,13 @@ commons-dbcp 1.2.1 test + + + + xerces + xerces + + commons-collections diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/CaptureStream.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/CaptureStream.java new file mode 100644 index 000000000..a4e8b34cc --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/CaptureStream.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence; + +import java.io.ByteArrayOutputStream; + +public class CaptureStream extends ByteArrayOutputStream { + + public boolean contains(String str) { + if (str == null) + return false; + if (str.length() == 0) + return true; + + boolean retv = false; + byte [] bytes = str.getBytes(); + + // search by brute force + KeepSearching: for (int x = 0; x < buf.length; x++) { + for (int y = 0; y < bytes.length; y++) { + if (bytes[y] != buf[x + y]) + // not here, keep searching + continue KeepSearching; + } + + // found it + retv = true; + break; + } + return retv; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/ViewStream.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/ViewStream.java new file mode 100644 index 000000000..d1db4e25b --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/ViewStream.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence; + +import java.io.IOException; +import java.io.PrintStream; + +public class ViewStream extends PrintStream { + + private CaptureStream captureStream; + private PrintStream original; + + public ViewStream(PrintStream original) { + super(original); + this.original = original; + this.captureStream = new CaptureStream(); + } + + @Override + public void flush() { + super.flush(); + try { + captureStream.flush(); + } catch (IOException e) { + // do nothing + } + } + + @Override + public void write(byte[] buf, int off, int len) { + super.write(buf, off, len); + captureStream.write(buf, off, len); + } + + public PrintStream getOriginal() { + return original; + } + + public boolean contains(String str) { + return captureStream.contains(str); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xml/TestSchemaVersionValidation.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xml/TestSchemaVersionValidation.java new file mode 100644 index 000000000..788f2f5c2 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xml/TestSchemaVersionValidation.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.xml; + +import java.util.MissingResourceException; + +import javax.persistence.EntityManager; + +import junit.framework.TestCase; + +import org.apache.openjpa.persistence.ViewStream; +import org.apache.openjpa.persistence.OpenJPAEntityManager; +import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory; +import org.apache.openjpa.persistence.OpenJPAPersistence; + +/** + * These test cases come from the branch of OpenJPA that implements JPA 2.0. + * The purpose here is simply to ensure that no 2.0 persistence.xml will fail + * to be detected. + */ +public class TestSchemaVersionValidation extends TestCase { + + private ViewStream viewStream; + + @Override + protected void setUp() throws Exception { + System.setErr(viewStream = new ViewStream(System.err)); + } + + @Override + protected void tearDown() throws Exception { + if (viewStream != null) + System.setErr(viewStream.getOriginal()); + } + + /** + * Verify that a null will be returned and an informative error message will be + * issued when attempting to read a Version 2.0 persistence.xml document + */ + public void test2_0PersistenceXml() { + OpenJPAEntityManagerFactory emf = null; + try { + emf = OpenJPAPersistence.createEntityManagerFactory("XSDTest", + "org/apache/openjpa/persistence/xml/persistence-2_0.xml"); + fail("Did not throw exception"); + } catch (MissingResourceException e) { + // expected + } finally { + if (emf != null) + emf.close(); + } + + assertTrue("Did not find expected warning in System.err print stream", + viewStream.contains("This version of OpenJPA cannot read a " + + "persistence.xml document with a version different from \"1.0\"")); + } + + /** + * Verify that we will get back a null EMF if we attempt to load a + * 2.0 ORM within a 1.0 persistence.xml. + */ + public void test1_0Persistence2_0OrmXml() { + OpenJPAEntityManagerFactory emf = null; + + try { + emf = OpenJPAPersistence.createEntityManagerFactory("XSDTest", + "org/apache/openjpa/persistence/xml/persistence-2_0-orm-1_0.xml"); + assertNull(emf); + } finally { + if (emf != null) + emf.close(); + } + } + + /** + * Verify that a persistence.xml document without a version tag will be flagged + * as non-conforming. + */ + public void testPersistenceWithoutVersion() { + OpenJPAEntityManagerFactory emf = null; + try { + emf = OpenJPAPersistence.createEntityManagerFactory("XSDTest", + "org/apache/openjpa/persistence/xml/persistence-no-version.xml"); + } catch (MissingResourceException e) { + // expected + } finally { + if (emf != null) + emf.close(); + } + + assertTrue("Did not find expected warning in error stream", + viewStream.contains("This version of OpenJPA cannot read a " + + "persistence.xml document with a version different from \"1.0\"")); + } +} diff --git a/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/orm_2_0.xml b/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/orm_2_0.xml new file mode 100644 index 000000000..07c9c40ae --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/orm_2_0.xml @@ -0,0 +1,47 @@ + + + + + + This is an orm 2.0 element + + + + org.apache.openjpa.persistence.xml + + + + select o from SimpleXml o + + + select o from SimpleXmlEntity o + + + + + + + + + + diff --git a/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/persistence-1_0-orm-2_0.xml b/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/persistence-1_0-orm-2_0.xml new file mode 100644 index 000000000..39b9bc1e8 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/persistence-1_0-orm-2_0.xml @@ -0,0 +1,30 @@ + + + + + PU for schema validation testing + org.apache.openjpa.persistence.PersistenceProviderImpl + org/apache/openjpa/persistence/xml/orm_2_0.xml + + \ No newline at end of file diff --git a/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/persistence-2_0.xml b/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/persistence-2_0.xml new file mode 100644 index 000000000..833640e40 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/persistence-2_0.xml @@ -0,0 +1,29 @@ + + + + + PU for schema validation testing + org.apache.openjpa.persistence.PersistenceProviderImpl + + \ No newline at end of file diff --git a/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/persistence-no-version.xml b/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/persistence-no-version.xml new file mode 100644 index 000000000..bbc3b8934 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/xml/persistence-no-version.xml @@ -0,0 +1,29 @@ + + + + + PU for schema validation testing + org.apache.openjpa.persistence.PersistenceProviderImpl + + \ No newline at end of file diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java index c3a535c45..d687190c6 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java @@ -45,6 +45,7 @@ import org.apache.openjpa.lib.conf.MapConfigurationProvider; import org.apache.openjpa.lib.conf.ProductDerivations; import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.meta.XMLMetaDataParser; +import org.apache.openjpa.lib.meta.XMLVersionParser; import org.apache.openjpa.lib.util.J2DoPrivHelper; import org.apache.openjpa.lib.util.Localizer; import org.xml.sax.Attributes; @@ -192,6 +193,8 @@ public class PersistenceProductDerivation throws IOException { if (!file.getName().endsWith(".xml")) return null; + if (!isVersionOnePersistenceDoc(file)) + return null; ConfigurationParser parser = new ConfigurationParser(null); parser.parse(file); @@ -206,6 +209,9 @@ public class PersistenceProductDerivation @Override public List getAnchorsInFile(File file) throws IOException { + if (!isVersionOnePersistenceDoc(file)) + return null; + ConfigurationParser parser = new ConfigurationParser(null); try { parser.parse(file); @@ -233,7 +239,8 @@ public class PersistenceProductDerivation List urls = getResourceURLs(resource, loader); if (urls != null) { for (URL url : urls) { - parser.parse(url); + if (isVersionOnePersistenceDoc(url)) + parser.parse(url); } } return getUnitNames(parser); @@ -356,8 +363,10 @@ public class PersistenceProductDerivation List pinfos = new ArrayList(); for (URL url : urls) { - parser.parse(url); - pinfos.addAll((List) parser.getResults()); + if (isVersionOnePersistenceDoc(url)) { + parser.parse(url); + pinfos.addAll((List) parser.getResults()); + } } return findUnit(pinfos, name, loader); } @@ -431,6 +440,78 @@ public class PersistenceProductDerivation System.err.println(msg); } + /** + * Return true if the version is specified as version "1.0". + * Return false if it is specified as anything else + * + * By calling this method, the caller can avoid parsing + * persistence.xml documents that are some other version. + */ + public boolean isVersionOnePersistenceDoc(URL url) { + + // default return value + boolean retv = true; + + // peek at the doc to determine the version + XMLVersionParser vp = new XMLVersionParser("persistence"); + try { + vp.parse(url); + String versionStr = vp.getVersion(); + + // if the versionStr is not equal to "1.0" return false + if (!XMLVersionParser.VERSION_1_0.equals(versionStr)) { + retv = false; + log(_loc.get("version-limitation", (versionStr == null ? "" : versionStr), + url.toString()).getMessage()); + } + } catch (Exception t) { + String msg = "Exception: " + t.getClass().getName() + ": "; + String m = t.getLocalizedMessage(); + msg += (StringUtils.isEmpty(m) ? "" : (": " + m)); + log(_loc.get("version-check-error", msg, url.toString()).getMessage()); + // return true and allow processing to continue + // additional processing will likely lead to a more informative error message + } + + return retv; + } + + /** + * Return true if the version is specified as version "1.0". + * Return false if it is specified as anything else + * + * By calling this method, the caller can avoid parsing + * persistence.xml documents that are some other version. + */ + public boolean isVersionOnePersistenceDoc(File file) { + + // default return value + boolean retv = true; + + // peek at the doc to determine the version + XMLVersionParser vp = new XMLVersionParser("persistence"); + try { + vp.parse(file); + String versionStr = vp.getVersion(); + + // if the versionStr is not equal to "1.0" return false + if (!XMLVersionParser.VERSION_1_0.equals(versionStr)) { + retv = false; + log(_loc.get("version-limitation", (versionStr == null ? "" : versionStr), + file.toString()).getMessage()); + } + } catch (Exception t) { + String msg = "Exception: " + t.getClass().getName() + ": "; + String m = t.getLocalizedMessage(); + msg += (StringUtils.isEmpty(m) ? "" : (": " + m)); + log(_loc.get("version-check-error", msg, file.toString()).getMessage()); + // return true and allow processing to continue + // additional processing will likely lead to a more informative error message + } + + return retv; + } + /** * Custom configuration provider. */ @@ -604,6 +685,6 @@ public class PersistenceProductDerivation if (_source != null) _info.setPersistenceXmlFileUrl(_source); - } - } + } + } } diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java index 47e596195..12d3b65a0 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProviderImpl.java @@ -209,5 +209,5 @@ public class PersistenceProviderImpl throws IllegalClassFormatException { return _trans.transform(cl, name, previousVersion, pd, bytes); } - } + } } diff --git a/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties b/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties index 53f95046f..27068164b 100644 --- a/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties +++ b/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties @@ -80,7 +80,8 @@ naming-exception: A NamingException was thrown while obtaining the \ factory at "{0}" from JNDI. bad-jar-name: The jar resource "{0}" cannot be loaded. missing-xml-config: The specified XML resource "{0}" for persistence unit \ - "{1}" can''t be found in your class path. + "{1}" can''t be found in your class path, or it was found but does not have a \ + "1.0" version. cantload-xml-config: The specified XML resource "{0}" for persistence unit \ "{1}" can''t be parsed. unknown-provider: Persistence provider "{2}" specified in persistence unit \ @@ -135,6 +136,10 @@ transformer-registration-error: An error occurred while registering a \ transformer-registration-error-ex: An error occurred while registering a \ ClassTransformer with {0}. The error is logged along with this warning. \ Load-time class transformation will not be available. +version-check-error: The exception "{0}" occurred while attempting to determine the \ + version of "{1}". +version-limitation: This version of OpenJPA cannot read a persistence.xml document \ + with a version different from "1.0". Found: version "{0}" in "{1}". EntityManagerFactory-name: EntityManagerFactory implementation EntityManagerFactory-desc: Allows extension of standard \