From a2287b6c6d43b44f656632503401b7968c42bd86 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Tue, 23 Jul 2013 22:02:38 -0400 Subject: [PATCH 01/34] HHH-8371 Consider explicit column name's "_" in alias creation --- .../hibernate/internal/util/StringHelper.java | 3 +- .../java/org/hibernate/mapping/Column.java | 2 +- .../org/hibernate/test/mapping/AliasTest.java | 55 ++++++++++++++- .../hibernate/test/mapping/ConfEntity.java | 54 ++++++++++++++ .../org/hibernate/test/mapping/ConfId.java | 67 ++++++++++++++++++ .../test/mapping/UserConfEntity.java | 48 +++++++++++++ .../hibernate/test/mapping/UserConfId.java | 70 +++++++++++++++++++ .../hibernate/test/mapping/UserEntity.java | 58 +++++++++++++++ 8 files changed, 353 insertions(+), 4 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/mapping/ConfEntity.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/mapping/ConfId.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/mapping/UserConfEntity.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/mapping/UserConfId.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/mapping/UserEntity.java diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java index f5711ef8d6..92ca7eb057 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java @@ -52,7 +52,8 @@ public final class StringHelper { public static int lastIndexOfLetter(String string) { for ( int i=0; i tables = configuration().getTableMappings(); Table table1 = null; @@ -60,9 +72,48 @@ public class AliasTest extends BaseCoreFunctionalTestCase { assertTrue( table1.getUniqueInteger() < table2.getUniqueInteger() ); } + + @Test + @TestForIssue( jiraKey = "HHH-8371" ) + public final void testUnderscoreInColumnName() throws Throwable { + final Session s = openSession(); + s.getTransaction().begin(); + + UserEntity user = new UserEntity(); + user.setName( "foo" ); + s.persist(user); + final ConfEntity conf = new ConfEntity(); + conf.setConfKey("counter"); + conf.setConfValue("3"); + final UserConfEntity uc = new UserConfEntity(); + uc.setUser(user); + uc.setConf(conf); + conf.getUserConf().add(uc); + s.persist(conf); + + s.getTransaction().commit(); + s.clear(); + + s.getTransaction().begin(); + user = (UserEntity) s.get(UserEntity.class, user.getId()); + + try { + s.flush(); + } + catch ( HibernateException e ) { + // original issue from HHH-8371 + fail( "The explicit column name's underscore(s) were not considered during alias creation." ); + } + + assertNotNull( user ); + assertEquals( user.getName(), "foo" ); + + s.getTransaction().commit(); + s.close(); + } @Override protected Class[] getAnnotatedClasses() { - return new Class[] { Table1.class, Table2.class }; + return new Class[] { Table1.class, Table2.class, ConfEntity.class, UserConfEntity.class, UserEntity.class }; } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/mapping/ConfEntity.java b/hibernate-core/src/test/java/org/hibernate/test/mapping/ConfEntity.java new file mode 100644 index 0000000000..3b4a136f7c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/mapping/ConfEntity.java @@ -0,0 +1,54 @@ +package org.hibernate.test.mapping; + +import static javax.persistence.CascadeType.ALL; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +@Entity +@Table(name = "CONF") +@IdClass(ConfId.class) +public class ConfEntity implements Serializable{ + + private static final long serialVersionUID = -5089484717715507169L; + + @Id + @Column(name = "confKey") + private String confKey; + + @Id + @Column(name = "confValue") + private String confValue; + + @OneToMany(mappedBy="conf", cascade = ALL, orphanRemoval = true, fetch = FetchType.LAZY) + private Set userConf = new HashSet(); + + public String getConfKey() { + return confKey; + } + + public void setConfKey(String confKey) { + this.confKey = confKey; + } + + public String getConfValue() { + return confValue; + } + + public void setConfValue(String confValue) { + this.confValue = confValue; + } + + public Set getUserConf() { + return userConf; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/mapping/ConfId.java b/hibernate-core/src/test/java/org/hibernate/test/mapping/ConfId.java new file mode 100644 index 0000000000..72a95cd0fd --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/mapping/ConfId.java @@ -0,0 +1,67 @@ +package org.hibernate.test.mapping; + +import java.io.Serializable; + +public class ConfId implements Serializable{ + + private static final long serialVersionUID = -6722022851594514199L; + + private String confKey; + + private String confValue; + + public ConfId(){ + } + + public ConfId(String confKey, String confValue) { + this.confKey = confKey; + this.confValue = confValue; + } + + public String getConfKey() { + return confKey; + } + + public void setConfKey(String confKey) { + this.confKey = confKey; + } + + public String getConfValue() { + return confValue; + } + + public void setConfValue(String confValue) { + this.confValue = confValue; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((confKey == null) ? 0 : confKey.hashCode()); + result = prime * result + ((confValue == null) ? 0 : confValue.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ConfId other = (ConfId) obj; + if (confKey == null) { + if (other.confKey != null) + return false; + } else if (!confKey.equals(other.confKey)) + return false; + else if (confValue == null) { + if (other.confValue != null) + return false; + } else if (!confValue.equals(other.confValue)) + return false; + return true; + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/mapping/UserConfEntity.java b/hibernate-core/src/test/java/org/hibernate/test/mapping/UserConfEntity.java new file mode 100644 index 0000000000..3e95a6bab1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/mapping/UserConfEntity.java @@ -0,0 +1,48 @@ +package org.hibernate.test.mapping; + +import java.io.Serializable; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "USER_CONFS") +@IdClass(UserConfId.class) +public class UserConfEntity implements Serializable{ + + private static final long serialVersionUID = 9153314908821604322L; + + @Id + @ManyToOne + @JoinColumn(name="user_id") + private UserEntity user; + + @Id + @ManyToOne + @JoinColumns({ + @JoinColumn(name="cnf_key", referencedColumnName="confKey"), + @JoinColumn(name="cnf_value", referencedColumnName="confValue")}) + private ConfEntity conf; + + public ConfEntity getConf() { + return conf; + } + + public void setConf(ConfEntity conf) { + this.conf = conf; + } + + + public UserEntity getUser() { + return user; + } + + public void setUser(UserEntity user) { + this.user = user; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/mapping/UserConfId.java b/hibernate-core/src/test/java/org/hibernate/test/mapping/UserConfId.java new file mode 100644 index 0000000000..2965a95f06 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/mapping/UserConfId.java @@ -0,0 +1,70 @@ +package org.hibernate.test.mapping; + +import java.io.Serializable; + + + +public class UserConfId implements Serializable{ + + private static final long serialVersionUID = -161134972658451944L; + + private Long user; + + private ConfId conf; + + public UserConfId(){ + } + + public UserConfId(Long user, ConfId conf) { + this.user = user; + this.conf = conf; + } + + public Long getUser() { + return user; + } + + public void setUser(Long user) { + this.user = user; + } + + + public ConfId getConf() { + return conf; + } + + public void setConf(ConfId conf) { + this.conf = conf; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((conf == null) ? 0 : conf.hashCode()); + result = prime * result + ((user == null) ? 0 : user.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + UserConfId other = (UserConfId) obj; + if (conf == null) { + if (other.conf != null) + return false; + } else if (!conf.equals(other.conf)) + return false; + if (user == null) { + if (other.user != null) + return false; + } else if (!user.equals(other.user)) + return false; + return true; + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/mapping/UserEntity.java b/hibernate-core/src/test/java/org/hibernate/test/mapping/UserEntity.java new file mode 100644 index 0000000000..135c500744 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/mapping/UserEntity.java @@ -0,0 +1,58 @@ +package org.hibernate.test.mapping; + +import static javax.persistence.CascadeType.ALL; +import static javax.persistence.FetchType.EAGER; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.OrderColumn; +import javax.persistence.Table; + +@Entity +@Table(name = "USER") +public class UserEntity implements Serializable{ + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue + @Column(name = "user_id") + private Long id; + + @OrderColumn(name = "cnf_order") + @OneToMany(mappedBy="user", fetch = EAGER, cascade = ALL, orphanRemoval = true) + private Set confs = new HashSet(); + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Set getConfs() { + return confs; + } + + public void setConfs(Set confs) { + this.confs = confs; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} From cce9b8147170f23b3ea4e23e7ccef84cdeeef6b1 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Tue, 23 Jul 2013 22:31:21 -0400 Subject: [PATCH 02/34] HHH-8371 corrected failing test --- .../org/hibernate/test/mapping/MappingReorderedAliasTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/mapping/MappingReorderedAliasTest.java b/hibernate-core/src/test/java/org/hibernate/test/mapping/MappingReorderedAliasTest.java index 6b3dbffb19..08fe660213 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/mapping/MappingReorderedAliasTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/mapping/MappingReorderedAliasTest.java @@ -27,6 +27,6 @@ public class MappingReorderedAliasTest extends AliasTest { @Override protected Class[] getAnnotatedClasses() { - return new Class[] { Table2.class, Table1.class }; + return new Class[] { Table2.class, Table1.class, ConfEntity.class, UserConfEntity.class, UserEntity.class }; } } From 3ffc6ce6e8c1d872f122355f5efc51b1bd711d6b Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Tue, 23 Jul 2013 22:49:00 -0400 Subject: [PATCH 03/34] HHH-8362 InformixUniqueDelegate --- .../hibernate/dialect/InformixDialect.java | 11 ++++ .../unique/InformixUniqueDelegate.java | 62 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/unique/InformixUniqueDelegate.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/InformixDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/InformixDialect.java index 6a665bdd4a..65d4b7287e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/InformixDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/InformixDialect.java @@ -28,6 +28,8 @@ import java.sql.Types; import org.hibernate.MappingException; import org.hibernate.dialect.function.VarArgsSQLFunction; +import org.hibernate.dialect.unique.InformixUniqueDelegate; +import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; import org.hibernate.internal.util.JdbcExceptionHelper; @@ -42,6 +44,8 @@ import org.hibernate.type.StandardBasicTypes; * @author Steve Molitor */ public class InformixDialect extends Dialect { + + private final UniqueDelegate uniqueDelegate; /** * Creates new InformixDialect instance. Sets up the JDBC / @@ -77,6 +81,8 @@ public class InformixDialect extends Dialect { registerColumnType( Types.VARCHAR, 32739, "lvarchar($l)" ); registerFunction( "concat", new VarArgsSQLFunction( StandardBasicTypes.STRING, "(", "||", ")" ) ); + + uniqueDelegate = new InformixUniqueDelegate( this ); } @Override @@ -286,4 +292,9 @@ public class InformixDialect extends Dialect { public String getCreateTemporaryTablePostfix() { return "with no log"; } + + @Override + public UniqueDelegate getUniqueDelegate() { + return uniqueDelegate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/InformixUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/InformixUniqueDelegate.java new file mode 100644 index 0000000000..8a00c411dd --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/InformixUniqueDelegate.java @@ -0,0 +1,62 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.dialect.unique; + +import org.hibernate.dialect.Dialect; +import org.hibernate.metamodel.relational.UniqueKey; + +/** + * Informix requires the constraint name to come last on the alter table. + * + * @author Brett Meyer + */ +public class InformixUniqueDelegate extends DefaultUniqueDelegate { + + public InformixUniqueDelegate( Dialect dialect ) { + super( dialect ); + } + + // legacy model ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Override + public String getAlterTableToAddUniqueKeyCommand( + org.hibernate.mapping.UniqueKey uniqueKey, + String defaultCatalog, + String defaultSchema) { + // Do this here, rather than allowing UniqueKey/Constraint to do it. + // We need full, simplified control over whether or not it happens. + final String tableName = uniqueKey.getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema ); + final String constraintName = dialect.quote( uniqueKey.getName() ); + return "alter table " + tableName + " add constraint " + uniqueConstraintSql( uniqueKey ) + " constraint " + constraintName; + } + + // new model ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + @Override + public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey) { + // Do this here, rather than allowing UniqueKey/Constraint to do it. + // We need full, simplified control over whether or not it happens. + final String tableName = uniqueKey.getTable().getQualifiedName( dialect ); + final String constraintName = dialect.quote( uniqueKey.getName() ); + + return "alter table " + tableName + " add constraint " + uniqueConstraintSql( uniqueKey ) + " constraint " + constraintName; + } +} From c620eaa952786d011be2a9038a8f983607299334 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 24 Jul 2013 11:43:20 -0400 Subject: [PATCH 04/34] HHH-8378 Cleanup during Bundle#stop. Added shutdown unit test. --- .../osgi/HibernateBundleActivator.java | 16 +++++-- .../org/hibernate/osgi/test/OsgiTestCase.java | 44 +++++++++++++++---- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/hibernate-osgi/src/main/java/org/hibernate/osgi/HibernateBundleActivator.java b/hibernate-osgi/src/main/java/org/hibernate/osgi/HibernateBundleActivator.java index fdf0d1ea89..022fd938dd 100644 --- a/hibernate-osgi/src/main/java/org/hibernate/osgi/HibernateBundleActivator.java +++ b/hibernate-osgi/src/main/java/org/hibernate/osgi/HibernateBundleActivator.java @@ -35,6 +35,7 @@ import org.hibernate.jpa.HibernatePersistenceProvider; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; /** * This BundleActivator provides three different uses of Hibernate in OSGi @@ -59,6 +60,10 @@ import org.osgi.framework.FrameworkUtil; */ @SuppressWarnings("UnusedDeclaration") public class HibernateBundleActivator implements BundleActivator { + + private ServiceRegistration persistenceProviderService; + private ServiceRegistration sessionFactoryService; + @Override @SuppressWarnings("unchecked") public void start(BundleContext context) throws Exception { @@ -75,12 +80,12 @@ public class HibernateBundleActivator implements BundleActivator { final Dictionary properties = new Hashtable(); // In order to support existing persistence.xml files, register using the legacy provider name. properties.put( "javax.persistence.provider", HibernatePersistenceProvider.class.getName() ); - context.registerService( + persistenceProviderService = context.registerService( PersistenceProvider.class.getName(), new OsgiPersistenceProviderService( osgiClassLoader, osgiJtaPlatform, context ), properties ); - context.registerService( + sessionFactoryService = context.registerService( SessionFactory.class.getName(), new OsgiSessionFactoryService( osgiClassLoader, osgiJtaPlatform, context ), new Hashtable() @@ -89,6 +94,11 @@ public class HibernateBundleActivator implements BundleActivator { @Override public void stop(BundleContext context) throws Exception { - // Nothing else to do? + persistenceProviderService.unregister(); + persistenceProviderService = null; + sessionFactoryService.unregister(); + sessionFactoryService = null; + + ClassLoaderHelper.overridenClassLoader = null; } } diff --git a/hibernate-osgi/src/test/java/org/hibernate/osgi/test/OsgiTestCase.java b/hibernate-osgi/src/test/java/org/hibernate/osgi/test/OsgiTestCase.java index 659f664d7c..a72946cb94 100644 --- a/hibernate-osgi/src/test/java/org/hibernate/osgi/test/OsgiTestCase.java +++ b/hibernate-osgi/src/test/java/org/hibernate/osgi/test/OsgiTestCase.java @@ -18,10 +18,13 @@ package org.hibernate.osgi.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import java.io.InputStream; +import org.hibernate.osgi.OsgiPersistenceProviderService; +import org.hibernate.osgi.OsgiSessionFactoryService; import org.hibernate.osgi.test.result.OsgiTestResults; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; @@ -94,6 +97,8 @@ public class OsgiTestCase { builder.addBundleSymbolicName( archive.getName() ); builder.addBundleManifestVersion( 2 ); builder.addImportPackages( OsgiTestResults.class ); + // needed primarily to test service cleanup in #testStop + builder.addImportPackages( OsgiSessionFactoryService.class ); return builder.openStream(); } } ); @@ -108,24 +113,47 @@ public class OsgiTestCase { */ @Test public void testClientBundle() throws Exception { - assertNotNull( "BundleContext injected", context ); - assertEquals( "System Bundle ID", 0, context.getBundle().getBundleId() ); - - testHibernateBundle( "org.hibernate.core" ); - testHibernateBundle( "org.hibernate.entitymanager" ); + commonTests(); final Bundle testClientBundle = findHibernateBundle( "testClientBundle" ); assertNotNull( "The test client bundle was not found!", testClientBundle ); testClientBundle.start(); assertEquals( "The test client bundle was not activated!", Bundle.ACTIVE, testClientBundle.getState() ); - final ServiceReference serviceReference = context.getServiceReference( OsgiTestResults.class.getName() ); + final ServiceReference serviceReference = context.getServiceReference( OsgiTestResults.class.getName() ); final OsgiTestResults testResults = (OsgiTestResults) context.getService( serviceReference ); if ( testResults.getFailures().size() > 0 ) { fail( testResults.getFailures().get( 0 ).getFailure() ); } } + + /** + * Test that stopping the hibernate-osgi bundle happens cleanly. + * + * TODO: This will be really simplistic at first, but should be expanded upon. + * + * @throws Exception + */ + @Test + public void testStop() throws Exception { + commonTests(); + + findHibernateBundle( "org.hibernate.osgi" ).stop(); + testHibernateBundle( "org.hibernate.osgi", Bundle.RESOLVED ); + + assertNull( context.getServiceReference( OsgiSessionFactoryService.class ) ); + assertNull( context.getServiceReference( OsgiPersistenceProviderService.class ) ); + } + + private void commonTests() { + assertNotNull( "BundleContext injected", context ); + assertEquals( "System Bundle ID", 0, context.getBundle().getBundleId() ); + + testHibernateBundle( "org.hibernate.core", Bundle.ACTIVE ); + testHibernateBundle( "org.hibernate.entitymanager", Bundle.ACTIVE ); + testHibernateBundle( "org.hibernate.osgi", Bundle.ACTIVE ); + } private Bundle findHibernateBundle(String symbolicName) { for ( Bundle bundle : context.getBundles() ) { @@ -136,10 +164,10 @@ public class OsgiTestCase { return null; } - private void testHibernateBundle(String symbolicName) { + private void testHibernateBundle(String symbolicName, int state) { final Bundle bundle = findHibernateBundle( symbolicName ); assertNotNull( "Bundle " + symbolicName + " was not found!", bundle ); - assertEquals( "Bundle " + symbolicName + " was not activated!", Bundle.ACTIVE, bundle.getState() ); + assertEquals( "Bundle " + symbolicName + " was not in the expected state!", state, bundle.getState() ); } } From fb4697d51a64c5be5955c63f2ec28ab555084942 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 26 Jul 2013 00:41:03 -0500 Subject: [PATCH 05/34] HHH-8386 - Commit transaction on connection used to export schema (JPA) --- .../internal/schemagen/JdbcConnectionContext.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/JdbcConnectionContext.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/JdbcConnectionContext.java index d3005ae3e3..359688edf9 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/JdbcConnectionContext.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/schemagen/JdbcConnectionContext.java @@ -27,6 +27,8 @@ import javax.persistence.PersistenceException; import java.sql.Connection; import java.sql.SQLException; +import org.jboss.logging.Logger; + import org.hibernate.engine.jdbc.internal.DDLFormatterImpl; import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; @@ -37,6 +39,8 @@ import org.hibernate.engine.jdbc.spi.SqlStatementLogger; * @author Steve Ebersole */ class JdbcConnectionContext { + private static final Logger log = Logger.getLogger( JdbcConnectionContext.class ); + private final JdbcConnectionAccess jdbcConnectionAccess; private final SqlStatementLogger sqlStatementLogger; @@ -61,6 +65,15 @@ class JdbcConnectionContext { public void release() { if ( jdbcConnection != null ) { + try { + if ( ! jdbcConnection.getAutoCommit() ) { + jdbcConnection.commit(); + } + } + catch (SQLException e) { + log.debug( "Unable to commit JDBC transaction used for JPA schema export; may or may not be a problem" ); + } + try { jdbcConnectionAccess.releaseConnection( jdbcConnection ); } From 8b7a92e170c4bc292b26989bbb516bcc50facde4 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 26 Jul 2013 13:00:01 -0500 Subject: [PATCH 06/34] HHH-8388 - pull in final jpa-api 2.1 --- libraries.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries.gradle b/libraries.gradle index e31b05afec..1919c0de3d 100644 --- a/libraries.gradle +++ b/libraries.gradle @@ -54,7 +54,7 @@ ext { javassist: 'org.javassist:javassist:3.18.0-GA', // javax - jpa: 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Draft-16', + jpa: 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final', jta: 'org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.0.0.Alpha1', validation: 'javax.validation:validation-api:1.1.0.Final', jacc: 'org.jboss.spec.javax.security.jacc:jboss-jacc-api_1.4_spec:1.0.2.Final', From cf9bd9d13fbf0e115dc0eb18136c01e26ad02181 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 26 Jul 2013 14:22:53 -0500 Subject: [PATCH 07/34] HHH-8388 - pull in final jpa-api 2.1 --- .../java/org/hibernate/jpa/internal/EntityManagerImpl.java | 2 +- .../org/hibernate/jpa/test/graphs/BasicEntityGraphTests.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerImpl.java index ecea98b71d..d675b07ffe 100755 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerImpl.java @@ -197,7 +197,7 @@ public class EntityManagerImpl extends AbstractEntityManagerImpl implements Sess @Override @SuppressWarnings("unchecked") - public EntityGraph getEntityGraph(String graphName) { + public EntityGraph getEntityGraph(String graphName) { checkOpen(); final EntityGraphImpl named = getEntityManagerFactory().findEntityGraphByName( graphName ); if ( named == null ) { diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/BasicEntityGraphTests.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/BasicEntityGraphTests.java index 0f9fd767c6..8a29031464 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/BasicEntityGraphTests.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/graphs/BasicEntityGraphTests.java @@ -80,6 +80,7 @@ public class BasicEntityGraphTests extends BaseEntityManagerFunctionalTestCase { } @Test + @SuppressWarnings("unchecked") public void testBasicGraphImmutability() { EntityManager em = getOrCreateEntityManager(); EntityGraph graphRoot = em.createEntityGraph( Entity1.class ); @@ -88,7 +89,7 @@ public class BasicEntityGraphTests extends BaseEntityManagerFunctionalTestCase { em.getEntityManagerFactory().addNamedEntityGraph( "immutable", graphRoot ); - graphRoot = em.getEntityGraph( "immutable" ); + graphRoot = (EntityGraph) em.getEntityGraph( "immutable" ); assertEquals( "immutable", graphRoot.getName() ); assertEquals( 2, graphRoot.getAttributeNodes().size() ); From c101258e037c8e57df4597087a6c7dc589afca2c Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 26 Jul 2013 18:12:52 -0500 Subject: [PATCH 08/34] HHH-8389 - Look at JPA TCK StoredProcedureQuery related failures --- .../jpa/test/procedure/JpaUsageTest.java | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java new file mode 100644 index 0000000000..ba9a7cbff5 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java @@ -0,0 +1,166 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.procedure; + +import javax.persistence.EntityManager; +import javax.persistence.StoredProcedureQuery; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; + +import org.hibernate.dialect.H2Dialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.junit.Test; + +import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.RequiresDialect; + +import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Tests various JPA usage scenarios for performing stored procedures. + * + * @author Steve Ebersole + */ +@RequiresDialect( H2Dialect.class ) +@FailureExpected( jiraKey = "HHH-8389", message = "Waiting clarification from EG" ) +public class JpaUsageTest extends BaseEntityManagerFunctionalTestCase { + + /** + * Some tests inspired by the awesomely well-done JPA TCK + */ + @Test + public void testJpaUsage1() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + + StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser", User.class ); + // this is what the TCK attempts to do, don't shoot the messenger... + query.getUpdateCount(); + // yep, twice + int updateCount = query.getUpdateCount(); + + boolean results = false; + do { + List list = query.getResultList(); + results = query.hasMoreResults(); + // and it only sets the updateCount once lol + } while ( results || updateCount != -1); + + em.getTransaction().commit(); + em.close(); + } + + @Test + public void testJpaUsage2() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + + StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser", User.class ); + boolean isResult = query.execute(); + assertTrue( isResult ); +// int updateCount = query.getUpdateCount(); + int updateCount = -1; + + boolean results = false; + do { + List list = query.getResultList(); + assertEquals( 1, list.size() ); + assertTyping( User.class, list.get( 0 ) ); + + results = query.hasMoreResults(); + // and it only sets the updateCount once lol + } while ( results || updateCount != -1); + + em.getTransaction().commit(); + em.close(); + } + + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { User.class }; + } + + // todo : look at ways to allow "Auxiliary DB Objects" to the db via EMF bootstrapping. + + public static final String CREATE_CMD = "CREATE ALIAS findOneUser AS $$\n" + + "import org.h2.tools.SimpleResultSet;\n" + + "import java.sql.*;\n" + + "@CODE\n" + + "ResultSet findOneUser() {\n" + + " SimpleResultSet rs = new SimpleResultSet();\n" + + " rs.addColumn(\"ID\", Types.INTEGER, 10, 0);\n" + + " rs.addColumn(\"NAME\", Types.VARCHAR, 255, 0);\n" + + " rs.addRow(1, \"Steve\");\n" + + " return rs;\n" + + "}\n" + + "$$"; + public static final String DROP_CMD = "DROP ALIAS findUser IF EXISTS"; + + @Override + protected void afterEntityManagerFactoryBuilt() { + execute( CREATE_CMD ); + } + + private void execute(String sql) { + final SessionFactoryImplementor sf = entityManagerFactory().unwrap( SessionFactoryImplementor.class ); + final Connection conn; + try { + conn = sf.getConnectionProvider().getConnection(); + + try { + Statement statement = conn.createStatement(); + statement.execute( sql ); + try { + statement.close(); + } + catch (SQLException ignore) { + } + } + finally { + try { + sf.getConnectionProvider().closeConnection( conn ); + } + catch (SQLException ignore) { + } + } + } + catch (SQLException e) { + throw new RuntimeException( "Unable to execute SQL : " + sql ); + } + } + + @Override + public void releaseResources() { + execute( DROP_CMD ); + + super.releaseResources(); + } +} From 033bf4a22e8ab361732c6d70baf62b9dc325c4c3 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 26 Jul 2013 18:26:33 -0500 Subject: [PATCH 09/34] HHH-8385 - Check whether EMF is closed (throwing ISE if so) when indicated methods are called --- .../internal/EntityManagerFactoryImpl.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java index 6d0d26fb1e..553df500cb 100755 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/EntityManagerFactoryImpl.java @@ -309,6 +309,8 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory { @Override public EntityManager createEntityManager(SynchronizationType synchronizationType, Map map) { + validateNotClosed(); + //TODO support discardOnClose, persistencecontexttype?, interceptor, return new EntityManagerImpl( this, @@ -322,42 +324,49 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory { } public CriteriaBuilder getCriteriaBuilder() { + validateNotClosed(); return criteriaBuilder; } public Metamodel getMetamodel() { + validateNotClosed(); return metamodel; } public void close() { + // The spec says so, that's why :( + validateNotClosed(); + sessionFactory.close(); EntityManagerFactoryRegistry.INSTANCE.removeEntityManagerFactory(entityManagerFactoryName, this); } public Map getProperties() { + validateNotClosed(); return properties; } public Cache getCache() { + validateNotClosed(); + // TODO : cache the cache reference? - if ( ! isOpen() ) { - throw new IllegalStateException("EntityManagerFactory is closed"); - } return new JPACache( sessionFactory ); } - public PersistenceUnitUtil getPersistenceUnitUtil() { + protected void validateNotClosed() { if ( ! isOpen() ) { - throw new IllegalStateException("EntityManagerFactory is closed"); + throw new IllegalStateException( "EntityManagerFactory is closed" ); } + } + + public PersistenceUnitUtil getPersistenceUnitUtil() { + validateNotClosed(); return util; } @Override public void addNamedQuery(String name, Query query) { - if ( ! isOpen() ) { - throw new IllegalStateException( "EntityManagerFactory is closed" ); - } + validateNotClosed(); if ( StoredProcedureQueryImpl.class.isInstance( query ) ) { final ProcedureCall procedureCall = ( (StoredProcedureQueryImpl) query ).getHibernateProcedureCall(); From 33b7e9f441b302525cd11f0fe7a653298590c0a8 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Tue, 30 Jul 2013 11:58:46 -0400 Subject: [PATCH 10/34] HHH-8388 Updated OSGi quickstarts and unit test for JPA 1.0.0.Final --- .../tutorials/osgi/managed-jpa/features.xml | 2 +- .../tutorials/osgi/managed-jpa/pom.xml | 2 +- .../tutorials/osgi/unmanaged-jpa/features.xml | 2 +- .../tutorials/osgi/unmanaged-jpa/pom.xml | 2 +- .../osgi/unmanaged-native/features.xml | 2 +- .../tutorials/osgi/unmanaged-native/pom.xml | 2 +- hibernate-osgi/hibernate-osgi.gradle | 3 +-- .../test/resources/felix-framework.properties | 4 +--- .../hibernate-jpa-2.1-api-1.0.0-SNAPSHOT.jar | Bin 113379 -> 0 bytes 9 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 hibernate-osgi/src/test/resources/hibernate-jpa-2.1-api-1.0.0-SNAPSHOT.jar diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml index 5c6f625415..9dec7c4c11 100755 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml @@ -17,7 +17,7 @@ mvn:org.apache.aries.transaction/org.apache.aries.transaction.manager/1.0.1 - mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0-SNAPSHOT + mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0.Final diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml index 8be58e1b26..1810286274 100755 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/pom.xml @@ -10,7 +10,7 @@ org.hibernate.javax.persistence hibernate-jpa-2.1-api - 1.0.0-SNAPSHOT + 1.0.0.Final org.osgi diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/features.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/features.xml index 9e31bed850..27d67036be 100644 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/features.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/features.xml @@ -8,7 +8,7 @@ mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/1.1.1 - mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0-SNAPSHOT + mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0.Final mvn:commons-collections/commons-collections/3.2.1 diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml index 32435ef2a9..d43b08e20c 100755 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/pom.xml @@ -10,7 +10,7 @@ org.hibernate.javax.persistence hibernate-jpa-2.1-api - 1.0.0-SNAPSHOT + 1.0.0.Final org.osgi diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/features.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/features.xml index e505b7c5a9..b8efe8e742 100644 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/features.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/features.xml @@ -8,7 +8,7 @@ mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/1.1.1 - mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0-SNAPSHOT + mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0.Final mvn:commons-collections/commons-collections/3.2.1 diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml index 4b8cde19c4..6fb3dd7f06 100755 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/pom.xml @@ -10,7 +10,7 @@ org.hibernate.javax.persistence hibernate-jpa-2.1-api - 1.0.0-SNAPSHOT + 1.0.0.Final org.osgi diff --git a/hibernate-osgi/hibernate-osgi.gradle b/hibernate-osgi/hibernate-osgi.gradle index fdf48c5ab4..3b50912446 100644 --- a/hibernate-osgi/hibernate-osgi.gradle +++ b/hibernate-osgi/hibernate-osgi.gradle @@ -36,8 +36,7 @@ dependencies { // Local copies of all jars needed fur the OSGi runtime. osgiRuntime( "org.jboss.arquillian.osgi:arquillian-osgi-bundle:1.0.3.Final" ) osgiRuntime( "org.ops4j.pax.url:pax-url-wrap:1.5.2" ) - // TODO: Temporarily using a pre-built jar in src/test/resources. It's needed for a recent manifest change. Once this is available in Maven, use it. - //osgiRuntime( "org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0-SNAPSHOT" ) + osgiRuntime( "org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final" ) osgiRuntime( "javax.enterprise:cdi-api:1.1-PFD" ) osgiRuntime( "org.jboss.spec.javax.interceptor:jboss-interceptors-api_1.2_spec:1.0.0.Alpha1" ) osgiRuntime( "org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.0.0.Alpha1" ) diff --git a/hibernate-osgi/src/test/resources/felix-framework.properties b/hibernate-osgi/src/test/resources/felix-framework.properties index a13b5f1ce9..9d092a261c 100644 --- a/hibernate-osgi/src/test/resources/felix-framework.properties +++ b/hibernate-osgi/src/test/resources/felix-framework.properties @@ -14,11 +14,9 @@ felix.log.level=4 felix.auto.install.1=\ file:target/osgi-lib/testClientBundle.jar -# TODO: Temporarily using a pre-built hibernate-jpa-2.1-api-1.0.0-SNAPSHOT.jar in src/test/resources. -# It's needed for a recent manifest change. Once this is available in Maven, use it. felix.auto.start.1=\ file:target/osgi-lib/arquillian-osgi-bundle-1.0.3.Final.jar \ - file:src/test/resources/hibernate-jpa-2.1-api-1.0.0-SNAPSHOT.jar \ + file:target/osgi-lib/hibernate-jpa-2.1-api-1.0.0.Final.jar \ file:target/osgi-lib/bnd/el-api-2.2.0.jar \ file:target/osgi-lib/bnd/cdi-api-1.1.0.jar \ file:target/osgi-lib/jboss-interceptors-api_1.2_spec-1.0.0.Alpha1.jar \ diff --git a/hibernate-osgi/src/test/resources/hibernate-jpa-2.1-api-1.0.0-SNAPSHOT.jar b/hibernate-osgi/src/test/resources/hibernate-jpa-2.1-api-1.0.0-SNAPSHOT.jar deleted file mode 100644 index 77b01a747d72ce9cb67ffd5fe97e5f4bbf98e21d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 113379 zcma%jWl&w)vNi7R?(QDk-Q6L$`^GhBa3?qf5AN>nu7L#C;KBXdIrrRm@5#%n`gYaa z#gDb9Vsy`z(W6n81BZYE0fB)5nO4~r1p$7*etiKyu)w1rp)SHGqbSJ?4x;>z2Y)7l z(nWxRfGmT9fMERhg9;*wGLjN%>P!lfF-Z!JeJluJ=P8fGWQv*M`F&}0n9iTA(By+j zF;96zmxQ%5T&;yK&Mj$(gL@ioHP_h< z0ZIbp`zu6xcb+216j2`yQCv40#SeQ%URCVzH$~^D)LD4Oy)cFz^y8untH-iZB}-;s z3Y(O~rtC+$)w4`TO8c7EzLB=HHt&mGdMpwg?lpJVMHB2ktdlTP?0#lw#>q!Fru2S| zlrZxxjz9C<+&Q@vue57Ou7cZREZQ%;5>EIYJzVaDZ{Q1@9N z{r${NV4T$okEWzFr>84_Lve-lV9Eu9XEd0ya_Zk z)tp~ zu582J3;pq<6o6U+^^M4K!&=bP6cA6iPOLs!zYvi8xI#vHOvG)?dzU=mvA%odY zTVVKFl8N=9o(#3j^;E|BNZx)D_l90A{CnXUsws<0`*Q-NS+T!}9Sjm8z=T#6&wk7x ztmy5hjr2}VSa}NP5{D@P zaZEqx!kR(;g7SXX-W5sqRRF9+Sv>Guzt_BO)dL61Sz`ZaEglm?;{FJ9;rVeJHc0A? zM$6g2YB+I#fjk7WRC~rLLp;Yg z>1~JLu%zG^HCBRowDl{y3%)fb6MOpW3(FxQ|1J4OtYalu<6>yRWwNOI1w*Q-xv-k> z=N{Xb07VSQNE&C2{W9R6G&PCH1oy=#$uzj0k%&exUwCCmu}MQ^JQl|7#2@URVWbyT zAO{|ZbOZ2^{}E{gV<$OtFR@>j{BNk^+ZFqTQ6$dssKYMc3iE}|juYQbuOuo@Vh?pXW|9gDf9<9eQ*BbYhs%l{H8d zKHqlXPnA2ewYGT8{v_c{KI0`b&Yzbz7wsyRE}+^tX0TY%BcqO2j&)rO$GPZgD{oVz zQA!J;`0mGOcL?#gujD7&+;<1y3h-=PRct0-2%#?fcXJL1VuM(p{Ojq+#>rx}+3%sf z6dPsfpgIq4Xo*5Wr*-KCpQC?@Vn$Q$Ix>uxSJNvN17s_DbWD?OAfdp(kh&SWTAd*E z8@OF&40Vv#PM!!(S$4YmNnLC7G6hbS--mz`y#9uz3XwqRFRwiZdM)7}u~c_+viqNa zX&~3nf+6sA{|B$ozUw2hpK`qAg=>gfj2=z|uo-JF+`E|bzTf$JOnaoI*b1qI$Z0cvs0ToemDxBWnckLUy_fH|6M-w*&dZ!Q`nldEuWQt_X7`QGO z#fy?*?UCmT^vma37xd#dm6vb6h)wsa(#&gzIySaKjY{8q@dAPj2P|0_ z{|K^#gR8ZxmxL!U!vQ2bP0gKLtsNcy&Qb~Evi-^^VQ;4MlBS({%o-vFR+(jx2G&LR zl_VIbbl-D_!~|5)WmGHU_R2Q^r03RiKjWlEAXDaaa zv|@(u>?&D#BOkuvC{t1UsJZ7wMTqo)-VjKJq~y?3IdOe_u;Mo0voIhRVK!O~4Q4_M zu2Hk$jWSvj&?B}OVwu9K6?Yh7iqoCNDKwmmOkt&Gi_)497oZpVrw#ojb`Gw9w)+6I zBk3P)sN!bs;w5eE;QDvSQC5%xXGQT-Tazqbf+GtL&xtfd@Vwa-QMmxT!lJv!Dme^W*fg+r z%>?6AFi*i&mO3|%WQ*v0%&M+PDtuB0q#5WDE2+U6Gne}{p!?UR5b|_gbVXCG&S`}N z3)xTV&xoPJ#j<-nMNXa0s5 z66;I7ED$X0 zo;Tv3#nDiLQ^W-rCqGci$sLOBR(oAQ-xoxyRk(Zz=|4N^@?v)s{L9_HZ4UG#1e$_F z1~z=!5j=`a6d{Rk@32AGRnL(1_5&cT!#diRD>$}aFIKzJ5iq0ponkAKpdUNd{65ldU?zeA)F_0 z*}zvAi{D}(5w#s8l(+G7ue-nDK%q|Ag>EqCU~=8djfS+M?7gaDvRSoX;rvpi9+;H4d#BmQ~Q%(1)Sd zR5CFD|2c|}1!rYI6M_PkwlKYu(4{umph?32l$G=<5{ESEj0RSvC;7+Vyy`~_ znXeh{a{so(e28)Zh4;!yM|!j4`iZY^S0{MOKu%~zr~$lNV8z8tAPt$ z0xSu0uPMTfrEXS*DtTSS3G!g%AQo|b;mOsG)4vaPWeWfqWiMm6Gf?W4ujOmj-yT?<7Dp}e$`{rih7#H^s|jLEey9Sd z8;f2>h;SmptgSr!mSxHIQL&JLs5b%|6y`r(1tnm_w+BXlYg2hgQ`_HDK;pE+yfDg8 zw|!O`&b+9#RUjB+fb2F1>fQ<+figX&1_o^&HB)T6Y%_(sIiK=69ECCk5o~U0f zyE!lV{f|F;kOjL&tvi$}7ug4@b~@7-bwbf(1Ao4o6N? zSrrY_p<3Bo=p3sLMaZh+Le;rBWt5Q;stbqax=(@01kx17Ha>>LD&m#bbPraZ=Ef3 zn^An0Fwo3S!A76dVxrw1ws+Cdv9FXo?RBNA@iA5hr0wCw01$B7q85?w;k4{(v$ysd__Mh~LIV0@Xr|$!&fP2M zKNC}D+{EKCu&{810Rf@>qi-qyr*)=a{ny1#NtyOglfW46PU`5ePSX0K9j}h^1v7Du z3lt)c5z|gArqG(QLBsVb1K~9Iy7PX$utZ?^z0?ahKM~g}c#UxZegu4+ZEDWW`z@i( zzA_IgA$cpiW3r@lK994w=}z|Z)NcQ0*d7UAoI%nKqCrai*e$sA@GZKkQsW>~HYjv8 zR{ddD=Jf$Y&Ts1D%?m93tm6q=ErUjiJXPc+xP1}3Y{9HMDG>czSv!%wZl)Ud9;b_J zGO@cI`38nfeo~eAqGDBipx`(a6~}M^#dgqvB%|ryz*GxdSmAR0^@)vXS4X znH@R@Wm%?*M{Mbf9+!+YwSUsp_9KaOsZ&sm@7k$SpgLz22eX!&{Vt;HtU6{QQc44j zxv}Od+h@uBM*PE`m60p?$Y=!D0_O{~a*85PN=jdIQOT|RmaLQmJxRN$#bG_=V4H3V znWJO4Vv!^kLg!=)jCw$3DuIOP+Ayz-qgF;kwNMG)jZk8SW-n^mcE4A9{;BhXbB97@ zUxGRJY7tmu^U)EHSi`2qOMc7V7wN|}{pfPQ0xNE__AS^Lkc4ro9=MDR_3tp+;&Ry? z@8kcuJJSb-#n=(WhcF60?3nkVoj?xjQ>`edo+mOjLq~O(k9IA}Ff>BRtu-&)^8-a` z(k*|3cnvO+TTn^c*;!208J}+A)^1K0Oq~HkBnggU$^0&P!nf}P0*d3`Yhz!Uu=bAK z}I@wtfr>t993#BUi(I9nhb?wG*qAGhe+vJe+yeMHtRp_8rXY`ZC71-F0z% zG(~u_O1xQiRrGxN+$WoZ$JOiv;9{2}{C?Ivd9>${-Bk@=I4d>HxRu}hPF~2yUY9UW zC-U_OQQT}7y5`&ptuKlrL-!s9oI}?Ql3>vJ%Q9PR@1yZ8CGHM|&ox5cvo@k}7%aKN zf)R^vRJje7@jdRoI0aE@;Jg}_u9y-Iy2ZC=qRu#D!G|X;&KN)X? zB^arB09=veAw=C)oFFOUTsS{T!TfThOM)MD7SJ znt&)lu@a|@6T4>j>?Mc1Mmc`GiiHyd9V72sy0D0xU zENbi(pEMxaC3?`IM(Bf3uLr!F$PoVGb2dTRb#776M4U}x8B@kiN-dToqLy(*FPw;q zz3Q`qLc1!#LYPqTB6adX$?i{f{r-cre(a1@j?=hzE#lJTd966%#!`#O!PBCH-&&D)3+fU~ zU?$oDMhvn)Mhg*BQ*!{|KP*|Os=ec!GKN1BV?#0$7gY=Pw|?ra;~!0uCA7si9woFB1MP7 z!b&`gjTCzUh`e=uyXq5G$rDkL3Yf54?`b?oQ3UzRmJC@d7N(-%cf>BU+H>|EzOi~a zZ*a68a0=yIu?2g(=n9qQaIGPXN!oVbwyhdz`vHg3U z{``k-Eo&UST;FLxvBv zD(jzTInPro@Fh7q8R6$>y>1A#m@}TISPi@C%3C|Ca6pexhvXWd0hA>rF=Qr!dDX7d zI=EL+PS{1?#S5wo#FOE}y3m#>?QFI9L+5RBaUt`(L2luXfT6U3Ym=16jn#w~ktj`z zXeuX@%tF&VOipl;rK)Yh5y75n!`f)(nZ%q(kFqL|DQ@BKx~nEs)LwW* z7LuOI=@F4FwScuhvm5ZE)CyNO$fKzZ2>qy2bR>ID+W2P*ay$u{d;>aDHn6xP{-aaL znEiu{nU)21Mnm1DdRn%NWhG?!o;2Rhz2(#iSa7ej$8_N(u7t9(8H|~BwblE*(1&6X z^%4~yA(4|F5+5d0<+0uq=YlZR5}Ujeryn576I3fU>g)abaH>72Zk9|a@lKM*L&A#R zLL$dS311}RrFri`Via}N*|;<3qTb!x>V9=~g1(yl7)C0fW5JPBR$b79xvOOHrfLgT zFL$DZK{>typ)DlIhRcHa3k@mX8%glQp(~RSj?j1N7d~hERKoMwWI#!_5v`Qa4yrDF z^T(|;H6`w<4kJ#$N8v9r6{E;%Bymy(>WIhVoC9IIVk((^5Y;4-;Cfb{_=dRLyM9aG zAK}-vuz|RB0}tsRu@iH2a5r~xHFx>DuZ&lc15#Oo8yuCj8n(x|!5Aw17@ZuOi)yoQ zK>@PSK{pMEreEv6hnT6IXkBeWUx-B{$@$QFYP%DizjO51Ke#)_@1aEUJ9${Sa~A$m z-beYu!3iJa;m=Ce@U%pw$dkQ-|Ee77oZ}ie4br` zbcEaeLq86&TGXZQ(r*+(@~h0}Pm(C5c711&%9-?P(uZHY9gIAOmL#H*A*&=E`$4x{ z1@P<8)#*sv-$?og4n0Xf6L;}TdLSpR(((xgC)evA1Pu}}KvOnodMWMD4#zsx-9{{4 z8zfF@>?X?qEc8A2^ z45Kj0)8%XHXKI6UTeHv5{&8ywN*Syb`5o}SAr=7-_XxbLR~;9Wqz889bL1+RWYXp* zQ4E%^4_(Bi91GMs>*-8o$xx%}%$t4Q+9Zt}Xp0-mqK*5QEpk(WF|raOnhmtXqjLfd zl#4$^e(C)E&E30aTddlDpow4q8wt_iy0)$tOI>yw4Cl?#Ya)mb%iLh{3h|IMcxq z{Am-xrTl4O=|mSspL)NMc#azpVR?T};k-p!M+Q0bW91X-L0dEEVyyQmf`kmx(5@eD z6X5!6$J0w?C-B<^J3NDaVFYG83SjR=@n=^sw|8=MF}C|p&in7l(9cyexTV!ptS?xl zd+3F_|C+f-XTQb`N^~k|0f;kWU!cMSSb6%;}!tQty9CX%L)g9p1GPDCP#ZJ zBo1fkXXx7p`ln#6EYjHqgIBw`bEoMBUZn3|5)TeJ`xOo6k_pC7Q=)4v+dFNxye&F; z4s{jBf7*#~n`-EnPfMI3OR5^y8~AjL$3MvR2a86P4LjOpFxagKEaNHhoBTYGAX7i) zt=~RvT@B+qTCP%SldB{jGTL@~P}8%LpDbw>Zw!OH9G}NK?KKT6D#ztCt1U2w^{}K0 zEDTF#;Y~!i90$V%GqBuL-Er+=p?>`Wm}oae(@GDB^&7^EDSLY)?ba#xF<ON8lpap07b|oB@{vz4P>A8VvBK+6ieIAvIK(@OX+z z;c9OPMU~~RfRHJaPa@(8BI;1O!ceuF*pbg_t0hfr5(`$)#o@S?MkU=)OO)nNtcmdj zOZcuknVj#31&+>tk33$lRE?cLH&X^iANoJK+kdrV8V=U38m`uM|BCE>@ttr$-Ydj1 zCgUEx7S)dN*%o1?7#l)_w6Hd66ptxHEm6&v3!b>=BN{VTBvjP*z^{%tD^EM#;4D(Z z;^X3LAqq9(UAz#eQX!XSDG&!eGT^v;#|C|QEJLV2;Q8(I!`m^U{lk7?WZj+(TudrOmvYks!QExm75V zs9)ne%1NduOCWk$zZ6k1_>pp4SABkf_-92hL4SG?2t=R*c=7&RW-1svc&R)7`u;nu zsg5i531bNK_vdeeZ_q?K8=IM;H&O+zY7SHDWsFeF#!-)kJ(j4^*3>y9Qi?&`lYXh}V)KiY zK+G=KSMRt^k1tzc4&wz8^G@G}Y6_nz<$YM{MrJavy%U~}c*hHnXa#ofNoBiL#m zyu=qE7Qf33M{TMhWaJx>L@~zp`Hj3yVacVt1G;bW|Bo9hyEp>J^REB4q;k6=u)HJq zNC6zRp@}M!ScQM?Rn?`7Bq6=zHUL9R0qABE@N|tFM2%FdT!8wj4`c)ta7Xmz=uB2v zcD_H3QvwkqkUo()k-LUmRh*XDy!iRUxHwf;=hTH0X}#rH@^k${lS%74Cc50&>{@*S z>RmZCC>LDHF70^a^5Yc-r$kMrG@j0kIeEtKyjl>1z-~QoUI_G2OBwwX0GH*qUQse2 zl2B`Y5l2Z|F{){od^A44`|3>Lgvz(Rm`|j`8O=OX{oX%kiUuCcGRF8O7dTpD6vXdX z)XemVKTKCuOu7dVDYw&O7AP8TDKw*QlL3y{ zkY7OplDUt2F?BBD&4@OFNB`nmdtgIA?VxMWg_VSZ&BDOEgSRF{nam3*i)L5$BIndV zzq6C=6smTsK=h3PA35aVE5MQ=G;tS+Z?cB`F z)!cxt`Ip>7{xA6mkPG{F9sPF~van?Jq7?^hLJnp0FMf>nV}&$*It4Z%97vRulb;k+ zSAkVD_`UMB9rA6RiBTf#)wu7&zXmjfij=XGiWIzHr_QKy3Ie7wn`{Qw_#4&)*y=Xr z9q0JOBPvIZmNumjQ+0GM&S7A8l%tz=(};0_>}9x;=&_lhGP87W%nN-)kKb>+;- z%k`H5rA0ig@zvM^PV51MMPOPtc9PHKbf$Gr$ZT{Kv$jy?xBL^t(BE!~y=!Gx3ODZO zlb=uZKi6Y=W=u#!=crfiyAv3I20{%Zl`RSb>YHoE4|psSZG9Y3Li`$>mA}J$DK>KJ)>r2>|$;vW^8I@uHb0)?_sc7fYQ7&hJfSolEd*m>(GY?vY;A7 z^AGY13Vj5WI;tOKj7PVLi=%mEB`w=x?ggI7*w9$<5Cfj_qVELoAfFq#TB#2@eWnHO zZf@4@l-`3@Y+A#hoqenguZS*;E@Cm!-Zz%$l}_U#zKGU@}Ub54-%(T;gUiuyOu@6`XI> z3M;lP%63Wl6!va>=%#VRVbC)#WlYc9K1n2}66?lb(kRXEt5*GH?bj1s`>TTM%maju7 zhxzO7*B$eByN&qVEwf24Kj!SYrj+qPb4_BiorIaXw=vN^sQHS~E@T=ypQ%>vyT-x! zr5Ti*85b&QAXK+EpM1THCp}`M|DgUOWHHZO6XW_gIPaa;5vq#$7kAu7u z&W@meGlBBuogUJ=MA`QlmOke*WgcHV&VItCWLUIkzpYt57G-Q`7t0Y82$8k0_I84V z6cADI6I?lE?INe-+h@-#1f8oL z6D>UEE51p9#(ZHwn^yeAuuvr!w^pe{GC_`^Ux}H-5{qgYh5tdSHJay)@QREY64E*n zTXB7s;@R3aOg(D!m7S&p1{UoTL6slpFZlLasV z@fh8jk0J$BcPw^ULD|pzpD1u432loaxA#A@)|C`o=n@u*>= zXkbHR&Omkxvpu;kxo+ZZtabo5#v=EnAo>o-Ze>|?D4cnpe;2s>)_M0AyOmRb6B^D4 z8F!)Gml#3~r*5ThX2w9XJMl9-D;i>1iPDaJV70nTp;OoPyg38m>rkGvT^2?xgQFF) z-#BMw-}w03YR^q2diTra2p#Ll&AU@?Ky}wJiM?A{%(7cH>76@K_(<(mbOmXD)2DQL zl?udl9fXLmQ{73GZd)(=g5#MO43egGcJ&63noS&s>d#L(r?;}{UN#i?;PXzrVU~bH z+O9)9zo@6zzG8>5Cfg^>uxrkuJ9fgcTcMQ&d-Z0Ys;xMFQ8bRT`0-+P1*YlUrSB@; zzJMuAvb@9K1WR3JyD9h6#~ywu_1ckUGZ_gYNT}0d4fVoH`9Zv;5))8oOxOY3ZLM_n z5CzLkv^YIoi{f(4yBRjW?KXD4k_=OXk3|8UUny;@o2A#v=f>dM={vZEd3$fR>CPkg z$?~PiAj{;Jz~`Q6OlziL4218+vHI;36M9nf?>WxocRWzw9877$zcRzT7v6=k5mX#dik9ni=mLw;Ljp#JffPhPW-FiZ(@> zONxgRiGu?qy6Qq$PZt8YCFO;{`CjG?_(^Kz)mBL&Y5Q{3Tb0tcG^M@4tk*MHbe6ZfVz?Ytz zP+s~7N0qh5>tSb#$R>FG9aZIsKMO(WB-G8~22(PQ2 z%WdpvVLv3P$j2y|5Yk2hB4JGOWZ=0j;~Y$}DG=Sn| z;jjf^@1tYq;g*cP(pxw)F6?CE-j<EqRuPq7JkpIt(Wd{?Dsj8L$P&#hTU*Kf6&M z1}i;|b&vroBj|SX!Il2k-!eDp!)7=-@VedrshK~|t0e8*09L;e&3|rbjH-bWo(Qlz zU}9jPr)g@?)hjO=%MYC!q7I)$QIHoV5=NUZG3OiP95=b`Z+I&+QO)GC_dScV@wOzb zyq2>_^0}MpJopB%-2W@pbO`f+;__fB)&3%(+u#trjIX&Z)Mc^62PFxtx87m$n}#oD z#0-gK8W;K7WZ^r?S$?}=m_CRdm3wmE%;tcq_Imn`Tg@`kE_F&3oOpb)6nCqOP+nau zJUE5Ow@D<%RIQ}OynL8TYS2vJk8|Dr%^u$%(>*!<%R`tkJ@L zw_-sS#CFq`PeuguW3jUbWOvY%*44VPdyed8Z*!Nz=P0W}wN_2y<3D)9h+76I_9Vy* zIZs*d_YhBV(67szWxp4%+t{MCP;E4aj7*4k(OlqJ4029e)8r4-_`;WjG33~KekiI< z7m*diqYYaq3`VRI^Ugparn&SrsI0N+5nOH5TMs5b#_AC#U)>?eQV1Fu z?N|GmiORSEk6OMXN~g7}p9h#c*a8S}wB>i|AD>m#_fd7sl|~U}(S6e3T(f=WVOy*L}-3tbGWAtB;) zL}@r^`4|gGuKOFmvdEV!7Gr!*mNeydiG z^2okEWMMq{-AqEFP)kC>56jvw6t}HqD*-qL$RRunb1;k#uH^HL!OY6>Dx(T zY^AOtTm2*0q+;b90iKFZlIpURqlNhJgDh)ye#U8ceKe?Cj{`F#-;sIAFv%tQYBXWq zFO1ZzBn56$EhgXidUyI^h3P{oPFt0NdDs;{-H3Y*`N`11dl9$1BT2|sy>m|p*J~|` zdN8G&WVLtKnWa=mvv%4DnEu%JmIjx-huA%EP?%=k?qzCI`dH;Pabd=P#j-1-O7J8e zitu#$8kIQqHtKNqv=E2&a{79Fwg^QRfe9t!T#8+^iY~=g zD7Kcpb_*pN#KrVu(XWKWh5$qTXyT-~HcA0^x zz}OjbnX$?w=6XrS9tt1BaXTz~qfa5KxyJsI%U!aA4`XAnQ^hw|68YqC>-|4HW^5bu zju7ZEtTOLre1-=+nQWQIxXuj5< zM;0&{EYw46o;4)S&v99EReRoT^>fFLmJ5;GPw!*4+G@ z`1(ZgnD{%eQ$14?`pQU!TtoA!N)QOVdTu2#JetY5eQm0*7kg##@LBQz)5XLb_2T^u zu1ERJYbE#xc+Wt2^15%Q9RzorjH4??*(garOodu9uOE(r^Mp(`7_}5e>%z&Gu3z)` z2XsSisoLg;pNzO~A!FeOGgFl_N~5hwy@UX84P1j%SCF9{N%eD&nOia)!%_`rGpgRS#cx+Ih_qCzeMiINaC zE3?4{4|Q$JN=8B;J2$%kb8TS*%okBKwa^%M>5xW3WkTo~hT*J<0TW3mR1EW8N{Mu@ zBVkNDE>unF?N=F{(oxDK_cYOH1pVHJB8kybhjbocJxDQGco$UzfGR-kO7>c~3>SA- zSUuEV#74#0!db?~(4|bUj!qH^)K%>(4lWLE8jkRXh|q+<^gzfFOUO%{l@Gq|P)IEJ zWPPJOkj1UP^`D>({to9rJc5CEko?inlpO)C@{Y#;cD3o=U*kil&44od2Hi?2Ocnm? z!yqlCAbQ4zriyZGr6bhF=qFV6Sf*z7j48_dF9Ep8ug<~Nc=Vh zA6^^9bz6pQN}T;$1?qG#l}`@@3JX|4k^d2_|5|nVm+!==7|4MEYbVpy3?1wDD5Mez z3v%$Cw`L8YMpSK(vtrTX_V~D#<{p}>-AiIZpNTOI$m^g>iNb(_?fopDvyRU04lwTC z;$TkbVA{v>)H(iummxdCn*9Ew__eR=*&tTX`8)K*$+Bi&4=;-{dtCjR-> z`q0K&RLlRJ{`(Hvu4cI`OI6nTSLh;iYfLkF z*S2@|-Cl!bjql&}_m)I1zkot^OlGH3%|E+=1`_1*yecIefY7jHSgMn7)3^WvMUW^N zes)f3q{GVHp@f)Md}b2~q0{`mv7C}3At}c|IFe2YtKN|(b@s1VRf_rTd^Ig zI;sdJf>LRL--`+90m(}0?Tl7!$N}aohL{h{&uJ=>!6CpsZ@LQ<{(|}^*WxKpNN*dr zl^J%f9_(D55`rKZF9&r%@?l}%3}Bp-1y8KMYJBW=(sNmCI&Yv>e+n0Q6sOe!FzRdc zVIdb)sHE)Eg(&j6c3d^o1hZKkWoSh$HQ5+({ zz0PSN^2W{*p`s&CW`V>Vkg8H2zfo>jq`bx5mCEi~6}a}@vYihzcraF8UVB4T^>Ena z(MF~6X&Uo^c;6z5_>qTY7GcRIa4tUzxet3`$P-;Kxbpq@wh&VLR411W9ZJB~978@! zy4I%^iK62dA~YTq#^q@TSqu#x=JyE`O0i|6F(w?5w69{VZbeQLUo=l&+x!g~)tCcP z(Q>k?SQLHcPdO zcL(x-?f3VHa$EBvF!asAgcFN}i9^%{d*EtH@5|^>_N|hOqbwQs^JmtWlZ4oh`27}r zws=URV1P#cH7)*g;rLfer)cbI?QZ_h#xCLSLHxo3kU;DV52qp1U`7Ngyz_!SHxAKA zE3I)SGiEBtaP|6gBKv~~C37G320?hQd~cp`T)%Md4iM}H(KntRj*V-IGZ3v;2g(>8 z2KO%f^JQ;P<$1>4&Q^v9t~L73{q|f(l^`waZAFiH80*p;8xxvd0zT|_=(VCC_4V;) zUzAVXj9kPtmZCE>@@!cN(z1-jg5PMtkfp?Y9Sxs<^qzL=Tau`!h2NEM3?|wt%t6!v zQzl5yJuCM2u^^8x-R1$0I} zo_6-R;LAg}s_{m-(&k^?)09EjyH@cg-K_*bF%Hwl{{2NW}4 zgic=@H{8tHU>Zm-N|fS*I~c&mklfQ)V+<^E81^TR7E&acyk)=#d=QfD5Mf~gV+!N@ z_>>L!$d~*6oET)P_P}UZWV*LXxPK%K!n=PyGhJU=OAfsiJ(CXI0LGO$My8^Hj+NXy zrL_qR#Y!QOAMr@mx~WOPG{qnX(>)Me$P#zHU~EBM?PwYz_ANTT8_^><#7ll?4ql1B z!U8N^(yVnuUpo70A<1THUR@k=oQL%rOwLeFj|%5;l6kVDJkqPkP=w}IqR4PS3w)I$ zk6X7>M=L;-M&$iVEz*ktAp}&5c&<{2eXaYN3{;D}zuH7Tj6qWcGv_}bx*;4D+ z96*(lR(3i5h?csGu>%0uDFSzuRLz~;tbs#<-<0bqz$KV|799UNx5}j-uDr857-ovv z5^+fe;z|+&G2iQ%7Nk?B8d6>umB9a+X5AOl!y!QcJlEs+iAjlb9nV3SW?YSphlTrl ztAYifo@D9LZ*a>_=TU>*vgIMf1mb1C?akNDhzZ78&%-3f50h6FrH>b-2#h6f@q2DF z`<_jQD4J)$V!{P{X_fU#8xfZB74ZVu2+PX3;)#&#ILmG}551GQG>EmQc)3 z2c`vsa7HrfM&nQ<cJ{VD;#5c1@^;i6VU;IKY@-M{0fENLVW4WR zZkr9cV(Z)~d8i7BB<&LJ_M?L`;uc>$CQu9SI>Nfe{lG48%<&_+2@y8Zlbq&}QJaDt z=GKj9{R`wjgXbXLHoz1JxiS#)KMU#pCNKVWQk6B}%p65PE|4xL$U{A!HK8X;!S`mA zMiSj2P?k)P(T#$#i1It*?E9-;oig{;i?G}&)GMUG$jCfcK6QMO?#DBdxrJm? zH0`&7GnXmiI)W3Yb2^@j!{>rz6LQc3)l>X$h$ZabN#F?ETm{@m{S&X9?I@K`#Jk*w z7XT^aNcEb@!be&>4@ot4xJWX9y#;k|R6U}pgGGM4r0UZWSXQ3zU%qNp!fP5BF8}bQ zI$d*D*fi>D5zA;KsBL&gE+X`-x*8C#2h2BA`lCq7Y(l>$<1LpVY9Gg;@&E6sdUy1UCjN@Gd4l2)X!o37d z{nN;=!BJ_Fkun-_9h4-c+rBj>=WNS*{)Bq(c}NFkS#F-OSX1VDTpmwHBGEdhL*^aM z{~a|H&EIX@m=W^azEgMHK03A#nx}) zI}2P#mX@1yxawr~+VtcP{vEL9&|Utra2;I{Yws#2Dsc|oxKDoXOR9#W98svv<74o- z+Q3~8E0t2$QHPmXjp9Ei6e8zY?-IMx;Z#g&f~$s9p_h>t8VAR68QZ!ipUfnGI6cY= zWT=!VbAdQ_{XlvmN_3k~*e6Kv{M&+!*7jxTuPUw+cu4*ntNs>w{e5$kdx7B{#Sed8 zvV5_O8nX6U`P$?&u!p3@(xqqhzHC7Jl@xzTD1j?x;LkM9stbl>MDF{nj_a9UAMSm9 zpjmoqKgNC(LzV)IDO#X}y=Gz>hHp)-_Lua^gOj7863VBL1C$nMNG^j_4mV7g;?Ye; z-XeQx96>1d&1t91)YC1<&&RNqrf3!wqj2l_Jn3e%G6%p4B|c;^;NSgt5LOc%@>gP-VQ8)Bj{zb9R~ezW$HZj99Wb{ML3H*19kllAHc6*x8Iz^kWh2RUU+ zZP&GdjpB4~e|Z^7znR}VA_5>3umha2t!@$BK_caaey2OhGf07X;=cb4#S08gWfLHR z89?{`vvx_%*-q6Q;AZElX6_1Xew?fwEdK^n^{>_7PUr0GE8F?fOtQ%B6+;G~pHILB z&ChCz+$N_Sk4Vq27WcpWYc=??sjpc3l5}(uI0fF%*?rl$K?IpHS`HyZ|3SrpgA@5Q z@_ET-Bl;mkFR?sf?@;>7EKe{R9b-dJTCa1tzz7YqX1EH2Nz9m7c_vyh*HFZQ>u2vg z_0O$Xfo%iSiX`CkN&K;DF`8YH)XKH7XiCtt@*~$^>Gwa*`?48?c2cuRBKSMd65C4o zrhn4!d_KEmDRfX&gM~um%fo-VCoy47=I&6 zW%j@$OF+D<-7t4bn{HGoS%Qj8n+%|N5r%Wj@HW}lXj>Lg6?iz6QXEI11MIbL;Rtxg z1b;ID5N9CZFwD@*yk|wQfr!Ll(S}HxLCXsjfnpGOpsqon-vB|=ncU~pE^&*?hbt-v zH-f>hfo*jCSOdA1`_D{K=>rOK0W^~U!vD3m{i|2`I|x;3>Z#2uqpSx@2fEN>T?R#x zq~&vADJOr*-(=ZNMz!1P#U(~MO-DhnrpAUOe$f&9wU8Q{nRJ~N!eL+Jceu}I>Cx?& zB#`<1dUb^j!qD#yW=L%m)$^sV0(qi=v%D|^D^P}3jW_RoshwUK6`!pa*v_H4rkjLm z6@8%Vyh1@6S+@|cvBt1-a&~Q*eQjI}UL#H*y{ZMamV(9UEc?493KsIywpmM;U|cE} zmd>$V@J!qTFRjE4KZJe{_5Oy8T~k>r(m*0d4Z>m^VE=2+}79Z6&m5id;e+zjeVNbgAhcDp!i}=4}`B;Gxu4tP32JD_z!&YF&l4F{NfPeVDdeb}}}M`rk+X@EN{w zDKk+TwH^n|xBEQso7!m7=wBXvU0Je{S6;djKDWL0EZw7t962AkEmu={oLmik#Oi_5i?RTDC73b5}<+ zM+7wL3}^SaMw!6Khwwnbpd5l9yaWFKob(yw54Us(PMIG>96lPQtrdQh;^+Ud#fo#_ z4yKysh*p2o%Q;}3hIY?8JP)?9d0*Vq% z+=dwUq{E3lxUs1a{vr3Q{HWYf+F>PN2i%7H9c&d&?QqjAen__8x;~_Ss}jpu1G5Xi zCo{DFS*{iEThb;Xilb)*UKLj0=;O~?@V}HTYQTZVf6H6c?H$oXQ2amfaT`{3=rQJN zuIhf)6_0vPqL46S6|{8C0DC#_Ty>qX!!kU3U(cXWF>Ces707EFL^NF%eV5yB{hIEz zpT0Kf_w;&1{{i}o#Ag%ENHSTxI=nHJfm$kH_C!;GTSvz|HZFwF?30G?sLmVve1dBq zCVJ4r&#|Gg?&4?{=Xa*tF~?Tv=WtIRCxqX*?-n+*vmRA!%wpAkdiuTTGGzTz`{a~i z=Itj#gUIa}S1R9$8dU~^GGTjEHVWRR@m!@azq3w`C!|xWMC${@{(^jpGVcFJ*jWZ- zwJ&d5>FzFR>6Q|tL%O@Wy9ERZ>F$#5?(XhRX#tV$5_uQ6-Dhu)=ly?J51*N}elaum zeNFD`e5_XECLgz5rq8>h{YkBzyhd%&zP`3#4+0yxYnw0GIvTnc&WdEE>69tpzXs8 z-+SOpSS`LNq0;POU)Fat=c9NACC@AYE3w_JLkx}Zr~fwR74*l8i#eIH<{1&39;i zkq64Ta*FEB+F?q<5VD^%nBr~Y{Fdt66hilq0Ef&%2ffJ&pUFg64e++D+dk_Tjx6o(R{8?FKU%iLW3&8Abf}{`{_y@Zl?P5qV5C|J>#NyB{g_ zx1`E2Vnhmr5vYcM8-O8C*E@DN&J}49<60 z&nl(#6Zg7}WRH%xW1cymTjHx&go(f*3;Q>w30kp>cMfFD1xcxOZ)_CH)gZIwD{$X# zz_+pS_eJ*w`;Q2`*&)uJvJ4y_!)c2lyF?o z^V*k{(`y>vK}cHAQdjv_t1ABf-8X$}MM$21hpIn*X6g_K)kh#yf4)@9*ctvM;rVBB zj@0)cZ>hW|vL~y6hREFrXe4z6I~I|$Fr}9mVrZ9u!dp*k66B*=`X$UZijB_BSsnB6 z&jvuZcV}xSude7tZwi=jhI?EO#3P(->&S_4!Y}}IHzydi+5ZJI`-Nq zQUqqguQ!bszoR>t1z~@r=65#KAk!RMYUn@t;PKc2sAmhCY#M-HFHV(WhoVU<&w3xh z?#!wV3yAOg&OPyv!}eZSyWKS5-Gw+-QR~jLq)6^&Y?=dH#$|9wL9 zyqh2}FT3^vM$aNEJe5;#U+)O_-_fc=Pi7zhA~ga$e@+pl?FwQo-xLPG z1VJJSZ+L(0A=lzIrHBOUesb`OR2nQq{lmfEt%4}YwePiQ61FxRl zVFEe7Bxh7K-QJT?t-t2Hn#JDQ4ApRZ4&(s$@4j5_V_$BHU7Yx-FGuQ{@x{qLaSuGq zXIP~s`(A{Y7$0btga;6lMO=YMixH6UwQj)o7P@5i5XQ#H9p3wTz#Twxg%!7A6 zjDqZD@u(`9-_SV5M}*o5oz*RqkL%x<@%T}j8Wy@dJ%vm%?tjzlZnQR*fdgTEv|{}k zBPMUCZ*66uYv=mp9{MHlksSj1QE1$CQurlhR91rBkSY%P?IqNh=wL4lIbkLhIkf;c z8zpn%#!u}!%8`REGDM@OU6j4uA@{)5*bb(b`M%#g^aMhMxXEZWZGw%bEr$!%Ug8fK z`BA&*cTUhnZ(bI>n^VQ!%hgpk?k%Dk=|jmy#MNO!TA zJiXO$yWj+1mL&u(kErY?Q3xi&AoH5ClB!+xh~E4O-tomPG^;64ZfK49M_!d_~u zfB7K@rSc4Max7G82gLtmO2Fl-yn5?$9&<8zheG`u+!NTR5fK?(S z9J{zLo0JiFCP8L8H+KjX#7Exwin<>#k+S?=6)U3yBo*y{hqJ#AG0Y7JWifDzA^Kx> z`_oM!ZS^aPvX31ov|=OM&sXs1j0A8RDcwacvwV`gu<$tA3$R~s<{l79{OUlR)L1Y^ zxg_k)DPF|6-QQgNc)kY40r?K065$ejJ9M8b>ZBT4Qd);rn}T~td6%l-#j-uw=Cy*( zmIRQ}p$gxAP^&Bo@yds0_YMKQ;RX44!@v3RWW-YbC4#;HC*f7|*j)>&ZK6Z4#4%|E zF3U{`t(sl>@Q<#%mMZZwDhB+T0~tAJ(lbA|r~_#4g{J%^=zJ`yG>Zxv$FXp#q%ccK zZ;6^ABkg>QUS0W#c{Q&6B{q5y-@S+Z{!5S8Lh}QuS=S&f(d3Q@dBd@`LJb?GlSS$i zR|bwEF^h5G6#Mh=FRXI9wOB{%0WUD2 zf9!M0Q+lIKx+ia_GkJBjvl#jF`j9o-4#Rt~=n}xf(dwDz7w9MMmDnV-Q_Ws;t(m0m zhC-kKrTT?cRwfMMeVwb{HfxeMYO1%u3w!J${+avnmr7mG&`1}^s{bTW=Y#8Qo1UGx zEh7*TI*~cv06N{^%1DKAj_BP}4xxsF4z>HEahEe(*U#R>BMTT)}&DvkBTz1(+iOsH%hIX6T?mD*eI z-BRvAeN?FTmkeNl6n@!#)6x=Mx|5+tASpB_r`5@c1gK5G+^NL<6nv3)-&xW+(NJVzN?GleP;8;Z)VhC}+E zk0r{LKkXyy&LrCYyGAw4=$Gn7Gza%2uA?DJuow_0Lz`a@*M-;W_g%t`VqPDpDOPka!0yDj{4<+%nm<$0aAMQA#x>UfCkOGeFS zgYnBuMAgW@_d~!aJDG8TJ%#Fjio#?76u>{YH1hg@(dTD;pLElJ{68E+O$-uOK8EA{ zm?|%yL>v~;P#ke1r?lJOZuc}^^a0h)6Ea>%dd}{D@w>Xa%oZ1y+lxjp{diq z2_sA^A4;2f)MYYD$~kkr)G+Q(opQ+=Cvv!+&3GG~J9SoU^A$DPAi!6xZW;Re1g&-* z{_a8*y(}7R1P{(5na9?5_PeX0VMngM7ERBGFeWOvqAJJn$SzQP<#H}Yd(pjq!jXtK zMTtaKO`zc{8VoSV8>B7wmzBd319J*wFau;8h|taY=GCG z^%6=i;)%j@D4y5=3Pb=Kpl9+F8bt&!kJeLFZ>lNS<5O8h;8UAUkZ#1Z@vaO}O+ruW zlV-x3cwy8)UmrrDp(84V%^=1^2Vf_N?5=W<|=> z7g{d`y{EJ+h+kzOq8EDSItWtO)S6hCn+n%zU|1+akOb9VDLO4run?g(oKZ5Gv~1Rg zFGGk=S#Vfh##$(ayWElLNjZXlnVB0DFRgMrqPHsAJT83*9I1<$3|sC<;F>)owihkr zRpce?vkqDdRDIev1b8(6yvZw5lvQOp4OtO%%-}_%l!pMC*38AJU_=<V=`DFfWUfU7J3`})rd_*Kjme+00lZ7KF(QoK2SD?#d35vWX$ zLy5eFo=@I8B?noVy&PrvH}y-NRHq#JW9mZUjZtfA;{EN9YcMZr_G!Yhj#darIFzW@ z{e;e*vIl2GbCz`P+?JI2FH5lvtaBVZ4q=t!%7;LgjGy@`%?tTWcGkU~L%>sgfjOX} z?-Riv#PE@?agPd(`zC+{!Vh|&=?(7|^cW)b*DcBqP~n)*CoS1zFyj0qeQ$D65R^$^ zxa;|Uv~DpK^HwMkdaSQi9SipLs*(`+Wbw3|n)0$We9Mp~9U!U$Eqsp?%532vbGvQg z1tyh1FX_WqXq0djib57oEgb7W#M$6Ct>}_9y#XRRRpW>5R8o`n%_v8hrRU)+5C?=- zU#W2dP~#`R4m3!X%|>Gt!ua;y3$TnfK?m{ z^lT&EWU2czSo*tVnFuW6A*WfG0)fN-q$kqjAP+4X^e?(2ppTi#Au z^4(d}9mH`+GFS`PoNr87wV@ntr@;GK1Kc?3T+ACmM*Ser^$UHDv0b^wpG9$E4H@YmN>M6r z3QOw~#I`^xp*KHmOe{e=6D+yYQjvtBCk1o4qq_CI)&t1^f5@zT4Q|=a$lq*?l8}j- zu>SdmKBdV|e9lq8`Qo=P`;v|K>g4}lF97s{a)y@HPQR|h<71E9Qpn?@WUHAQ{WA)x zppW>pE*GT5mEQJQO7v-!r&!pgOpDd^fH?KN*ZY|3Zyj1N5K+y?Z;t%FJh2#Qr9ki? zvB0oUZpTW8v4u8m5lgxEvH_Z)l68pp-Eu4XyQnw@G1HMnNUSq4k_ zFXEw4W6S1OksD)~avWN$oFmsmN~CLXF+L{?4H*QnV`XweDm4*Nj_YwXa7j@3;s zJ>Y-aYP-uD|g+!VS5^tPW{N_jgBCpgTY}s0sj7vQq6M%o@k1Q z&nvhA72$6XzM*a+iTqYzdHdjP_UJ`j1)e{H7^SR#UY&mH-iHG9D8Q5LH^ysPqa=$q zi;~4h$T~shA3C8Z5Q23nkUGt1D0ZvwLaR(l=l<-)po_5yO9ig6?ZbA{j>Ps;?SO2N zjt1}RxSh?{wSbP+II(Sgf43v|UY%u9=?@v$qGaSfdUTT_Z^BI{VSx5MbDG$4ya4t4 zIP5Od<4*0HLG2)xXjM7|itlev2`h<5t(7xWwwYu}{GGsr zV8_F>uSrD{!}p|4$E5}x`)QqwdzJX~*_?*YF{CIK+^yb13Sv6m(tDTz9MDXGWpqQ~ zh6!z-mc%{UAUeaf1f|CL+`%PYmeq`6@Mbw3iEN{TA3C9n+J4U?1+vB$WO6=~I#n1C zCmR_*{C3T!+LDQURIgqFdoAN1^RuV6@Kc4^05D7I8yYy;J&DPFRcQe52(aPy8E?t< zylmCZKhYs1XFd7$*vRjInd!Ds8w?O1tX;4QbpNR&vsDw~HRq~+oeEHW-sj$3{y2qs z)+fWDGv!4KT8o`c2B-$N^mZG&-hFmxut;_+@wNEGkZ=*ZqeHEr5?YZmhK`M+*xpVP zhhsZgwHqw}hLhtHvrlQE>LX|#!MAmqC+C)AzCK{@{w1TRz^~IQggGAaIYdQNZ$Qla ziyTiq$7%G#yX>&>Sle@T$L+VGk|!+&-gQKUKgfuDx4&uS<257EN!FplFWhGEo2>I^{nfzM!Bx`^dCcwUby7kzI~y03?sQ+xLYC8hR7F%x{m z@T^HHQ)K|xHDm>sGP?&z=%VIKYoW|c*DCdmy$u9x*Dc!!0_XXV6RJ8woo%~U2N*YH zqtyaSC@A(b&MNJ^=h0}XnI?tEuk9GP4->o>Z2Rzr!`bt1`M-~L5vQsf(8{_rJcrW- zSJI`u;=PU+P^Y3z`p%~1}i}#>L}hi;pV&B!!w(}EGS51PGkh=16w7GBjs?N7^jXD_LEt(TmTAk=%sS<`=J8wLCx|JGKi#`3W$)~nH zXs+#|uUDtNRb<>GbTcoopuBi@@wwg3Ft*eVqdu`U1j4tI$FbZ@qxoi|yZ+zk=X1J}w}JRKUvk^I^uXYj3LmSMX&1LO>J+&>#CH zUq8E2xKHhU2A@(r zQ(t4@ZNWanwF^sXe&HE4hXm5l$B@=Aed@lkCM=xxuHX(jW}jVV#W$^`ANRUC3GaTC z#REG;;zpSPp942?l6hw`ykWh^EoE!U7o4W#FR={|)zLi^$yeD9aa{H>Y;WgG=?FqH4rA%QWxFs5NlcsbAz zl_`t-R*7Pl6(e8qLwo2h=auG{cIEpBo(|A$Di>xao^E z?{?C_ebqA>wQ1vU9v$A9yMy#H$Ge_Co4BlyHW>vRi{mZ(2uCl?N-;V@0MJvIWhbA? zuBRD3{b%@$ixi-iS-05V(X}e-sfdFdmf6Y4y``MG7rCBRQy?#u$NDm*I0;;M!8Y9! zhOfAiF`j#LZx6IfzLrjFRE3b<6BhFEL)L3hDDd6??(JJJF(Ajw5Eur=YddsX_n0YdG^2Gp*-9 z9L=Eq*G2k&@9Q;+%Tm+4Xm3D8!I6=L+TVW{zv`82uC8Yj55N@~FCjMzz+Q@$W2zqc zPT~7ZblB?DI zuz2J0#6#|ym}0E5(HAC&$cXbau&$RHAu`rDsfq8;Hc&DB-I~^F&6bTrJE~v;W4#d$ z^>tV*Umec2G9)aooB0U3^BVdpeIYyyR}~e>PE2w~kkbh6aCrTSsr^Dl=9^vY;SL8b z`&+h6FG1XTH&E5m!vEeL91SQ#l?zi_AG5GmCky zRp46GHl^)*;G^oz&)I0{+Dkw%Y3Vxbyz6LJyqhqN&|O{-Aic!b8fv!tO;R%`Cl7`M zd_7&j^Jh8qPu}9M9q>u3>#>v@TI)F-?nBT`ta7f`UX=H!OH$WE>z;DL9*jH4B~1q8 z(2$RE=oE#D_@IxeuEWh&Mow?utgbxQ*c|Um3-@>d2@>1D!U4@fPxlU$BfhL7WKzO7 ztT;2h1kF&jIbkA{_kd_XUI^bZLHof<6CKBUlcG*adiSldn?b&%$Pq+cvr``KB|F<(Fv-R}Z1?0WPmHc! zwHm`jIKk1T)u%qZdq-&R-a!k7qzF5ZJo9^HHWO&5nCsY^+s@6g^v-KJq;< zh@zE^7`=L}RNz(AVf>>&LN3;b2_{GSbPWGh#0kT@_ZWjy5Dl1Y)ZBgDEiYF<;`sj*f8x`>DbhK2Tf8b^&Sy`<&dpJE6mNvSP z(;3yIZi2icBp}cDti(0fQ@Pvng$k0j)V{t)H>|7K$J_*pt7VZa2Igw)j6Xo}Hi+i7 zAMCyWR4CZ)oAgA+jJexsQxe;UYrX+AE1BwDXqt>5LkaoqFT}%Zx&D#Pp!1|;=6hP^ zz6EbPnG0=StxJ_MfsGp3s$|9< zB7vQ!#gP!SWBlj+iIM~J!uUPdLivagBB4mbT8O>95p8;9H`TWFBH_5nUAq4Kh07(CAMQA3n>~R z_Ed~JQCg}3yoYarJ~9}lY+_LEK;ElSX=d}nsR>%HU6OkAtT=>$GXn!Kj~tJd@nr#H z-bRH|AqD3HQ_F_h%AihWr0PsHtz&zAy(O)=uAzr>B$^_HxjLoPDSL#2oM@QkACCEjaGH%bv0c+?=KulM; zk2Xp7RlT$&r*2)@S4!Ft2tHSiZ>9c2nCP??3`0aK{~d&MUz{A%I#9d+092c_2EMC_>e>+xW~%Pj?-KlgNh+EMuseO7(%E(y9r!_c5NsrO>-rfdR+g~|L=ANp=pqE4BQ{Sm3q^* z!t>0b0#om$;OlO>3=a8RgU%l}J?U&p%;?yLA`_vqqngNS(ypoo?nRO#-LvM}7u9DL z4L0$wq}fHqI6fT*{{)qOrY^GwK&5S5Gtxw2ii~>nKa7X=Djq?lO7a*KqS|+i>joUj z<;}TD{7ygkZW|_4$#fF9c(R?ZHr&*ln}y^Dn8$!5?In2z(9*>V<2du(%)z9se7Xjq z-`K&Bf<5_q#)xJOxB*{}1jDl@Ur)=UujjWMx0Gn8|FH)d1#G~7E+WVS7J$d`T)#s# zWe3DSfnMY&YpHFpZ78zPL@=lpH*_FQ0PFx3l62}iwmK&_=M>4eyeFmYiKGTG&N0J1 z!HlBj#Y%H4ciXdhp@oi0pC})6?~q7M6bcK=IqNSi4mB+Jb*2J_obN#Nk(YZ6+&OnE zaxX)n0jCcGdSg@t;PkO^H_bud1Zn7R%Plp?U>c+?{1U&4S^BaI zL6v~)-2<|rB|}+Zfq1W5cwP6G5NBJZE!(i7l?hY79>^NA)V*&ywmvlG4f?y=JTXu_S@bqwi11E)+R8S(H_cOYm45XU$CR= zvL}MI8L{003&|ycV`TV_i**AZqPGv5ubbliU0UaZC3%CSF3gk$TYd8SUMJb4uPR!! zJ{`w)MZ}t~5KYT(+Z-~@sq^CynhxLv{yC+6DpLGh4kG{A?*5v_k&0gaAt*ha7zLgY zj1H(x1PZc{2Z0p^+APK)$J3}!S_OXp2_&->`$Emfz2DdDdgiqHlny-8;O+1t0X>Dl zV-BEt!wZpUw^ceO@;L^;7sXxre$!+URgSfLHP@-AeBB9Jfc?yebgsJ`G}N&!_>GTP z!(`%gTcW_DDNz9-$#L>f39}(P5qU6@53@*aRE`vxQh5>imSCXozWvbE{aWmJ;l*Id z3$<%mDqGU4``Koe2|LiS4s)Ub9VaSmC+ z$1%_zUdT*kRUpSdH^o#wGed#QuYN_~Uz;^+pVhnO@TxBG3w1Mc)j49$y4ZS1pb5*1 zz1aiq0*v>&;~%sk3L5r(mPI}qxmKR$xT1=M^i~K{r%2F<(Gdzya0(&6fCFJAd;`w; z42)Cds&LoF+0UQi;|YSWZ9og%v&PJVaC^iQl0 z0cAMPfl|g42UXQn(Ln8-RNfLC;epN;!n0_~+6i2&P8R3mv{F(*AvZ@$t}8^gikchO zSR)kPHDLYazoL>Akc4d&T>Bb5Ff$pVag%BTiXGn1G_!l!oM#Hi;78>5T{~vddoq{s z%Z6%en+IRUMY(FoKS0_6=HF4~iXWOhU(dPHwyxwnsW2$$7ZLpRVO*16iYH>2=fgsM zxh_7Y4{GKm01peoLx)08;%Mjv{J+IjB^7lYUf)6Ofd0MV9P8i$YQ~4u-^xJemj`Sb zK+xX+e=pNq1> ztkVSfg+L&ag|gbnI2cZ5D?4L)vNGBrpu=9$P^g!W5th`4!v)pc9(aY<(>9#$he7+D z(qW;|#lpa`5zEY7caoT5Jp&!u@*ir~kC-{6zD-GpM6pKJ0vAh;fHh?CnNn+EQ^a2q zfU6mYhz)jf_I~@I!q2^lRj@HX- z-&T>&1KrZXrgg(CnsF9OksnlDXr8VU!72D%CqdHczjLi#GtQw$N)r;0YyJ7Sf9gWX z83IrpL#Kc0rvDljaHt0Ok68V`+I(}M$H*ll4`z%ojMk|HW7Ee6gwr`%s_}9Xl--}U zR5n6{tHyp%Y&Q5&3{Bf4r%XMK3$Pmt|D@dt>JnmyM>Xqcp(Edw>3_!wmmv2=KVfb_ zH3C_kF+1BceVxe1&R_P%LB;|#qtJl94qxk!OmAW_ zICEwj-2RP7?_`z409R7@><9x%;=7;s2|)u z@f61{Gtp-AsTc3l4SjjG8#4{dv4G&J&>h)}Lcy$hB|4N9-u_1S5M)rK600;a#J?4; z$Jo{br7g7Kn6(0|<`_E?HXnt-8QR7tn-QUl$u8qlFQ1%TOvX5*iC;;qy`c01=$wuC zY}k_SFn)Krn4QMJb4Oz76Y6OtHThBG*8*HD4zE~HBCQQ5Nz^jqIQcs{0fySLCKC;@ zh`btEBGC`qxF#0Vf;rFM#5|zT&dQHj`~dJ}(f+Yn^5k6kKht=tP*ZWDb0|p;P6VC6 zE=Ae;{CtWaszQGodFTe+SURzNy}u?Q(YZV$fMKkv{Y+IO3rs>uwyeZKvC0v~!E+-FuBk@Qnj(ylU)p@Z-hf2NfmG&LzLL{> z>J}bjjRL_UY86T9hK1_)>8G^RvNOR)_Ags<71@5DgS1J9>ivk3@dFM8qCb9DfQ($x z#sHYV`qz2=PmYXPi3lY?fSvGqhTBiy?@h*Cl%mk!)huHM8>LH9TXMTHEFdGFst07`H#2cg+t;&qGzmJ)+9;!G zW?88x_Li*BsR??c>;B_f*vfCGhKw*dv?>t?IE_0}Wn;KPj-W6{IJXH^h%36@_mkSQ zYQidR)Obv+pJvoIQ<~<9*=7AajPl6bFQ6; zd`AdyWS)A$bO_%tfu3;NzxVef0kx6;Gg|-Zq5prQb+JLI=^qs}61TknH+AO^(EDvv&(6$-n`HnzXWvOJ zjjuD&G%z2ui)X~8O|bxRct1Ji?O$Uc_hFQ>*N76?m=T%JqMqc;0uCtEsQYB|m?q$= z5|jqQZuH~3h^5AMMDipDEG1FKAc1z|qqK9M`=3GmnT!5| zGVpWZ8iu|`=wv_=G`=DIp0NSKGL968E5jQM7Vu%TqPV}qR>lHJBlH8P4KOY_=L7Af zsW5ZW+}#OSc{_t3Pq){2h+-yf#+y`pDDUAaiFf=({h@(=T*&@XH~MdoA6gh(u0iHKB?w^}4KBVM05Gfl59S znKh8rwlEtJ$`@x47KbYgqTNCXj>;ch-Q@iO<-iEt1CHB^f3HE$%G7Im;3WLt#xy_} zZwhQ!0AJxB++{hR zhG>G@hJl0Q^ocM9!O2}*=MO|oNQW|SYOa@X;Hc}*Ik*{RSkBJ|uB!>`Y#n#gFjLeN zmhu$MY9WK$WNw&HN`VyD~u*jdz3b-WVb6#w=OiYhs?M!N*_*w;7%aAu}mdw>yWwl zd(M|LjpX@5+~8c;>f%=Y`e4_MN^e@qx#qEDJW)_=WWm(j!2*}qT5=rbYl?*TQXg{U5;mL3|LrM`5IkK z?>9{AnEsG8t7RmJf4I>1EQ&)6st!V4)ZZoSdHoD(lX;rGcha39&HI^^4mcd!z!1_Y zYRU%>uN{~bz?b%!cn^~?^i*DS>{R?XU`Ylq(y(tV=8;%8)AH{42% zOelH}uas5Q7T<^OJPz3A&CZ`OIj|{IF!AlNqsiCvZn5P=qFdr+=#Y@4WKg6;*$buF zbssQKo8=b^!&rp!XHF!Mtu(z-A8=GqqfC7Zba2$?b8*LtuOV2$t$t^El-MLD<^k)C z89-Y7xiQRVZ*Q${`t;5H%ANi?E`JVaXd%(@Xj`Qp7tDI>&67l;$Rf!V1?uYdx{(PC zhM49Fz8~>>u_l6jq~HrKsWLn~(y;SpbinC;J#0wI59`?I98EJ`CW$r-=~XUWUIfxdooaD*HT|$cdz0K^*g#EO_@Pbc8imd z;M);7O132I!ot`6kfG!_lpmnPJSrlfb;#Qo@2h0EWet+ec8|o8Inu)l!sL48fo;)P zF`emyNQ^>isH1RBe>>)}B0W!lsGdLe;EX%?`ni*g+0o|6e%u8CE`rZV(x&E&IBBtl zZQ(h_WUwY_D0${&7IKU~jIZ@@<>x!~GPx|qk6l(dCNKLSQuoFz&vymB$bW=~K}3_+E?AQH0s)Ktb9=N=5}J163w2@(xHw(E z6@efn$x3K!-&ZPZbN&six_f0NxK6)cwwa?AnIAPQ4?u6@&*Izvs`r&BNJ{(gBBhqh zsI{qotDB*&3Cw>qP*t~*K!2kHuM^Ok?)8W7!HomFr|QhC=H1x}q_Hmf*u6 zyhE5`n9{a4`6Ofxacc6(=jIvxwF_gr_!}*Rz}A)^S{oQxW1>^N6MdgYXZbVyjjQs zVtvQ6gGsxkhZ;9nhbC#pzqb86UyR?!*dE4GNY<=dF;!LBZx1Inoo(*KA3Y<;K=bt- zL8|N@5FPCQh}46@b_E*XM{(mtQVS|-qYcHo^&=yM*hky^1&i7{xV;g9dzJUv>`cp% zuW*vXbTQJ@NxEtv;-{8#ptzQ2|F|v~Te3AuhI;6lML;S}~?7s!)>VVlE?FR&mx{P>{OF@gS zBvf;KU|H+5ifEArPLPMmC;tO)IBX+Z=USjnEdVGV23;U3Sk$B5><@FSy6?QMyT9L= zqxA}RIm*~rPyHz1{fguzsZ8mNy1pxk2kl5dPhBKamNba^r%>pUYATjKmYBd=5FIB$ zfuMdchkLT=fo=x?f6$%(0vdNg?Vf3g-I%DQkxc3z@0`PR+b_R(G93C{O*X#e}_dRN+9^C(c?26h;t zKfYqXi}>g9PPa<#ZG4zutgN@*$6LA>(%ZKnUi&q+XWBggR?+mqPX}p)) z(td={lyj9TTk}MBDW{~)>+K7qX+KB#-s6-j?@lXvW)z zfH(W+hWS6FgOBseellN zUnT&QLEshZ)j*1ntz{Gy6|TOkIlt@M!}Y?&Rdo-|vUcw%$;KZMp! znANz+BL#mX0I;g04&tySu~;qt09F>c0^`H-Hw4GEKD99PLD5qRPn70lO=u7jactZ=sg ztCA)0t@qHP3!;)jOrrr!#@ba6NXv4)Yjbutv`_Tu@uSDvmVn}CtAqqG0oPo9~6c;JS2cb7q7V^PtIC(feOILtUy z`HOT)873woZ{fVx2?GsqH7vI5m5EFz1-$pe3DaUla`sZvlcvBG2#QV+BH;hS}08fh}cb2zjPl10?cn_JpD#<7b~8kHN8%+zjoKt8?~j%W+Cp{Yus zk(N&rKfuK=n}r{UItj=DCXLh;C{0kq`m8Ts)W3Hm8b3u3K2EGmxgtiDz2>gxwcK@N zPL=Hze^FAcZ8e4A?gzs2K0+_>D1@mh$e05Jq1g7>_p+?p=mZ@fb5?AOazD!-$stbc zn(uVvuDr^FTYeJ~-qJWBVFO{^1)e{9L8Yx+!wyUM9hSOxX}5f&@@BK z&a=cDjB}8$T+;ca&>ZQ|0&$y9)ZJ-uy)$|mRQP$LzpM2lDB?$u3k`B{l={|YoGT-y z_@j;wOj#jh`10edDnnsL{3Z287C?O5DS@mpcW<}fQnITD} z)3cm4;Nws!Xm)(W7c3ENp@dNjF*Ly`@mF2G+t~mfy*_?Ab556H{OiYwuC=rSheqA- zGgS^(L5m?piHip>xGBLNY6!Qioxc1fW9$cQSUu~Ki?1C9!WHQ3w2JE1-Iz%E#|5V^ z0&&y@Qj1`4gwD%5Zax%A_D=Waqa@AS=`Q)7SdLp7d%?`8`%P*tyh@WKMe$e;N) zKP@r;Qr7uBVGEEvMm8v@Dg#e+&IQ*tNVT|;0i)~0GrRR^<{14)-{(2f4Pb-udSO^h zM1}Q=pwZm5OiR(R?NKVk4yY88d^8~)y;CAlQSx7@&*^hSm&TFXyknIkXE&T4%?nX# z%P>~!W%SvqWz2|G^4m@!Fvu+N!h%A}*>a<=n1&WXgRM>~SQN(Vu(}m3k2_5AA(#rB|FZ&f7CMRgL%IHGXjjW!tkl`YbP+ zq8($omo)C@ugk=_RKGFSoFHz|O%Hxzp4eS+zEx(!1Qx{pb%PhLX@Tor=s?{IA)ZB= zIM3tiL>28^`$oZnW5t#aer(a%IvQ<|mmp)vTDGYqn=cH6gK`B>{ z=4I?KSmRRqfCFCxX1&?)iYKo&FX2X$j}7tuk6Dbx&+oY2t{k)w5$|WA4n9ZdLS2XI z`|T68UozczEa(RSY094uT!1QIVfci%{in=BPE&pg7*wjBF3StvgBTMoXN;sZ98%WW zKkiUm8wX8E?_m>X6S%Zs!LYz^Jm-E4d7XuKL|j`NjZF{*j0#I>x=L$l^L)65Uc=XV zXW}w{GLDwO#zf{_({g_(swJi^9(~=%s!Z-mU+$(OVU(koXgbkAQ-kj1g(*($7zZ^S ze2tqUr(+sJX`ism^vbV&bmhk}`D*NYzMtgDhpB{3yLe#_`dtQQzmXiKI7jP3M}VtW z#BDhGHAtmOwq&=S9Tjt|Dn&sLn2N`{Svz8C_HJ0`$E>EWpLl_IZLum}O3eJwXutrq zi0(u76ijDXNxBEaS7D8DTcvHrnpmgSD*PlpV;fTGS1pbl;Imx-&~h@63N0&IdCimU z;T;7B!gb^@s@*6{(WZB9Oh{-cy@uw67Lvz)U463C%de-4`AUA>p$+)vPDkl@hH5>d zMD%#>ft)Q~JL!X&1^x?}Z)bn(1_*#G#bQco%%gMpzWTB4lGfsyrblyJFq+q}0a4Ee z`}q?%M#VWI76=^8W(UhLQh2U6dhrU#(2L`QI%z0#m%&&r{bK(emkx6u=RG=h!O&xr zvd`9_$?tE}=YN*~$8%&Ti2rFsL{~iAFP^C*$IJ6{wFQI-aVb{i z4Cuk6q{bFe&8@hX|9c6^J~D5)VxcR6A^2UcOeh6_jyW3RFSQn; zp;n6yh36Sv(~8n)dc#Ut8j&W#|0(pL9h*+_ZDIY1L?1(06THzYb>ZaIZ)SjJ{j^_C z>Hx97WQTJT9JFR-t=Ga4PU+t9H4neC&^f5ISkZ6Nvj`=4B>ikb^bI z<(pu9bnd?q!Z+7v~u(^$!U-je3sLI`#7p^vx zDdP<42f%n9!;`no;73tcP({?2R#^_H_ZALQf9i%z?90-PARen8jX@-PKBI-$-aEnV zFDMDT{L03gR34NG57L0ISvU(S#7D1@%-<@D$Q#EbhCmk86bv(@$B5J`844Zz_yvr8 z5vxd{5P}L3wy0nmfq^kmdY#ILx`I7@#&{K9gf>gDxL+OwO}9|NVYl^$!v#thIscobT$N5jl(29_Ao2D4d{diwQoq&mx6}SH?Uvb;OH$KXZDA`I({{&0`j*x{) z+c&xVmL?u+XK=|yzF#Kk=yn)Nd6uF<^b^-#qr|veQ|)UoVQHPWZQS>-Dv7iQjh>Sp z7N;F?Mogf^AXY4}1U$)Yg1ZCzMc^q56S?3s@a^Aa4?N%WsQ*OR2;_{IyeZNeaYsZ_ zAZh(xrfJ^^~LJ%lN`~g#*F=4Y4pv>~pFBQlc}kd&QZt=V6;2lN&Kw9Pla*{&%g!{z+dT0lc)GD8F% zEc!USz;Mu$oT2mb%M#^*lxqv=f?nKwc%(f0)z#D8>wRSeG2A5%Dkl|%L1}qt*G%8; zpr4fq&rJO$nx=t;;b;8EB#{||R@S**s1mHy9n{A_0o%m;zLFy$AGow@qn$$hx23(qby;GF;!bgTuMUbu`!oGDx%-B{y* zh40BAL!1uObdspFujo zmRJ6AQIzxAlQ@b3B^Da{JyxX+HkgQ&%LI_d%Fe_h4L$zg7}7Up91{O5P26-}$6I18 zo;X=HrQ!XARrIa#+XK`wY5s*7KW(v$fFx_53T<08fVr(<@>wlKmdG1XmSQ!S(>#4< zuO)>9U?a#x6ySTnbiOCQw&1rY3QYpoN_&(_N`Io0jJI8#`m|)cdb3%mX0<7}U`LW) z0)4K_8D@6?0l{olx=R)82%Omd<}=%iqZcs_966T^2?p#!|BP6lZS+t-O z>W*VOrmUX#HMB%X6PouxNEDkJNGlg6IOwOo37$cfI8F=AUkSyk~q zfF>JNFcSH^?I89&*+d|b#DJyW^O0wJal+b#FBhYJDWk)KWo9ywY4Y&s8;#FH&q}A} z65Cv=JL8_Oe1+?NL)#pwvvwAX2ofrW5Juv6^|O~YzcCp10UtBIm_me)-;(xfAB+>v z!gzs>yvidEr$dHjR3&!S+1L7LtqHxg*s>+u4cG!4@wd`)t)+LH=&wcBB5N=+oHTq6 z^jSGA?2YI6hPgI9&8rtkY3k2anlItRNJA7At}GsL+bSJLOtn_`vO!UYI!lk9S;@ z*ccq3(M`UcKQ24JHbX-J#0Px1tfd=2Z|#7@a@_{2Bo>Il4b?so!{8 zzZW;ZnHbez^T|nxr8*IVN|ZK$>ST(5@&YyRiTX>Ys6#XtVdENrAdy2p;Cu<79k| z&Xou;kUFNvVfp}@tSCh{ubZ`D%r}H|1>X2fCuv~Um}0WLgzMe7sBR{u4V_eXb;9Bc z@Smk}v3TpKagsvKpKY%K3{91Y&-)>x4+AiO%txE@jklB8Z9Vo{pTmaDfs;>D0NGIL zLM@eWxwhB?T)j1#nXH|kH4ZxZgjkdpN-I?zZQe7dOy4KspJ$P=-(Ct@OraT0f|Kq` zYYwPw$AVSEre{pVt|_iS_Bh+2+Juw>;D*44OqC)TvBbKNiOk_wSiC_D>ao@VxOKGc={^>Z5GllFGm{p3DR{II=H;-X;1;=xNjRNa~(rfR3 zA19C~O1aB`m&*n)>L>c+%k}1t{Vi5kK~o-47ugFH)rn8xTwM@^8JHS!x*abTJ`4jS zh#0HDC@r2vy}Ng#d$5J3<*0o4wDpLy) znupsHl+Ldfn4p~9_9H7%xM<@+&pRu6q*(g>Je@Sdl?3`88?C^v8y_*cq0tQPFh~r- z=~^iXJLt#>2W({d#l9p-6QhEHFr*$vJ`hxwDVn>Lm17W7#}vh7&?;w`1gu(8lXTqB zWzxt5FgD1E&=$t?bC7qq^Uo#N-5Z!ur%Ue24;NYReQ_twC;Oh$zvrT+S+Fisauyey zG-^mLim5GWfRl&(zVq|mt=XFFFiaVV4i)mtR|k60HBbj52Z3Rh`nftYK26%#&zl@g zg+N9E8YFT`5fQ<16^DJoR37biXts5&#Y&DOCdHwF&G}UeTD)u8uo6Ma zMa?e~g@f3k?YiHth2!H&@lm6;@~c%jr{z)^Fx4+EInoPr)5Y>-sZGT>jwYuuGXQ$t z@M?9o-7dsa$n(syeSU~r&Bk!kY3Qz_E_g<6qrl_~M#e6QFUfeVKpZc}X{VY+8uZ_C zGh`YKi&q{Bl#1o&?aSQ75@yw?Zz{A81)1yN^I3JiXfgJ8;hDqWVKYn`Z&rqQA$^vB zPC_@>zo~AEih%&{vwaUl5*&e8-M{F6{yu?FWeo|*Cb~4e--(>ZzcQHz{KIX%qd?jd z#4>`QlFoguPE6?$8;pEo&sMEQwFCharRO_SyCG+Y#}|wpM1oRhjSY6$fh%Vu=1=!h zv6aSWj6HgZ23-Cg@4!i=qGMU4G+3mT*bgf0rv^jRH+#B86Guc@Nx4q9TD8U=MtZy& zo&p-{L=&6ISSAaQEhTFyh1`+S#bnL8a*=mPn&Wd)HQGS7kCb8Yj$gG7LCTO^LY!K2 zCbeSEV2mzMA&>FHoL$0!Ev68ub|;j{hipzs|GhcZ@R{h})DW{!|JO+RO#ty%aQELu zXIlawGl3Vmf7~uiL;Zd42d#6UA~c3v+Q<_JgPzEAa=(RrI_{=gap*kLRSRsF+eztd zKim$u9hYWXWeWi;(;x@#uF=8L#BR^eUKKZ^r~(zR7O0g$9t=$ zy%~;B^%1-0q^fY4u}KNeqvD73-=$u|9rvq+O9qSP=g!vV8h*g^%u>O2hBSjj^d#!U zLb13!Y9lq!9-+uPWi9>SxfT_1ZGs!mk1{i_aISMmn80vDm>rX^@d!lguX!Dp16?bc zi97R!>z+_7aDG9MHjY>;EOWJ6CUez2H!V@^Z*wI)bOOEcHOAVso!V9C^5b@kjr67GwxY_k#U?`f&brT&_uX%f+4R!( zF=s%1_X2qD{wx}n1N0a5boI@D|IA0PzJ=2Sj4x%Hlqr>zSt`mD%c;|6v4(C&kU|@{slQ_>u`e-Ev-aW38f+iE&-bZ zi78x~>`dk@t#+q{=W)i3<1Hxv=(u{s6z@!$%FL0!Dkl~*bQWS=Eojk&WXEQSr*nFHvh;0KMz+>hpN5AUc^K!WD(3D2~4)VA^_Q)Mc zg8a#+Tezs{G2;twSU=*c|M>#g5_VlBXR2rEdGq)r1>+BR--%AlxucJ|z^UK=eE^dz z-{HMYwzUC&C;lI+6B%7QT}wj;L%Uyh9K*3mF#y6EZ2k3HQ&GMzqz`QtD~-cqC@UYH zAY=5ZI6FDy1;b4B1FW>kLViH+Js#6i zae*D-Hc>CZa8P5xC4uWDxvgw2Ey%#@UdW_yXKd%m0;=dZBOysCj;?r<@JID!A9y`` zKPiFv>zO$s1hYcn54@SlcSIEvoJRiU$qfc-0_vw{C4Q;;KYr)%%HNun!U9f-h5xf< zCT!@SZ}MM_UVz-0_0(VRU8sD&2=eAkDWT8Fa`cO&0}R!4`uz1hQixabMbdNmarw1j zFUpa`VA6;XpZUFLSyr!BXDXy22^`=cYccjL06e8POagq%v1Svv9ofjM+; zi1rl3;yHShA*JWi$ee~zsn@KQfpxnfjz*qzQcXNIVDE1&td1``xVax(hDx9>gr9}B zQ2gXfW>YJ$NV*@EF`{~scLF^+DrIMkXNTOMozw7_9XP$`Ib=!-4ZxM^&^RIS$32nUt3!LS$b52@DHqArS4xOy2NRMtk-+sD1SIBR-vdI^~D7K-UXaG^RLR zx>AXaDF9v&hEnBYD!T!bK{WnUYRvxEC9AbutA@}A|CR1B$Od4!TCS!qT=SUF#jne+ zxO0bA0XPZ56|zg3hydWODge0Kiw*$p^25FXce(IrBRN1G!xvEUq><@YNC3cH(Bko} zu|_W}0C2YrqUj3sf)W7SZF%1)u7nk@3pp&CF~J6P2%K zr}!Z-)r7?0Od)JL)=|ssLd}9dfr`jvcF08fzsnQB98}fr;7{#>F5F)`B#ZQrSQZH) ze(2#jw99lNcCdx_f2SDyEg+CvmRbg=egnR!(@>Mg@eh}| zW@U-h&CItWM+-tFBC^BE#Rr+$f;k_!vDD26KdIq9yniO^p^}m4K!lr%98cq7w0C;Z zd;xS}GqZO)o58VgTG3?ile4}Al%4o7y3%-3liz$|QAGAeSQnZSv($S(`;kCDUor5| zg~Hwv^vjn)s)vveWV%mxb;iginCYZ(kavxBt1U{c=io_36g46U4<`!53?EOoDs}AB z&7=;UBU=I)QpJQ4P`KkL9dPM16GI!9-mz8w!=2GJ@mwIeZ$G>!eh&*1i3kf8yu3{8u5DH4(@@j< zpG-7*ySqqn^^+8K^)7t3>I)nW#a}%!(qk;i1;0v-(QWps(U!@Fx0AbE_uXObdEQWP z0!zF*XCEX@GlaEke z-#QG{#eHP1VdAk5ugmZ&CF_i$O3QEXDHePYnm7D>9N={z_~W7bALY$|hr;7;rALrA zmSyg>bO|e8)!&{tb?90py{k>rSZKpxBl#G~s*2rC0IRGg1l%67v68WS>~^&J@%s4! zaviLYJCIwLTM7gWFcdCenhgXAWRNBsCi_rx6u(5~Q}eA}YVWF;G9A6Srru`gpi2n| zttJpMyn2}Tnb}t*Ufiw$s4aqlS-S0TH!nM_G_1i~uaPJ5qI)x=%#_SH4bUzZj3h2! zNPhC;L=i4j2YcS%p@-d*TW_WR;l~^wUyd$LZn8#2X9i9agOV?`UVRyPLr4Mygmdi-xE5;=1cc(u#Nu~?!P}*wYb}aJb)8f1kkDZ^Cki`)R)oh~r3zAPSaa;Biwx=bJ~XlUDODj8#)qawo6e zQls37p)PV)B#juHchlJIo!dQMuTVOWEI#AuY6^j=Vvu97qsT9;?`Hd&#>aeh=`HXD zxpf5%Lr<{pIopWz7J5 z39FPa-vwGwgpuQSJC|y0cCwH2`(j#_31AE{RL~$GT-KVKI-D)JC5O^tKjg!h% zBUR6wmkZntYAu_iLFKoYyKBPnhL!{EQ|3MA?WDpMMH^%5wE8Pk1I=xR@GG{_HDmW) z)`_k^CFtL`K-+tps*bWio_wz|X%4nZ=LlSL4k1t4fHW-BhwD@)TnL>$J6$PtOiU?p zt2lES0S+rk!=PQ*hQ>6ylgndTiYtT`X9a}A>x*}5u3)8@_AnDQiV}>`G1J82&s4d? zGD{m)6B05l85{MJ`Yh*um8*vtr+hi;e=?#zMKKX5G-=CkV^V$>B@OKv^LWRJ8?KB8 zXmtB*pb8Cd-^+sQ;KDZwpK}R8Hw*>>WYpKDBXCCz2=EP)7$QwyAv6UxzjUCl!>XPb z0~AGMKd)E-GV1qy5AWC;s%!$zd%f2g5*nRW(3pp1DhZJ7G2gs8R~T}XJ;)>3wuB6-yys*MGwt z|H$xU#s+NMS-|t>_V8Z?=s%=`|HeoKyoK%p!Y!8z3hKRT)d2GnaPqebRGtr%FPCUG ztWc7puk4_}fVKDK7qW$|i76BmZn!ns^>X***X2QATxh;GAnWnwH+wGGgOz`Q?y?~ zfBudWd9#Z*K}4n6bME<+>hZrQ>3z_Bq~`yiq}!4ypE%v+Gpv=&av=(BP203C0mXaQ zdD~y%(9{rUU*jPos)uEsiMs@M#BZGaZ-kTOnzdM42F_CeENP4nrxG~^Odk=;QyfRM zDh2hxU!#&4IaZ;dH}B;cG6elEi#NGO*k~Q_sdg&8D6_R$(`b~^+`0_6K3;=EL-;3* zd{oE{SY4}&h&(n%1dUmC2c^H6?B#RR!`|Hg?XAQKM7F>K9E`MpgYnM>GI>`keG@xt zD^s_>Ol1EpS|k1`T8F;Eg4s8Y9ynN)(}B`2f&;4>!EYe~g{+E9Narif<+sx0Q;mHe z`rph5_`(h5Cf3_k_c5;9>zBLxoljp#$$Ihh2_q#rWulZSQG%c(lQfX+ZN+wldYzET zH*hIjda@2rgs*o>jPzd}PuRPao)_4df1pWIRaoD8hNUXW@@(x!@7X>2Z7kF9uzji; zf*$PdV^UZ&;VQwMa{xOT9ifVr@5WH^3~WtE{Yb35+JGuhY-L|UzwbzO*0Im#kPG54 z>I`j2twYHHc82P>R^!@`mfFNuaGDZ0&A_RC9yw@ebww5Z7?!mAZDHlc8KZjsYIVF0 z;ske*$ylPHW#t?khh8*AM3Az85sW9jW9}|XIa&6chCKM_w1cq>tv;&RK~j;=rYH%m zBw9(9H(J-8#0y20Q_1#2i9TacUMO-*E$Fl_Mpwa>ot%xpuq5>KF}8YvG=(!sX5k_F zv)8@$>j4}<9Vhr$JQXAEKAgkO%T4_B6p9|qz@Rsv^L{2d$MNT8PmoR~e6#OWG(n(H zbu?aL!2^|5}RLL7`Im{#tsX_REq z<2bo~-cdp8rLM*@s9&A8j+QBv#!;*f;}1;;^#fI)o_^bvJD*_Dc~|h%R`7+v$w$N=BI6=lro8ksy>z*c-;q z#{>U<5}PTJMv_p4t^QHp!DMXD+`BqSFnLnE$3$pLnq;3GU$hW8ee*p!NFM$r@D-kT zM;}FH6O_gC1>U12g?=S4`;xfAPEp$ioI0^o!*6Uz5~-I!k~xdJ}o`*`R2z18?yWr{HO<^pv1QP$7WQH@a;wSzt04BoRO2 z{WQmUddD-gF#H~|1~Ta2R6$u%^7iS;%M-YB!9KzkYSM8g_n+~u=Z>5#4M6ukxKWsxeV{|LYN5qdI&|M%tiy2!MeYlvfH96l z)-B@6UU&O_KEvb#yz{RW5EV#G9ipr((U{XgP>R~qQ)I|&>FNt=DcRI$Fw60sQ3Otn zB4?jc`V0u1S@2298b01u$?q{Ur$~j_vun;0XW$p9$gA?XUf2DM!6-X@3|qGN1e92U zHjadnaO?Cy_|xULE&Q2(W^%g?Z%as_IRKuQtzuUO?>fBQQ^SB zU<>w-?B|dJf)9>ds&@dBFE|oY&hpYTVW3Y*!VHCjXCjwzK8kL#a4M8= z&;GQ*p3pWyT4_vY*^*)EK5axX44eoOj>WJwkasZ1?o%*WdZH?Bw`5ng7}sjFY&w2L zaE3m)dPv- zE18Qpsgxqq>8cq{m6=wdupijVFGLx3WCtQsbb@oOv-Hy}XnBklByF`dV$SLfeOYDx zLvkjEKX7B3B(*np5hYtx`Avi8=oxEL87WxxNNt?ZCgI6y)RNq?{XTOYB+U$Q`H2eNlWn2HH!;ypG}{xJ3s6|e)=LxX^UWwRVnw%T5Vb60FUOvbZ0lnl9!={W0aBYP z4^i`2tCIH}%PiH)nyU?M8~W#|MAR?HkOqaC44`U{@m3_xiea$BR913bhnBih;Mr!? zC>ZM#B6Ue(rySMY+}x7zp9d9WM1={`9b@bko&sj=$~=s3T`~ z;JeB;p63kcsb*RBdqAZ9m|Mt>&l~yngJ?oUV)D8){-@$q&vX?$NqzKb)qO1zrAR~4 z=+Bfep$l@Q9F<<X3ovu{jrupBWCSfILN%A*Om| z91!YbMdGYnnF9{S1y`2vljN0NE_nF{{ZoS5O`2_+Ht%85Fv=ER^1KANw?hwaj7SJA zNxb*GZ&ncRvoW(0$EZZsDA9cai?KZMn&NUUoN@;~FA zFx5HFaV2NWw^7AsSoLDg$zyl8)0vuZrY#sBQ(>I7L9`~d7iaI*S6M8EH2(6)Z^T3P!27DCm}Jjp@j~FYlje#7Y(HR zO2Zx^^nmxm8)EA&Ry!2R<+g@Awe#O5Z8iI|BC!fzg;g10^sPx%E?igkLaEyLPcUyF z$>0Qor#kVf&!Ptq!ohU!DDfDiF#98u~jZV9=1n= zYn&#lK&KpLOi*tcLg`)5ihK1t#MY$F3$t45Qdz%={z82MK7)qDtgSvM5Ew=*DpO@Z z_(6|T8@7#8pbc|wl2;0r^Ya!5?9ZYD_QTAb>_A$lxZK`CNFEYgn{p?R*Fu?!q2F`8 zr+3el%>m!hO2BvY&sULuIo$uZ+5srH-`t9&^dU#fJ-43-hz@%}D>8b%fp|oh`nsf) zTBgx`!g4b911Ji4I=&*hGh@bo!3jJlqA6}M)6&Y!cz>)n3P94LfR_NN5&|P8EiQ4J z4n_iyw2}>0goY{ZKmnfm{FG%-k^!dxa|v(xrlvNw-I-FaS00dI9t`1G)hI)%W-@q# zq-?34cpquQ&(wjTeD9xNM_OfWPE=910_h+Jm_y$j@Za00$C@s=`;5#vF?XMa0uf%* zg?MntrLd~q;k%ueSH(u60_28$#b3%5N-kMD^9(C7cAd4s6_0rbZEsR2Crf2`{N!V2o@S^Njm74>%>EK|{B zah)QqoZyAb8xfy$63W*b0BpRjh0$NFUX3&OpFG$T;&t6c*GI2QC*$wl+~41&>9X>E z>Tvq9gGq7&ro&VbiMTP2la$HA=PP7LS(j9y~VbDCzOaB>SRz0mB!ZRqoKm%m1ctyF{X zi7y`!kc2rU9U-*~1U5PC>0wV=lwQ3>m}Zx7q*3YL-TTFVj^Rv~ZMO#jmZCyMYLyt? ztons}JaI3V#!M)Vl^1WWIF7kA8{hqNv18S? z9*Uq=U-I~vTi4zcU$#9*P{=VXr~-EDp#?pNV?_*9%AmgJ3zpYDhexF~#`MT!Z=*)x z`74QA)3bb49f%V}^HDGXnJhKhvvxA(Be$|KJYzL!iqYz^C@*ebxx0Z%aHCB8*wF|% z+W24$5{d+UCGb_xX-o7+7SHGanG=+5C8hms%Wn38qYQI)@PHMFsVcDw{RQgoy2cz@ zN_K^AdvB4L+ngPhA1netX-Q9a)MifF>P_l{tv<2^vf7A}C+$GyFH}A6Mto>9G@hW= zGIdNHQ@Z}qagx-6%8Jnvr@ixx8RG$C#KhSl5Ewwo#GH37(oHbMErDNvKZf6_i%B1N zF)VBf!{IcZeTlTV5`b^^7-ZB>`E)}lRzG=$_Hu3PRfOs^XM#&=S|sqveg%vd`<8v< zl4)_V<7Hl(O3aQ%J^jpW=WcbDJ2C$ zlP@qmAdjK6ep0Q@?Gg_aj_WPv*(NMtZm9X@7f#M-*s8wv*PtOUmf)sZ2ai!;_tE(7 z7kv*W5pfUkW5@@eIdv@*jOz$upBw+ssqIs%a%Hu3ilQ{u4=G7g64YEAiS8#&+E5yB zz`3PPjQOva6tlhIPky}m`Ifze)<;bXQ@&}^tm1l&?4i?p7f2-!el7z|>J?%GHNli~ zS1){8_U6?vehjYOqTsnn&mg^O)P938RpB!gx;|rGOD-IRBOg{~Xck`ir2z&s_b)aC z*r$5Hl4F|XM7NRL@CcEUP#Yj8=Ynj#i>Uv;g#^uGAjbgyt|P!M{&P0S|9-OqqW^Lm z1&)7_0i|{6|KMv9Ze_7K7mpB3F!DD7Nd%(uvi$ZR%`$R_#PYv za-iDwcPytca2m5img|xHgJMoK<@g1@eDwa!o1(5 zyM_#bPzhwT6BjF)xL>0pLYP-tX@M@#L1aVZ|JF-}|DoE#3s_A-z-sT zBXFA2ley16r7zAm?4c2G(#H{dH4^qCcyJ;962an1TuiNmXe%!Y>f zR-U}D_RrjK0n%VR%oH3!aQVqt9zCq0fhyPqYq1aF{q?XAIFGM7a&=%Ds|FzRn~B;@ z5QfM;19g%X!0k%-QR(%J3GR~SK&QP4#!jRmGBWun=7oVC6$G;|uH1qkC#3f5kL%8P zCp7{9#YrH&+wt>A-GneH!o(r2$h1F2(z`1)=Lo4XFXH{XM;fnGAsH_#@8yp+Uv|!3 z7Hqk76xZG4T&5Zi9Kgi$sj35r!UhC%qZ0}OsN^FPWYw}2^T^d6+2+Xs$q%yc*ZY8D z^bqOdO)h`)!M0`Hr859l0|c-de{Qn;GdTGTbN0XfZZ%5Z95U24^z*|alhA;<1Qw`G zQ>>s$;>EXm0|2ssJO3OKOQ2-Z**yl$cs81v{QOsfTZHEa#SiYNWGl8n7#^UZ42>Xn)!5RKBRIL00`>%C()jtN*KyHgQpg93!oa6%XE`?Bw!p# zk^2wh%NdG0_;_Z81FrHAnW4XBI~wH4MoWQ2z6=Zl@Z3WSq}@=xA<%MGUd`BhAjmN+ zrw$U(G>zk2L4{{*r=GWwgQE%4M&8wFE2LVaVM%JQ;8S5hJCYbC*SNSVN4wuCY~+DFczx4igr0@OXp zi$fBi%c>aw^WJaUz|4Bjm<&*o5F-3vWyvq%7$C7;*Z4nTpE{)@r)gpMjDVh9L`0FM z>A-pry+R@y{0$@4TDByX9>^Y&+KCtVsipX6;>=l&s7%i=ZgFqGctt__bIG+HySb>8 z$=D#q`0)tq+Su{f;nw$8k9S{)?+8Cs)SQ3vu?S*VyJ7dSZ;D8$GDpVlwnVM}QTuH$ z?EWLrmgP`NTnEhRgBOy|7IVu1X6rDWNr8fQ<(e_Gas}Guoitvz$?~$$cvawu8p!F5 zG5M#!Gc@@EYB*8mVCAmX5)PTp!Wa^7lLNbDa)pStqKotTXD?ju%S~A!61oZGjb(j)gQrO zOjbao^^D!zNa?ulXGIpF`mSfY^>JRUyMyf7nUaC~OEW8`Nk(sDxR!Izk|iD91EYYb zCWm9Dai7TrmZCD1I;bO^3e6_(=L}j%C0im7{rtRN)(t79f8C*GJ53-NY7^eG2kANU zDem24j=rv~o_G|jNdyPe`)}CZ1YPXuDm}=w1MtVP#m*d@%&EfaH0xEz$&GExpQKEu z`sTKEIe9~o-0HrXM!1Koxg<)MZOq3KsfxMoI!-37+a>XSZ7b^ZN-qWysJ0}27dhr# zyt8PPj!|x%v~r`7=3DK{WvNa-oMT%;aBc$}I>Zy&G`j(`FKZ;CfCC5I>)w?V1X zzIFyb5R5Q&U6nuOmY=8~Yo`cNceNwnKkqb%qG%VgaO1%Gw6e6X+LMvZ=QIc4w~;Q8 zdaNe|GK)13Blz(y=Q>j3RIsvG6}FnmQaB5Lr5FW2AW@VzBxaF^>yN`q2{k(Rt7zA4 z;O890f(W#|dN=zFA|r;^fR_X0MQWy%m|Uy($wj(=Fvy9%qvJlwrrvfA?&kd7EAsqI)CY`w>D-rI-X~QyQk(H^ zP<+}hVtm?e6!Py1FWTAMgsvkUiiWRh`^tG-hp!PmuS$KIeyaEt-7xrjc%%k6yNWB$ z-`4y=b-zP~4QatY+)jCk4-wbl%vh`TQNt}L=XrvjinuBdxZ7wCfb+iuRFfHsE_g>h zDQ{#)@pMT;-69+ssvzC;PgM{+XLOA7zW^Qaa|giSmsFB(qjJi#jR&-sU11(xb3cO~ zema*s_`X@O)>EPU`Ws2SL&-7p4p4Hw)lm3hE;U()tob^J@}< zzl+ZO1XMLj^Q-?BoeRpB>%ez1Qz1csqEqLE?()$n>978|ONU|jC3-qK8c^AMD+L24 zx;T)fqKPu5-HTWDJfP|R`$fnu6l zQsTTS&k(1gc8z``%j^=#cAI7@`pGivE|e()um?RD=5DWev`x&5ft_rg?n=3$1U4U$ zGwp4`9hi%`v^xkDho0*tsYx zkWvC6Hi0+4m7U8SKn-uo)-k~IXM450t;Mgon!KR{U>0FxYGwQ%`bqTL_!~Lkt9WSk z6AFtzd@w!`7-0NOj|>^Y9}fa>^hJ~tD3>GY{p09Mg>?u4@jEe3AKyFF@b>if0BY>> z>B$h(*3-`Vq+CGaGkh6#(XB8#>#iWTQ+sK^7#hQh6k8G@JpiYuYL>bNaTSwO&d@v^ zsEt!kcUoMfM_pbv&*(NOB@Ml*rtY%isGHt#QLSkZH%fR-DOgl&_j6^tR1+6HCHndx zhbYkZ%8?14&C*zZ6Irs?;0~ODB76)Ldg$(+z74n9{I0^)ChleE!IU9?Osds4o{64<%KmL9Oo&l;8tNa6SpTo$@5O$bhvkH2Y!i`BGbUI8y)7vLZL zv$6Gm#tCoCtbYbNZ&{n@X%z*+fRHK9;NU5NiLmz-8Jx8;fUHf0sEqoJT#H3O8U^{^ zX|Z?t;J}Fcy3WSQi^=J$XPM))@0{5#Y`1fdwvr~aDC8ON8{08>#%AO^{XdL-&}6G8 z50Z`etkmMpzsF2@83(>JP&DoBVXLkRKejo=!L`S#xgW9tZq8VkHfF)ssL~JUZ#BRhk-mtX$ z&Fs(B)5c;HM%pYUDI@|Q$O+-p_h3KsK$m>g7PQ8>oea3pYAG?Tq4gXpgPzU$**Z3U z>t|fP(1n%({y;E*ZV~T~yZJAq9N_2g8>F11G(fTFnHrTO*Y!Eu14TrPGESp%##|a1 zI}`+lAyB|hZ3I4?#eDb&-p_fqbKr;E;xT>+Uct@v)kW5`(PPfDHSb%vcV6o)eig9w z*d9npEDR?x7quZHygJ!G*qWmorWECzSGkC5jte`U%IB)0NkXr=H3!=nfHpNcAPrH6 z#1R*YSvkITePYdK3S$u9AaXt<$Aw1RWXPvv*r?-mT-auAFpU-1?fY~e)8M{-4-{*w zM@t;fTlK>P&6Ts$M4ALr^JUW&ReRLlY)Zsg70yR2jbvW#aVE?NKx!49^2 zK@QG9MOS$jbCLXs##W_+LCK}Uwsz>C4i?IvR;6JWjD=e8Th7gIy@Kz0%ISoFrJe#j zf4=zsnt=Yj(0^Zy)^NiRRC ziyK5xJE@Hvy!EN?l!PHw*ub-E1+V?81!uZ^&yO_gs2h=Oj4CYP-A=ib{r9dhk3q{!>$0&pKqd4)>eRH z2oTryf5pB7jwL|&U9D*jy;|8^x(@4^G+R%!gWM&6tEmi%#{C73 z_(w7rXxKW{>6*uFn(kjAq-ohFU0so|)Zi_!H|WP7iVWy=zB#+=gB>w=!b6YBP|#Bq zB1NxWi1NoOB?)t4TuL0aW2*J$j$lZ2k-l{8bxIgCjeN)^K!BEatk||Ew-VAK>7C_7 ztl=*x9-lbHtf}(W(|ftyb5`k>8QmJ3Tn0=3b*Ul{?e9m#QYZi9;L*gQaGf}&$N~W2!9vT z;mtF$e0A|zvJSwBZkJuqmW%f-XQ?uL3xEHeYM`#2X7k35F#y~&|D0zcV`plqYv(F% z==%Q|=|{+kzhy>c{8Z~jyPFdvCL!qQk!*FF8~|XoeM>?%6j*@C59$pX#J7LN5rx-a zuUA9ja4;s{x8HM2zr4-#J7BQYDvdkaLBXM`k!T1FY!r)bQV6*>GH=lRWc-o)J$@uzC2EaSr(-)59)iGK6C!&+MAO6fi%kxvZo*vh(5lqLfu4*SaDa0M3K+p8@*dCA1sMbzs1=Fd;EJCNFT6sK>}b=ZUKuz^v93MuM_594Yrt3DIh@RqctD; z6L_yTf}p;^7Lk=EMt}$ib1;pAVjpEf+!)(5{?A{z*KMFEL_{=3OJ28W(@iI>-mUHL zJMfRyDlPeb_fF zG}GYP!$ELj9u)S>(&i+v-H%uowTR+&tjc{#oQqD9PNgJ3ER5wxOmqymCI~cxK*Y~A zU(G!)B<5@EBO6n<;ji;3#PzZVZu0_&{UyB*i#y~^Vmo>PeJd`?FwDs&!WnJeM9SsJ zQi=XbC%ZXX<)|1I82w=<*|@=93}BbKHK(&-323pX9riP-^_?-WdvS8r!zT1$-OPNw zK!cN8@)eoh0et%;-T(&?Jr|TwU?-JNlPnz%W(qbaxys#pLlvSZ95AEAe4a1uuE_-m zDn5#s_Dt%zKOeXR;aPO1TNx@1;rUVRwEx{QWj&n1z5uLe2w-jhtZaG{Z2nzs{ioCX zchRq~Z1Sx9mEfP07gV*DQb7LQ0Yh9I6M3qIsrINJ+`2$W4b)t>FL1Jikyr9|>R$mI z^K1!hacnXBkPew1il`3ORFGG4xO!rSbog4MS8^ZH4C2F#q-aT!Lh{x3JZ2_c&_Kol znE}0K`F!sW6!SZAn-RS3MV7smw5d%!Q)wC_^B?pY8O44EU8Hvi0kDP!NrXm^uXsfb z1uBeWHGBOYbYgcE*Aek*Pr-!BQ4fR(`PLkfq>L17jz8`OjDS4f`=C}(&&+k*G+5_$ zKazirG5=Hn@2}}lrJM=z$aeRe2LO4l7!MKfZ@u~0{#;f8lF{tEG{hAo1EOZBE$5y^I{O#|AQ%k`)B<&)M@g2{BKP_?hQkWc z7sI!Y>jmJU{o~2?I3LJXl3xbw=-%~s_X6B%A2iLLKaHNYE{dthoqvnfUd&a z1Jdn97Ryl}A^M5=PNiH#bPV{4toDmjFaLLg<4E3XX*qqrg@=|etb&W+X<4{Jreb(SuPZ6T4YuOi%(z|BRZmQDkVyyAjY=zqA5R0 zuU3IDGA$EJ@-U>0y?=zg#Av!G3U7d>=Dl8~GU}Yy-~V#0(kjOgU(5V4=_A=(eE_bt zssYH0w)&oeD>=Yg>qo8p=Z+8yUx9Cstwp`@5(7)z8?aP8sGcHsA47i9s#2w$Ac%*2 zDp=us`|a=w+r|#Z03f{cS3&V_jV)~GY;0&^`qz|kh?2GZJfOq5S!Lb9Xwchgv+j~_ zlRN47qd9`6fnvNQh<{s|}Hi zWCo7Fym{A+GHCNK#KyE76IaYOexaRUt#awJ$QaI6{p4<9ikXbArMWdn4HIBn^&(>& zKe6goCWSAhxkh9C_c-n*x2y8)aB) z(Xk}=?67R560I91hoV?~lVM0oSzd@cupxh16?%SQuFU%p9{sd**&^(q)du z*2|i}LT>X8H|s3JGyL~8DOJjj+u`Ew!1k_7tSYx4s>h`DyhE80=_!{3E4ycgW#R8! zubhR;Yq0aHjctPxsqPU+p^QVz#$3^^JceoA^?DdkHo=T>h2~<6Xu}-r$m@fx5zfpJ zjG+M&MwZwzB^Qa27EFta5|;P9OxlGmRbDJ$BNl88?;T(GRU5D|SEngxyXq150zdRS z!jtnvf5NzTNHivy`cfhTB{*q-))+8|6#N3^+RNAH4)-o@1hYIg-j81N0BYy)!}=;> zz8g{EV-6xm*dVfZD$?O-D1E;Ja!YWIm_$nIXs-vho+DNGyxK) zt#{y3K#PALSb3&F6@&-C#u(t^?@2{}1|=${whs1AhBp83U;k)GXwQqCsyC6t8y)9; zANnOzMu~<^%?M}Nxa|6tY@!!UI zf1>ffjfwvnqh&=h?oTrXCTU!1Q#GR@=$~!b%7S75eX-u-4Ra@5zwQ6wXJ#fCDt|s?|&Spj#Rw_lD zCI7laaFFv#j;LvwHIsMM0fh^RO)Y*h9yO;!MimMsA9UG|$Mp zBd$6kDkC@f5Nb3%s7TSS1jTe#NbC!qNrsMb7nIhU?vbnp_gO=1E*XNl_~pyhGfzgi zN$*Q$MVfcq`x00`DK}$AVT>Qiz1Wz8pb))B>$(1h3kICurMatb==I#S9S16*ptxT# z4gR7*V-idj&8t4!NW3s!u9m;}cdbA;B?H_c`rdpIcec{62FThfrwg{ox@5FelEk$^ zC%=uEd-`GO{#s|W_}^A+eu)wPS7f*D3ClBh_f zhzywl=pQ*09^<){47z@Fz#>qe==tt5naeyUt3XfI+!^F;O3hI9{Js%Yl2(1iQ7mF7i53txDW z!D`xAwg$Z{ki;*<3ndbMQiT^@aRR5iZC9n3vaofHYdc8FKL2)nkL%1)soV10Q`xjZ z)kPJPo!{6&lmW7@+)uwhh?1OuD2#=vU>PFM>V%#&wDMMM ziBYu1m=mP(5qm>veh^asFk#XpiK!O<@w)LB-vb)J_n@>rq(|WV0-|0^)wpM;&=5+o zG$xnKVu|E9u1|pU1J@HvP8?@cU$r8w1zBo2{%s&DE|CKX16W;P{7(kS$S zT=!yDF4*ttx8;CGn-7R;fWu@7aP@x=j{jJ}3K${z>qx~Yy~@u6i17BOOLoaMw*E3I zIDl2E&aHW7!Tuay6TysDX*FNUzR1&1;vRG5NBAX2i$seJOus1(KjX#JKLFV}$;(}K z9c4Xx_Pjs5!1hw%jpQ0o#+t6HG8XOX;TUTm64n^<$c;Vc=jReqI;aXuZG*rf14mIw z!5})HN>=s9mz`^iFRi^0-nY9gAK865%5hHhO0jp>`ME(z&uk%fAlg;mAbYEIYiT-C znIQ{qT48s=RQfZfN~_C=VX~_;QsZXS$+L5!HvX$_nc%mJ&Rv)S*0lrft{}8~nTmrM zbXBVJZZnMusk~0(Hm^?u_crb|nH`aC`Ae4Cas$^kgqYu{s$F|UFW_aJdh9xyd-@n2 zQ;93uz+Ip1Z8GT0R=lbY?s=4;%|(T@?YRY=e+bzRo#R&OU$_Rr#bIY@aa^ z04R(uGLbQ9b9JF%Fb%0?Cux1exGf1#{TxVKiA04WX##ZTdD zG)=GZwbAHyrrVC3xJ|r2ardZBd;wei%BXcUde~SCgg^!&1I9tufT_xJNK4=Dbk(yetATNu2}} z2_#;kv04&hMM<&6n!$db)t2J9k@(r@8%7(J0-tCsa)Xhc3j-v?NK2S`3`;JbgfsLp zV~VTB@XEn;`G5B@fVlGm;9&mo4-N4D|EXW#>|jX`@|)@bDNIY)3ji2l0UL+R|Jzsn zm%f04lf4^2sQQYcrbEbd}e{P~??BuXz312XNSh%^WTeaG zWTd~570)-+*L@X7>X*?|^Qv*X1W2$nP`^HJ<@fghLiDI*5;;rzsAkVqKg(Ks#g20sjy^Zf!mt3nWF)+HLM4 z%pyHT`?)r^Q*qpR+x~6qEOmeNIk=DtM$c=@IC_)7_o%uAhh}h#Cc67g@-#w zC+ToNbADqoO`Eb>tJxWBo|;psm+RA_$hR!^k8LKn70H*Il6H#{BpbBck621&l0;UT z^Nuz{3N#;s$!5QPAg$`I6;edSC{i7uO#}v&l;02IeB$FzprKh%>SiiPhW;T>T19PIdblPQz+O?bpK}2B232 zkch!I0ZW~ykf);p=v=T{{iDDE#AJr2rC=~$pQA)PkDsLZbDib}P!r(bS9t-3I&-x~ z+t!s>8Ux{>&%lS=CFO=2z3c`fTGr6oqkQox?lZv!`N$aD4Kf~>iiav9G(ISFsQed5 z-y^z&9;F)ls*sE~cnFs$3ziQ$>yIUzgG#!cyl_@07UO=M*?&3l%zuuo}V>1m5! zqgbe6G;M!BK(EUR;|=_fe?sEmn~gvB!P7w-7kX--(Zm1Ykg19jloNyt*$a|PkD#$T z%vl~$E;w7r>kOcmhH;>dnzri!L7OHB!Q(9ur??r-cO$&PghJ(B+Uv*Q5I?=Z9PY9U z)i`qr>JfOgO^5X+M#6l9gsvx`kH0Q#JnYBV_|f<)1fFLsS%COD-p$VQOfRGC0}SC& z`ZBPyCdmr#CZu$0ENYINJ!ZWSc`$ZsU`64gEO#hJ8R>U>F$4V2&^{lXMFy3!8F3wU zovI1@n^KL}n!x)k06GABwuqR$hF05dZaF$KI`p`-Eg7u z>gsx;YTZAopk8`}rsy_Kk?INAi?7k;J4a}>jc+8*Trm?LS*$JESz25x?ijNQoDoY^ zswto>Q&AXugFoIUtvQ3bec|K{VX^^}hk2UyaE>8s>(7 zuDk~~-A%Vu1p-Ama3EFZZ z|NXOZ1T)rGzGr|2B<)~b>9fF>^#f1$>7VPjK7MacPawU-$wmlubbD|YQW}X4@}Yq^ z?2i;^CQ@h~Lt-eB7njl=y#!~CDVxp1<}4Ln;eFv_=~DNiRfcHAjjPOc;K zuv=IhRdK=%lkqDR?AYgBTCXunrK+spmR-t=tG8qW^UFU2#o%1MOgv$xogUi+2UoYN z#^M)fM%Hm}JBrulIshZXz&FF(p;UAfxF}QRTbfA@_J4M89 zvVXdu$IGZULYzSpKE!9zJLFzC?`g64SXaF0?BchNlo-DkVKG!&@|E7q^K`0IKVAME z4Jz?VP=a;ZG;Ou_`EDqYxnk|;^tZ%7B9+jQpA6Ka+j)T%qjFz?k)RVLX1;OpV+ya- zLcm#eBu`AsY<$N{3zdIjH^0|taGj=pzkJWmOKFjIK}Ts9=)Z#|hZrSq)7LrpsVF7_ zLs=*|iLya9jsFSe4l_>~GKa#bRfY{|e@-Q*@4C?!Id9vV2e$4CY)3h~lhCDl2U^oq zDHOF5{F4@hCY)lR06hUJTRnL=j_wTI4%Tw)so+;w5yzx6kuf^i{+wO*@MlU%4#69Y zI)eBig77fFBAV#q$@3`V!7n2QfDA$!lqkhgS~D??>;-Cqb`|R0y63m~45cUA{a@=x zew7~oe$e2TBq8ujcN$O3@5d_*}ev$>kBRf5$mb*sL(=!h1@Ph zIYv(;e@jnH|NLZy=lua+DpPksO5TX>@tSxH{rV3wYDsO6E9`fym#Fu70+>fo9s?%cON{EAZ7KMIx z9uS6|o9JhZE7}Aj-n^er$4Mw448Cvo8~=k|8oUWtAjSkyJKchB3->^7%Gw z^cz~cgAGa)RYSN#b7cEibkb^V)f7$`YOHAWkb`nuw43`O>l#pTR;~CIN1f-MyJYJo z>3xL5TU?JPnG2Ge@RzSQY3HN*2z32aXH1_erioj2ZLCo5UB`oyXe(W}4(38leBHt-Mbh6FRu=c*y;3cp2=UtNEJa&ctzTRc1zr5rTVAV$e zHuC;&u<`GMvwyiV!+RnX zs<;e)FtKrui)kMrCqQm&w;z#9Yovb;5$5@{RDy$-tvfhR@!d>`ti{w*2xma4DTR7O zi5hjY13f?0@}Lw+-3fa6)e4SEm5mwCD2-Z=fud|N(#=@j?x9?bxkSxa&v6Yy^xN>K zWSbQV#_-Ihs+ao_rDWF7gG`Yd`bBASgq`MwZ{HFC`X@v+Ct#r=%sr+WUuWC&qZb5a zW_z77j9`(=&l)|eJq7ei(u-2!y97E!uUUpM(z!o8XYyhYTPHviQ+mMeHP-uq?-0=? z(C;!;!+c1o$Ik79IOmS^qVNtDJe3!wA&yELQLV?xcum{G5|r6~;>Wtnd#y4|XW@Ae z9*CiVMP?6^R8bgZS<%nyF@wPPCN8*ByY)aF%$9DrMPxD)BZdJE#U9bu%c&v$?>F`N zgAob?;Fi(?&eY%Yaem#@U$MHfi=oRuTx-lPF>k;oZ;+22z1NyrMa@e@TDe+-c@z-fm9GhFRutm*y9)`1UWqYHLk${qw`|XtD%s#~jbCk# zM{(LxPSaxzQ`U(q7I01=GD^J6J`=86U3NV|#oudBYoDXjK25lue0>caj`!gNk-pMs z87ZEKpFlS&OLYr{AQSGi*Va<6;b*WvWwz}DH;$VK34T%oZIbq{e7{&7YXcc{0G0|uXl@|qeK zNZ2O}0j<+t96?|>%M%_c{uv^@y4EE;e^uVI@g7^%VRx$!e;w7-u3O9)JqshfhFSfA zASd$)H5xU9hFtRf`j@mrB&B{ z^;hIVJE^-IK~7N#P(#UIJRBfIk)RyPYk7;z_Bl|*D(p0Za)8pR4ofCd^3U+d(9}9H zBk%bE=T`EU*UNuDawtbD>k|O@k_SK;`~M4!{Rv=yNeJq>f!n!(>(>>}e!n7T?afFCVCG=vrLQY&r1+fBhbJ5W zo2H+q@6Vvn(@LO>9|gALCNM9O=&Xbsm5H3htfU-oW41H{V{EbwJw;C|CFeRZ3A9?| zKNOz3u=|o(zd*zrUla`!Dgx*_jE)j~aPyNh(rfSbQ&O{Xg2E;SAqGJPfn=!rpxfUI zyx$P$0b?V;#|;7z4+{fc3yJ<)4(^aSV$cV$i#otQe?OP^hx7bL;;s5GO~shFF6e#% zgkba6D6x;Upw`s3JP4I@*2EOxb$m&x^uZ!@B@Z@ezb0SwLkdf_`fV8E_BA6l$h_)S#BeZbwjabQOfF&dmqc+i z&8e6l%SL0R_Eokd2JrH(SUB8u@GE*kd%wjH$)7-6fdR;*20*6&RSfYf6Ogns zv;XsW05Iy8+CW#71Q-}4e$xX`q`+!gYlJ)*hCT~f1}##vZM6OJh9PEsmZo`0xZhWZ zKh$|IZKON2CDv1uvb?6+a{S)jo?t)JZ0CY|v98?bCO~@8$j~`ctB`m~trFuv!n0|e zZ{yI<$SaciTEwV~{nTqu8LE*M!RZjt8y68_4Q z@sQS-hy#K7&$@v=Q!fu zTCuNyYJ8s&1#*a+g@hY}um>}DQVWUJ4%Td~`*fj6msgS!)akt(aLH;$)@#I43Qp;1 zU7v;kwxcYCjEFa@ozx;isv=jkWOltG;$Cl++Ou_C>=g_w7y}nFF0Qii^Kx~8P(iXQ z(mms6#%m$y^UbDJrl`V+^~~A9y2J|06f&j=1lJCpF#0TlLL~5&BlfDuAMMS+F)lVN zZF7!tAs*-$BNvf!O_&UiqNkzr&h3;Zh8>rEWccw9v#3Fp$xdahqAep! zN22wBTSW%ARs8=isPm7?7UMs{rx;nlWrGpxc~Gwc^8!WRofBl=)k4t?7nN67%OxI& zo?O`drsMGeo>b0pM~Q(DDlRzn==ALagCGh4Bmz=@SKDfIsuH7NTCM9GY9+H=C8g<< z+sIUD;`um*e1Wr8aqy;)`8Y>e95$UC8T<_CWch}yPB+{Rozja7+%PCYe$$f85tVB! zE@8EeP|*`ba~QKZnBmh=Q0wI_t9SkdayFL5(>H;M``DV9Ed^1>=ideb@Hb~Yk^n_M zee@3>BL9Dw<8Na4f0hFOz0gPUiy{pe$^IW+@TXC|B;!U*>>6O_5Mu1)wBM7P{s?-54_F6C^}-fQn&+- z3ukj}#Amm_xn5}40LRYu$)Um9S8jloE3j3yd%bwiZ9Q*C4i~w$49R`Rj?-Ij0NrI_ zI|{4W;vq3s%Ug2U!H43on(5FKR(H(~1n^?>XDN|q8ZX5KnWD<%-ofT3 zvmDIHLgHBiZIaK~ zq6KQ2!i4PrrJhm|*@NQm;pPqv>mz(+kuO($0iUPPokU6Vr-ExVMg zn|qV<6|77l(~pC*jN_$D&T1RWtHVtkd61Jt5`v?fB3m(&P!>f|vQlKQ3}fmZCWl@P zsnhvY`So`0L_8&lkT2=TGPu~_*hxOtwVrIqat6%5iQ@r_r$SVk+UQ7(YZ?bwQ@SP< zmY>Lc*>NRgE+bGFIwggruMoO3Ml*rspS@Cs=NV{8vY^RDaMqHIP_kIWJs!qOTlRL`ZEiw-6U!CXWft>WER`#52Ula z7Wh~XsCtZ7)}8&m>{r;Gs$NsWWn;f#Ap2000A(tU$qbJ^6M zR*&C!TspwcTUiCizo1Q7Lv>-o>wNI>k*%Te)PS0$xwSzpW2_MM63yZ|6|E^WvBk*$*p@a{t6q3|gY*beWw`3i z#n-VDsZC+qkXP^&nqv{wLW*n9@~s~GdOU-tU9}ZPO=6|J16|}>#M6stB)t^FV!xDq znV~30h6Eykyb0)gneoMPzMVqB=W~{Dc4Zyj&idD{_c`Ktz3d^`jZ6BG0SimQ@Fi+u zb6tB9(3PbX)TnK}N;Az)XNrQ~?P;URlA5KZwI)7NR}69%V8q4Dua_W^Rw>}|@~cmq zDC_27LAB4QgjnCzIUh;2N}u698eEN9_sHs=(qW-d>wGTSatP*5IYk4F#s3MrY`vPb z^c4LBXR#t5rP$A@vVEk5oN%l%YfR202@OqdB)JH7M&38>YnpwdzeMaiFg;?4;6dMW zYHhOD`$*((U$Tdwxx?x1d=5i%url7#u)R;Gu*UIWmIG9L`9mgr-y#Nvz!013jSr6X z_HJu{F@CutQ1P}?b^=n4PV=~TI z+`yM6!pXC1OAwCyZ+p(m3+9a0%yJ1_@PRo_EqRhtSwS3gea_5Zx=t34Fb5UoM%$*j z!wbp=&{Qha<={i+X_m)tK{iN%&8}qt)7prp0M|M zbld7?FR2wVXZXAo{C-<&)d@Fy=+*=C%N3QKfwns`cI6k!XE~`JF#8=fsUBU6)}ZrV zT_dgwwd$>Ks&S40#71D$Zmw5F9yv6&}Oyr+Qmo zXvD`49cyT)`&dy{lEjlO#!jPkhc;a!2%niuZQiwR3W*9F)1Z?p`rrioX}_=_JC(&bRu49g0Z60tSb%C(sQ>(i$H@((f|tf{Ra zJ=j+)HjCD6SCwwK5g}OgW~+zrdl#e&HOV_gt|3BZfi#q};?}fr|K=>FuVTQ~ADo5S zQpp1DioJuUti~a}Pns>`xsmM0p z<1jKpl)h?F<8?-K!GVdrce>|OvNOH00c^uLN*B!bMKIqb%*G?oT8r2n;NdJJGT@8h zN+{aV@mvb=WTEimlBDOobDU~lI3t=WPM9C66tCAAyw;1}|0oeVqE%E=*dA;>#~tHv zDcE&ASOn_B{vz^;Sn(~`y@p16Tp8*h!~1gwjQF;bPC|`ss&j~9&=b!YXL206#d<8V z#wPG2zdQv4PuRDk+*`iSlJkzOo?NJG%9z@{au;yMM0moucqP%~$*Vc-p!sqm4M>fk`=hd=gyjJrTc5%l{_Aq5aE|Esf( z^S7ZhKoG*v*51U_=KtugQ@m-bK?5=nT0mk;_a9yh7_0ji+yD2HhoBocpBuQZ8#s^{ zc=yc$(&d6w&0zgt{CJ>>7`P# zq7Sl!OHwJgqCSC@i4LU?k(PZLD|>1yBaLW#q%7lnB2X3J%@CmoO6MOT>xUk*doQe9 z*hjLqr!g|r7(8PLxmv}ESJp~S+tW}|N={eN0(KDr73uP|lr)R&wl@MA)g&|&NDY9> zy$|{+9hIoO!Q||}i4IETf4T^AY-}>qYmQs*DFzPY(!#`Kk+iMBCI&7BPDWq`F2;%c zkr?D72zC4j_%35D$RiF+rUM^3g0RLq439y^?-i48@a6|q0Be#0Ec^G=p}(HUpS$CK zt!nyf9_O!85}=L*=LRn422LjiKD9l0(>a(pm#-og%J2n}I;V25UgR}%u+zooA9moD zA*_`M@fEaEbTo66w9<1b_qDT7S`$h@;FNnmzU5T;=hevxydqD>$VF-E$ zdx6O`F;{w7xPL1aF%;V5+W-y|IAAa2-xt&Vdh0@#b|$~xILSXh`O5-X%GQ5sGq!B- zHKVLWc6zJZ3YHxd6$dLwkjk}1-Bh?yRXy@`*I1#*%x{0WWQLaMZ0N~wg_KId}{j^??u~j z;M_9x6ZS$o<`B>Q8oele6HhcWPqwRFb>69-=B9Ydtt>Ls-(YREMP``q_#I<6p_ZZoq;xJxYO)t&jS-_fzkn~Gg zc8+Z&&1cQnQ9j}NM68Mh-|kLcifBzn==^PC5^#X)=c7;-EUAYb<#WMQleZ!Y$k zi9Cn_FP>Mu-x8@#DkpCt(P|rI2U&z9oC;}>WF`=4P*@)ljLU`HS_*=(0PT#xF4G_P z9fyNbJC;BWX*m1|F#^hE3?B)JFUoqiwN$9j7=||PcNLjTNvxDBz}@Wz+}*#Qp!@3p z|8-vfy0L_R!k`7ipOxEJ(;l?Rev<#K6IX z^bAZ81lZ#e`utVm8W@=vm>3xSKm5WtY!Ge`koZG`ZO};P01?bq%(OfjJa9dRuOq$C zg>C*+X#r_Nf=ItzdUDG+8eD+A_5mM^|G|s=YTH;E%l^|N{pmLbVt?V=6`xnvOBO_YPkBk@rd{9d4}tUGK58f|AU5 z;5ZG#sXxqD-Jm7&c8qB}a_o*@f`67OgX#N|JNu4moGyG+jJdsxWiit*(e-YP|7J}u za7_C9xq(Om>I!9-(;c)ePyqBYj$gb32VD93S7-HriKeSEtJ0rsLskhTQ^cSEsT&PS zu6`=4-|B`yHyf6}h>}l$skp!2*Z9}@{LRW$iNA>Q2qWg!wl?LjSI=OyrNn@e`8?__q5fREGo+ z$)h=R8SB)NrLWH54!@ffgURchENU4P3kbd~Ds~*PcD()KRZV(j0=&v{ig=vw*phc~ zfNo^|Edpj)rPYW)vXfR)M^8+Q^?9BIYcHszdim_2h8{Jd_gfF~5n{mINuL!cq>6w~ z6IB3hsZ}{6I6`QrBoNhSD}_dJL`7h+wNj`6PGkGasGdAAej^-vGoCt zy(QqWu>ObR51>lgnEo*t^jj?TSxy?dp8+NFA!;c``jP;{!7_>9b=yA?@KRW?9%0R+C#w;K4~A`Vf#D_LR0+^3d1>6^ymOntghEn5z3bm@NL2Weg0d#}vmLBq}g9 zMQXa=r&d}Es>_Z6aR3MShY!O4!6yH*#9w2YF8})8Ul?1Y0^lSmVEB%YuiAQf7y>?>ON8+6JaE?PP!j+ zp7Q#%*>ZF|?}*%TDhZZypx zEuL|VfzmkAP->WEpfOk$sQcWcx>bAh^3b{5f2TyO%aF}xpgyTtmt9(RjiQsA0h9uB zg6ZTqa?=6>76|*B&#QtTHZR9LJ@#z5;pyTkbpev?$dpb3Jz1|b*qUpHvLtO18bQSx zrK__tso{7VLulc=Bfl{aYlU4!TfZGw&e!r|A+j7~g0E*J4b!T!0=uFk*Xv=PMPppD zA;ekc$n*PDx$M?jV%3_on74s?F~a;H@I|YmdI2yti|sgL?XYO z2~}FLR{Vg&)-;C@t6)7fHJ&h`)v>;$ubqUhZuo+zlxrm2ZwzFOi>ks9MGkGZ7i%2K zSZt7)PQZ*jDZ|7(G|4h7F?W!1tdL608r29FOpHF9K!1*H`sJzZQ6CztQtt)f1$elc zi?XL;1WDRuDD3+?A-Pz(r@zB`Uv3aGoE#U&!8VKVI|eqDDZ)-4?4&;l@<+t_k+r}p zMBNex9|E#%G)?v`Y=U>qVBB|AmmjM7{DW=s-}ypDf!pc8L`JF1?a~H#wo2NPOes^g z^U>oE4}-`Z@L!2_`hn+TDeg6cW%{CG6E7E_yf*y;G+-pEkw-_a9ClGTe>mp5IL{4? zv9gnQWj>I~J#j#dX5Kh6ZS()czLD0C=+JWEk2LzoSGBSs6sP9WGE;$Cg!a^!zb@3H zb~8Vh>jtw^%G-l*3??=+&?6IG%n^l(C6d@aI{W@kdVAvJsXeS4OO0WwAH)>y;h!-0 zo&Mj^#Z7Sb?kC^|jRVl-?-gwR0{njl(tmo@&j6O>FDmvln28Cf;J5GDV0Jbvj(Z43 zER3^htp<9zFziW$Me&x5e{!+MJs=1%O$%f*oV}hl=5zia?WS_T&A`n-Ifz(nnz`kV zgzhZ{nm!uP9=VHgkHpT%eR~J3elnL=e$Cfk40KNAoMtm*HLfxL35DHMwIV}M(l@VKz{!n9*`g>HoV*oE2Donke5`yuoKp(pbwl> zxoNHM(YY>TzS5L#+9!QWrG|Z@yv>a9ltF*m0S|ScGsF=!?uQ6C=ae2>S0tkeU>HS2 z`-ZUGC|)#r2q7PM1N}=bYWb>bSg8o|m@};1#aX&)oJRC}9N6V#n}FZWyr0)`L;vw- z&Zj8PV|AG7!E{Oi&)O>YsAqZrBM6N)QAY61HG04lZ+jq!58*qP3Bq>P~_U9AG&cNdp`+kML z!*s#m4w%R#%OeoHP9Ebv36_6kE|luQ;RtmKu7xK`gy-_f1^1ab2)c-e_uQus2BuwI zj9d^Z(SvH9DN+}rz4_IiHaI?`Ldcv`pp;r-=V zt>b4V_hSaKM@qxNbM^*|H=kFxMt~sGge&;B(ln8|q$;c+rj7J}Q z0^#1tUvD*~=oE1VY7INQxh>e+)LSy(vM3DWPCkZevDqX5a&C5eB~tg6_|uxxv{_5d zsn~;W`I-xgZdGjP-TLjbgQ9CqudtyD_Jb-;+{x!Xjh4hv^P8@;{8y!k$*6EmD^B_Q z=ge)U>=yU8`1bKMU(Z1GqWGIb-Xe04&_o?^(Fc8GPd&m}!IB&9-g#Xob4&NQ%!o0D zf@39tD1nsIa-)2F#X>bEj$4KF+1$#Gr?-26~|iTTB>rh@!)~QHG|s zoltrr1Fl>gaIRmx2IUB%f&-kP)FE%@8FY%!`sJ-jQAkq=X5keM(Tn2{`Cp{l1F?m; z?Lrxy6%t5!RIfwH5cOY)_lMJnaD{n=h*(%SS-^#b<3hxkSVDL67%RPrq)%q6>_&NE zGGa8~B^wYg4Oe)8xnH`2P>g~xNi0Dyu?*ojD`(13EXNJ`_Q9f~Xq8f;d4}2Mw|+|~ zKRRl%ApsJc0l@mpzuyM)m(Tli1*XYghxu#jsnWax!pJ>^l3+FP=7%9_^bm?oNt#q8 zl#d|H=L}b06{cMJgOeqd!Suy)2H*h`NC&N)d7Nux$`VQ#{4T5dbnhGQ7m{~3(_8vI zKn7^_lp0ckWMC68?b7R-E;wr|Ra~9@xqe1Yw)*eo)T$#4pLyI>z;Qnnm>kP39>Snu zPg?`WB@5fBM@J4+5vjwq7T_n}x0XH3t-2U$XGv99E@1bkO>|nEux%C8q3>Er9}6Z= zVXKuz?!J8j4}p?jS*S`(92roPBo${W)+;Mbk}UvJVO&jUW)?SLbG!dEt2l2CS&Zt& zuoaFd3Of`7BPIbW4CB|aJ!xVjY>3-?SS@C4*ZP=57stjNSI0*hLkbXs6EC*O(BLbu zVO){wPAtUHmELNyIg2%wKi80uv1c^}ktcEW^4^gg%Usz0ruFa`lZGpzKC+4e zFA%-Rt5{++)7Qabib64~3sq-ckIQo&xkx4aUEiEiQcp z{aGm3`$dQRr%BoZ3C4HhdI1tS^o?ak1>uu#onkIkv_m}|Y|UL&ng#%lAjO{bav^g9 zYrmUwPEv(LO9d@Lg{3L6b%;IVl2#ru?V%Qty7EV_U1HeV@2b!+Rtcj{Oj#L|-X`h8 zhV6+mf?Sb$>An6@-||D#Q8yF~+GU1Z79Pg$UDz7WYj==&7F0SMyeU|8ho-#Stk54L z4R<@L>~&jikhYxZ(;QJTqS@u=dT{LRWCHtEEHxXM4IH2V@|tmP64*g(t$_=%E>KFN z>QL?k?)|Je9q}Dd_XPKer_b@vQ2OF{{(?DS@%9Ma5#Ar|p1mPHrPY4fqFBFu#Jk4W zc^-w|&YO;l$4B-Jh!29CvSxhc`&+u|{9tQK2M8t)0S@FpRcn58 z9)HG_SxSo100pJDrOAm2nTfzn*u-2AeBOkURsu-qP6&N<+irAo(1{udH6GN zkz%lU(vwD-X6fmdU+m3}UiWTc1&~M3%F*CxaI{J_Iglv45|mgN3=4yfD4~+=nu=GH znItI?OJhe(l)21fI_kWW<)@N&Nn~J-fjE(woRDB8IpL{dirSgT;GWC!p4>YpMAH}4 zSqN>>qHpyvdK`xAIOgUG8HKU11y%zt-7xOwE}9OMmy0 zFB4#VFTfNb(|_>Xk|uzio0evl|K1#EQeFe>K0(L=%M08MiE{U0LJZg13xaPZ{DFjw z6a$X{+)e9J%374>W_`2leFgGDvFBn-RR%sZ820sU(gmQ4+VT8!_X7MOthT7P9T*$h zhDjow;siWs{`vsdZ@c^}t}<^!+mR(I<|mcQ*;^X<+<9SF|3LUq@Kvh;Cz-9oiwK13 z#al2d7$(>Wm)0v?lbL1i5n|e!4)+ygt@DpDn`ahHl5IC#X4*+1FitFeoOcv4Vhm7k z)IZ4WQ#`Z<7iSFP@evWh%qdr{${IY{$;IKu_r^}Zx%yAEJFShAr~r9EKhm)@LAA$3 zmq2HXD>2eEfoAnO1BC(;3TGnq_!okzhjG7xirfc}3IU!~f^T2=n&`2NZ zg$J$JEzt@pji^_A`=bfC!b#ujH)ellW;sI(;2Hl~t?-}H4?uIx!PG?A)xp%s_!k@X zFL(A=Y5Biv23h>yH-pTjtn?(!w9Jf@X_N`w>?ywL2oOSVh08Fkbv&qsvc$FmtJ~ zL3(SuLb(RH7!>xcvLbnEJ$OIGtF_h{7wFP)3i@(|0*ef*W_}jR@sVKPYj(%!vS3bSPmaQ+bcth1REBLw9 zj8GZDQZ!V(gbjKpV{w_wD-tJo`(-TAlCFY zompsn8%vE}+eu3s-cHqXt{Wow=30^+i(V`WKR$Ku^IaPAtj2|*ePY@KyyU&zH4`WZ z@*xMw7y$yz-vJj1h>TFwJ7WdXq;#r(xN}Oe3rahH4nE{Vi%+mc=}Lf+KdA<>Od_+C z-&y6$Z#f3vprZo<;E@Rc+ywJ~Kq_UxAf>B~q0?`V?w3FiU7f88E+O2M{jGpbcK>~GOw(23qC0rNYcrmsmT zLgYf*RKn<{u}cbyErY~ zi)cy{70mjK=NN737rm2UajS+jWD`}yD2TG}`9B3t8kIU%!u6K5VAO3BwMjx}x3Dqj zxN!vqUB3DD_RbV6hlw6*qYc1;FDMkAws%wua|CY?tH3|s_;rBbu-*PH0y}4@HP#6* z?60}{zaJ?7E6A0xbpER(`scm)&$z($x458AW8{`5WRL*;8u%}2g34cAr&s^Xx%@$# zk;KIO$-=orl`rv-;hwK^iIX@!__^R;5Y(-?tr_}8dr>p>wURSWQqnbcQ$_5&L!@$JAO!H4m2LesQ(C8(h2Ek4`k|CR#@&JZ_ z1sLK#wKRkboh^<3FTfGp?{AIpKXuA|{~JK`;42y2oivGW#FOAB4x-{S-j2isyi?rX z(f^OKw~otdd)|iWM!G?|rCYkYLt45^Iz?K#yQCzfOFE^yL6DS|mQd>5=+X04@pqng zf7smo!M^UbXJ*aJnl)?Yl|V0~RTx4Tf{ei6APATY!Xv$ycppTWs^7~QfcsfSuK;hF z54`F9XF306`S+4L_q{5RW&xOjht5#CK-PsA)ETge*!pe(NVGr!5-olv1zax!9Bgv5 z{5Nrc^4o$$h1n#(bhA&`&Id z1Eg2}%%LbLN2-^jm~iIE8&m{92}LG&)EMl6U;2-Af!Ig z0nt7Hg?bGP^q^ybWD30$_}_#=)s^ORBS0z@fK>mLnF18p{!R15%SqmyJt=+`Iw0yz z4D-O1Rq*f?r6BRcrihB@Xh{;o$O};Z^>*kBDkd>xMHH(oi-OYZo$tFThYF_(Ckj{v z0q*Hk&Iu3^?b-Zu1dH=n>dda&Ohh|y7r~u!u52n&zz~VA$@h@wuzz(Jq zug{afQGpJaHSO9Tf4PWzoUMsxXQdu)VO@O=&j!}`9$7XLZhlR6XNS8*FWb=RJuBME zfjOCY4|eS3U?duiwBdu3vPaIt^Pq^r-PevI2a&=kvk|kAW}|`!x?*KwPdet)8Yj=h z$;Z(8%ZoYjl)0sIgkO0U0WeZzcURU`?=JWk0~QYjD7&l|Tk zjaGmN(_=y9vrg6v+C@`orJ+Mdg&&YN{8+OLh36RUI*IQy_yyJE8d;sBCFGMGznYA-WUeZ4@s_%) zBeXW$3@+E@WuTQQ7+N%@8PIuR0L^X3x$hlKZ7#Syc+Mjn@(+6xrCFS6(Ps%gM=6z; z#7ghc3igX?6WE!^Mya><&dqG7oaR4-WJl$mvBR-_*1FWS?6KTq;%^?9d6qH~_&}A2 z4x@P>@phX7*QrRh7KcEIs(idR)}^;!PDf>*hjxiar@hbwt>nX8{a$SAjJWO&m$lQNj9(9P5Zb`a8l zfJs+T#F3xK%YADhU|xOzoEaNsfBu;Bb(=dkS50%;`|hQ@3N%X#4Zz~zUQgj1*nagpCKLUwCGL+*px zA4U^Ku^cyyBX3;Szhs#-Y?pq7U}JPau{KBS8G2+rXnaBMvfWCzs(a8B3CU+USm)Q1 zRHowno0mCvC>&w}2teRpPu2Yt0O5ZWortlO@sE?gHF2_cC-VdIVTzZrLY}%X40MHe z$4X0}A7uxNP5Z5=B%nw;YmL*viQguCQ&8${A6_6kbGar}%d>c04$F*xb22#tgu%}c zW;vE$2z=Fm%WO+_o6jnnJf^bMQjLW2(ZNmsTteUf0uru1G=Vl*sITCZ$iEWy<4*BV z;f^GkLUJ?}H_-BY1}7h-j%m~AYe4~n=9UIqX%a4{A3a6oS(1viWTF_}R1_?JvZQCy zkw%u|#vWu)y5ojMq|M}4onXN>h^JN!UmNR%kT5LVG8Afgn0lm%d2)G51t)9+PB~2s z$MQUa={A%wSVoc`XWR&~S1(X;Z=$U)v2kzFG%|ZsD`;M{EMpA`NbVFJ5|?C@g=RP& zrMS8GcbP-?2n&Mcy-R>aziA+`tE?`01PVq?K>O9UH}^CW1V>gwmYPwl3HOe~42nrq#4q6zh$45@|1XT{NAU@%zuR{3* z2;o-zyKx_zOE8zejA5wszTG@mT;=a+eGcAcsm{8b40;0fh{)^{+(Qyq!u_8th`pW-^09qKWVDo?Mq~aD9KrB`F41Qiy&~h zB$rI|YM|n&_ zuQ4xr-WGYx8>PH!d~ngzZfmW@hJPZyxH^JWlAN4=Q7Iln=&yyXyO-X7t-F!Ot1w8j z3EEz)dr?}iGYjeaxggQ?jhgr-su9b;d_?NZP-hujo*TA(?Ra=@TgD)!(#{ZjWPmuk z*(+zajbgvVU?ShV@dOl^+g-T!H~qB~C`8!GGj)%TS@CvWfun^`V1)ZbA7czDhosu6 zp0IVztORbPQp;Pm#2;G^u~09Kd2+0Kq#V#@1aJ)@;XcLSpS+UL$(N5@k#zTV|81+5 z>y6G92cQQefdIjO7y`iC`yWG~)P9%Klx}S}F0NNf33mM>w94fSvmr=Wo z%x>i}V501f<~`s?xGWx=T0NajE_PV{sb%imWD()yE_V#XwA@yBK2Qhm3`QlFXmod> z+fmAN7(%rJHJn6*Qbi`mVDiWmEkLp(*Df-|6?6x$&tLi#O1XqWIt>|e;^5voYmhPu z*ApfC+>X5jnypi4n{E_l@vr!J!S)VXVDJ-|)kyU)Kcp3D+!l$p`6BvRK&iIXW+0X_ zJnOvT(02&d+8BJQRi3`Y{aa4i#z&zmUV!*70pj1k68(2Z|IAWuFv`zL39!`Ebk2?bZ?L@MzA0f&_>ea-i1<9LdzMS7ZYa4yVD~?u-5dmt?St4Ds^4|5L z9!SBjvlLW!2|P>1Hae^`2+jdGX3_=H&AUngh?(} zVw$IyN*20%-`Rap-P2B0k7h$3htPE3z@FH4U4(Yrbd3rg{#O9yOB&a*$zA7GE(6Z!nT z1wu&bhQjD8S8S7wqm3cF4Tlq;;5bwd$^#O7F-{UkmITS8cM-4X=?N1yx|)hi&nz1; zhn3Z6F{_GTcE;it>$p*DvGU3(KAW%)gvVvTe)XO>Qe~d$Q=lZ}g^{v7x^m_&P3vP} z(|N>&!E4nfhB&)SIk`-Jsy3^Y(;YChX5rP`$WJSkdED%@0u;3|6|FBNgV}7Q+Uxb7 zICuzS>YcQO3{!&avBEG(QC&gUAh)IYQfN{YtJ!-0cLxHeS)vFXpq2<=k?v2A`AIQl zz;XDNdiULjOjnYX2BK4Gdrz#UH0NWn^Op>h`61TPP7H%Pk(pP-B&VI(h~*UXw2U~4OmSqW0O#)dcz+Viu7 zs-sndR1A(Qy<%iJ+T8s$F~m3_X>uhDqYs>{;%OaF>#p988nK^)tKn^}(#Td;SIb+< zz2d{?g~9Lo$lj|Z4lBNe^n|a{@qI5fUd3z|6z7~x^29664I5@cZ+OSsI=w|C_N=EW zrp6{)?-himy#jXq&6C9)a}ZlqU94Ts+_aXBQl}u?`9DBL+}7H{!c5lfT!HT(`aq@d z)VLP4uem{6zwm%=wDgR$bb2rTb_e=5y-?)v6#OnIqYu#b{#@vvv=uXUG&K88*YKEj zV1oi(^Z-POx}bo6St>6$;+Z)fD#xR+UOSLg+ za3RrSi8s{k*3}E#E?5}a3;OcG0|WqdUvG}dr6bLCkf*(Kl&DH z8X?8?hEHK_g*m0sq~Zil(I=#Ai*yxyB#$nn{b%a*^QimNwtm>ZoqC ztyN~f8PMTKmg`I;@65PrULNSJCri?eS4%EnO>n8=5}nRe84TnXcOp(^N{dU{=Nym~ zAx&q@({djUB?l9~-9z2-hDPQr6$gsnn;n`TpPSV}XwDi;nM}RtbjM2%ch+zjqg~qV zd*D3aae(&ga_49YHRLV~epk}+{^~YA3HtBEE&%eB>ZKY?tvht|T` z!T2v>ML3ap=!}ID;^^n-p(1bSkZwS{&YA;E++x$5^LPD<8gb|0w!ILB zg572*z})|O6&+bZTqFR2>xD)41U6jCZr7Pd1sg)>t20x?llYFeI7x_OC-o1hjYhS` zXOl-CMPBmmib)~W+a_YUIHXuN&XJgKb_nA)mX_0Mmp&^**}omKqR=l2Y7h(3p?X*J zdIQ0ptbSTEcvxb>9CMQfy5Ng|^H>!LjU+?D`eRe{3UnyeLalV#&uo6N7f~4G&}UD5 zS}M{|=YsY%?2+Vizp&;i%B{se6&cT(Xi53PZIRB`@ZM&qFy1>6^L6U;3>o2wxKNB0 zMIIIeyN4k-_zb;$M7LG8O;lECCIT*z85j-G+hl%ZxiERKR=-OZerUSBSza>ROhc{~SwAv~+5r{jYN$&O=$x0Qaz4VL%OI#E!NLA&-7 zWuvX5f&@LU~Job$Rv%;IEmG`<&i2MvDJ@QhF6SnOuXGF#)imtNOUKvZ1 z^l8`8!wppAiS#Y2AyFarIOBCs>A>%1a6LbMQ+is#wQaxouxnT${88HO$2_&*0R`NJ zvrrU%uJnh7o|lm=*DtdUpv7)=3*MilP`NK0ByeSIZFM@ll9#^O9YmNcJH3TDO$|?d z8K;Pf&#~}Gqi2m+l_D*E?ZIe%oSLmnbrNiZ6XbT>aCKMBc@OXZd*t8lT0F+FW#*CaWGgw~z11Wo z)V_F5E6kf9JMTDy^ZKSOJHt}B_VRr$j(@62&-4c$VTtx)y8`3Rt}u zJU$9$^#Jc#mX{UeIhEeZ7MQZrC|-;UBvHfQF;K_JNeVPCHm~zZZFxzWMNBOM2OAh1 zipbO(I8_CiZ)KA^m);C+Q}>Sh_7>l4K-_e1@AE_uW5NCuK+W0BaX;{)PM zK~z{Dc-Wb(()1vG%o$#O#F*R{>OxU`0(}gFX(WzUk&XcuC@u?U1{<$dAM`m;q&-H- z|B5nhBLtfv#49+SgdIpXU!AUl0Q~Ai)*xj(U-!wtNjWKY~x+u+1 zoQm>OVpphIBa70A1+G-T`R3d^{BAXmTFn5j0bv5#ks*xtFU#|E6443~VZW$dMk`?K zG;YsUIy`1!ep8``M5&vvFTQ2!1)h$*PMH9EP55G9>Eok5etrES8C3dQ(I@sIA$>eq zH5-H>Do-6;#w&NH`!8#KyT2K~3l2)7-Vt11WO@|7@ zRnc@+B;sWL4Vt(MxM>Lhs*}KJ-#i z?~mX?Kn|b$?a=JsFx zey+Q1r}}Y8^IMPi23obS#IWz$Im5}~AW~x<%7h@mk`XsYi@B3F2ceI1uMB{=Nkyw8_t9dqkfg2+85<|=R49onovpQ? zw8ImV?ylC>eIoe`jFQnpJ~^cHJ0 zdzySr%5x>3_J-ahl*~m~7)>sQ<#83|Q8yQMKx;M<=0q^jSyDw<`p~@rW0f6GX>Kqu z^5Br!S7DL6l|pur!#d|ZE#i;j;*QyFVUEF)kbOPzis40lHJ=!zdxcaZ@`_N~G~`)B zHATE=0K`_2##dXob8 z)NQ)k%}lat=HE!-gG=IwvxMV@3~4Vqh8G$*g*_mDEA_PoV=%6YWKM?`ESosPF-(&n z#s@onNtMA{N3xIDv*7V7O^g90y_YGc6RpixJk!Oqj8&xvMHE(cwu?&EjV2-->dHls z%;MyEQ=dDqW|{L)X;b4rXoRtM8_+&M%8`!E@l1(qsFxgVRGJR|Y*r#Tq`L7LA!*-; z7&OJ2BS^8u$9c4$vEs$*)LFT?fYEs`&8ihXGd`Pi7y=qdc7o=ZWXa(w%5X$YLm}r_ zBXRyGa&I!`lOD8a!V zvr}2a)n$9U4pC$al41_Ubx>XCu~HR2CZP)6z0jx8&|6sKk&`E9j&o4`)Jx>@*rK5` z)Jn~tP+u`#8x>%#}W-I4wNxs({f!GO_ zGH8`EAWyTabH#5zX-Qpr3bN6zJxs9fklPMa0s2VViXTYcWj`#LMV^XqOX7vIg z9UUpfuFpj*n?wt{aXQF=e+F~nc#DnWX%TH62OJy&=LxH?N8-9zLS8;AfzSI`z zex@b8$eiE%Aex;QF-Rk^c+%F}RZegn>vJ)kSP(9kX|E<4iS-Uj>IVMyCO3(-BActn zn_27}nU`O>Y#I1rki?PXsixSP=;>a1?Qr4iO6z=W=*p}Lgj{?=Zr zs<=V9U77_vx%~B+$BVMda-CDB`b$MEFE~o1!`@>qA?^k^J4aa#r|m>JNc-#)})0@OcPXW%ryPEM*Vx~PEqrqrxSK1HF3C=(9 z5!#+{P}O&QqQrU>cR;@Ffnc8%{UnW1#xzN8)EZ}^H_CE%>w`*Aa>ItGuD?wTzxUI* zwl7-KYqLRCT#=*?nv%ZObvje-btH5gb(@&y(uocS$X1#^CR-98pO0OaiJ4MvC!90Z zAtjWdf*l4Aji(GvG^~uYJ4J$&VBQX2s+7cJ8qi!eq;D&=y;2@oS5?i2Bb=^F;mgy^ zzp#DWl{wtAV=hjxin+l~*eavN+G$&28Pt=3bxLnrG#B|dl0vq|J? zVnftzr<)cPnlzPxbB0zQ*TJym2+~+Pl5;W99#BFweeocDed?(V)w5jrS{uO{-Z$r{ zbk~cQQyK586O^sR`_}sq^Old*Pf(xszFql5N>^AwaSS&v^2oOM<;giR?}2J#zQ+9O z!!KA_p%&MdDSOEsNRUyRQhu#bss>_B^1de=;kAK_dT>`SebB_wEqJ$@N8hp^^Z2rd zkDZQvhTirEIbg*st~O)@B5cDkmlcrw_1Bp#L^CTGVro=wH&Ge}6IkudnX7em^b3bc^{dl!q`1PBN+;YsKKD6MR2@`=0TJ%)OaE@q>i0mRAh!0Rw^wQchscK z+I{&_l5L2n%pw%&0G)(Qm)2b4^4U(K+O=lLhg_tU=27e%!|`X>#RNf0+o~kY>JHcp zajX)CRn1>Rr;E@?sGie?MRenY`_5A7FN?a~926AUW4FMffJ`Igq^^d&VBnB~kuvK2 z*!D682?eYbL(C;1T%bPxsn9Scr0QhWY1Sr!U_a;Wg+bo@+YlBs#z)n#U!3BOH3Xk$ zWWSa=5P#I4DPTc^^>DCQ!J*3hF(_}2EQzbw`=GNyOKtVU@Z{2X#Tko_?T5x4?klV$ z5IHtqFrc&(L-zG1@$3S8$C9(A)yu2lDulYsL|=ur>1kdyce95rr(j2WKY6S78RoOZ zGMZ1ysAL>r*Tq=dK;3Z<2tOuo#L=3MyqLV+)w_g&(b&;g9Dc})!Y%W#%m-Hp5t*=i z@`57l=JA2Lm_E2bZ>|h2fqEqP0 z%eEYRTaZPnLkhVAlYYani8d|&V>+QPoKK$ft;2R}`MiZ)pZ7oB6Z+zubp>|!;|bjm zbtD?cU{VYsZ@ju{!+C1vMdQgurEsDGGuYtEE+TFBbUnoU9 z1@pw|VSs!N{DN-LHuHw;DFfl_J)-p~`*?wTsJg|t_5`#Qw>j|0(&uSVsmYalzpdVt z*s^8akptbK6WG&{Z4b0{4we?s3z$R(aq6Ys3yS5`fMeiCYg7xn730Dhzm?#EK>N}~ z={eS#i!yygq82rwp(oz@IpcGN^>e1@{D)0M-nvoz4856HA4lMiHzB|D)O$9h9}|S} z3w-elJAh~jeRE6PPiS(5((l4@L$w~sbi;W_S$Ff5Y+at|hIKvrYDD4Y(b|aO4bPef zmAB6kmQO2L?`>t<)0GR|jO*I%I4ZJBQ{58sZtV0&X-c;I^zUhqYD zgIo!JChg{jx`EBV&5!f$zSwMQUGDdmtXIFG)x4bv^9H}ta^zoIQoP|^<8O1@kK6+7 z_VzvI&uL}6p(%{{|8-`H0xf>yiY-fA zZMD-Yc20`r*#|ujIO%;1De)6Kj{H?A`F~5SpXhOQWCoUN+(04$_5U5_xeKlSn^zaE zWN8cZiRQ(^Oz2U1n5SCa@)e>;RK)-x*L*^u3poXyjQF|cG_6h5qoGRHuVs&9i^qUU zri8|69y;;e;&rKmM*D+Y4jcQP*JnrYFD|Kqvh+xO@sDZ7Y2Z)>^_7#jiQkdNHPO=4 z3Q52{1qv)&@nVn%3N+6Z6EGLEMSMoPmOV=1CUy7-#BW5u$8V12Myuy-Bi#Z7%_q<1 zA-IeA-K2DN_CB#^0I8?D3?@b}8C%myk=XY2SH;-Iwihuh@9j)l|o zJd5u2#pA zO>TeZr>U%lb;f7b9|*YLeH;lEDX>s~crk`vYZ3`PmHg30`BuV`(;1<~7A~tIvycvS zhLItGsASB9M^8Q3M@bk8iEW`xcH~|3XcRh7I=pkiIQjEAqNSPgrXxK+TZiDmOFa5Q zT6zBdjJ!y(c<<}frQ-N>RJwJ1>*~r_6gZG$$&q}2d800?Y%u*tdMF4_s5B`UqPz%1bND}M4cP<~NtGYg%M$PQE##4{0Orq_bMzubS9S>9GyO(AYM zA4vf|ReBR9;|98PHDNSjzS0DTxLBggH*kV%_0x0TaoNHf2mfK9nwChkpbC_x093`Y zX~-4Y`=U~2!RjeQ8Dz_JpY$7CPI79Nm(lMZ)x0NS;w$??GoAbN_Q9|5#R?_k9 zFhHE<|7Uzr*jC@l*un6hAaP#Ix2^2J+d}K{$s*ND7&yiD7mHQ}dJ0r94^yhjyR)od zQ!7*B_)?(>+k#LzD%{Nkg?JvZ+%lewydC%!t*Pr6?P}}F3MCaXr5^W%{Xm6Ao%-(e zb+A%#jtX{XAmb6?rUA9DN`c1VXXZ^Z!gN(Y-1V$;`!vY$$uu{L^I)=_?4R_0jz8^- zJbAe+yDk&lH0nH5LfA7DCdAEIYcq25bmII?fQg~eYuvT|6!?zNkgcbQ!JQW-msvgY zc_|W>^NXysjvYwMHS&B5FX|qZXyc=m;zglBrP}#VXgd0ze1#_f%Q7B`xNa_C&qACj zvMF3vZ8(49Vm;$B-J^?fzA~%sjQn8qS<`Q);IDj~%SV9e-GK0)(_7ie;MxD=F{;f2 zrvcGF;90`Mz@0vCCDKI36~!S))fWrl0|9&gz?U!JvkV>u8Ji@EZrU@)D-ixRq@#AE z!yJ4UxmeP;6o*yE$LWsgrB!FTo0TrluWydfKuq-5p^@wvWBr1-T>Ya?lZOUD^z+J_ ztUuv;XvVs=tV0hnC7Zabwebv$W*o5bZd@kp#!MF*~bOUeNBRYJF2JuGSC7>Zo;aw(DJ0zlBH7;&pz*vKDcG0BE=XNR9Ux3N*;2J z)xl}Rnr-tOMJ@L9*3e<8fMzF_8teR-2Vsqx-rRfUv3Lgy$KlB-Wtn87TK!L&as*ke zV%h_+3<_C?YHSncq89izZH~N=bB@tYPgBt@ zv+2j&WsptS%eIt^z&9oGoaAUQN?jDB%74NkWseq=R0*c$yjT8mTqA~iXPLxvH~MX7 zhXwsD6SO`PlS%-jeGU`sigYBtSct{+<35;y;8p$hs8rNP{$;%O7}7;^k2@0frlfPI z-(tifx41afOd4D9)=W^r$$J&5=p?%Bb>wTRo3^K{Fa=5Ns}MiCsRB;fBCxn(#Ou&O z@>;>cxWTD8r?-{k)0|%+`Og_8S2k9yJ+4sa!lkGnMtxJh)j}*oHh^!|4CHfFI+3LU3{tv_YB{$O4v0?)hLC-qU9-6zu8hMP=I)&K8;5F^KVoM8lj2*WHg7WlY%J25r|Bxa2cbK^?4kyf_FDE)kEhC(k5{rILk%kKZ=G>_c?O43=a?8x-*=1Be6hVF# zFJ*LCqYZlQRyH%oPPG=Xit`9@Tp>6Ze}wM>$^C&;WkaeedYxfVno98KTn>KKMu%sE zR>KUZay5*3hDkIn)6GwuXQbq`WR3vA^T1fN?_F8cWFrneQ z_L!FJR3ln*taIpo^+Y>&2iB8XT5GQoDa>Zqs)putNBl}e1QZ8rfUkeB&ZeTn2Gew7 z4pLmdpL3fayb1S7;i_Ve{4`C{|DEJ~|t-ImlZ(&pfFl5FSU!wI`RtIQ>uRd0tRsy;8a zwXtlh{P7+gl<{Kgq1g-OH3PlGn)G&ymdw>p$$>iv*n?vYBK#<{Y*1`Tlq!_0&}w_! zta^fLqCi$J0rVw*)$kdZx62{jPpDhi;=RKU-RZq~6I(%N*jfaS@RYxB zz%KdD<1P^c$-3sAnTJ5K?h&cx+S{H(qtBjSe>=eD$*Aj zKOIQTU1LNh0GHNsu0KFbnBpT+02L(5^&(ayFX)F?(TXkr4g#yU_sY`t2p-a2f!wy; ziB*a7s*noM)Q0DyeK(84Mi}q&E00?ckjBF*hyn5oMPCfC^%uJ)w`tN~Q#;*9P}+)#1ZFm(Ntf_S~J*o?}qCIXBU}Z$q^h zHIv6~(M?KP%|FX;rGgI6h~4yg6YsZ8Cr0#^NMnjR4uqL@TsA(g+{%hzEXM^3@VPe8 z&7ZWNZ=0+2-^$je6X*&m>+-AVJf|W%dmDqiPj&?H9Ai>cZ=a3LhmCe$5)7jI5T}Q` zj@4%#o)B4<1ec`_As$_x_y#6ZiO2d-Cd3pmL;8qk6;fU1$h7K)yzA)^&W)Gf!A9ff z-!{f+sRG`p0b&6G?5N&f@9Gb&^J8QD-!1VxmAmX4F+qlch?QgP~O8*6-!WBIst{F1Ps# zLv<+4sK)`4Z0UryU(cky z7|1v|~k8^g~|nj_OOsq37#_}|%#m8)85|>8yNt zs$L``g7ue$N^jD%{27+(Y}FE-ykpIvD1tJYvYlYBa=}>~wQena+Zvi_Be7X(tM%&Y z3|OSK?JkZnSuR7;xsQpSp8P`;x4Va@adrw73(t^k!)Lou#vMt$cfX9`(&5i3j#V!n z#+R&xiMz36a_m=z(OZ)}1*c38wQCUk!Xd$VRe$tCU@MEenNJ`7I_Yhrzu+Kj;L$^G z8&w>y-?CAqs&LUX0HVzRKK*|zChtz>{h6!NZU8EJbb^G9#;J#f^w3H;8;3*L$f77~mjiP*!wBaE3`PKT7W=xnN0rMep9ri5T^NqTL6gH&kWZFQ6m!|so| zLJmR{V4K?gQAu`qb2RShrSs;vXYcwunY+@n`OcGH5323ovaJZ^2fX-P^GW%oLfPx! zpxik-A&E&-P1?m!Sz`a#*2CtnE<-$6Ubv)_N@%&V_*v2Mjht=LS=2=F=w4?H`S9Xz zn?b+4w|@N&w^dmBfG-RK-gM^iRCkK}wMXze!%|*tSiC$I4=cZs8+%wzFxU!F=K?Yf zzm_oP;N(1wubYWUNm7rg1`Os$L`^m-=-N{QJN<~zjV1_7hb9|49QF;@iQ?@ikhL*h zm%rsvar-ss^8cfUah&9vd!fh|v&9ADPOI?R5Oe1oROy}4u#B#QQ-eJ$qq-drr z5E}brOg7iuOq|azBkwB8WyidTNTskLw$!T>6dcXzV%9F3+NV7?x~If*I2}njeV$mPG20o?HHGU<7*#booW1S zhyjGnkyjPEv#&lamk29MRHsLL6kN)e($wWzWt%4;<0t6XiM_V5X1y5@>9z9kTEdja z@8WSn)lz|^lPPjxH_i$Z1wY3IiUL(cwZ{ZV*YO}5N@yhsq=3kuwkkQO46z z39;6gyuAtGg~{`1E5a9h>5hqTk_FVp=BB-m#p)xp3vBhYhXS&l(4&e*u6~O_LA=?D z4g%B$HuT?)cm6bt-|{6D^zDHP58st+N&L`V)?~l|kwQEF;Sv_3K(?%QU3n~pjXE|O zlQD4v?v8q3oDYIBSc_lTtAmve=)ZnJvXFs*K+4Ly< z;zRU&&^+=c#{G5duy!#2C&4D3zVNSXbEAFoUz=EsM-&O_-)dK4wuDNsQ(6yVDnBG7 zfr!By8))?<<3|ZQAaxgatM+CnZ1tN!^p}1!_~hxWFJ>8vU_B237AdE$k+D+A*>H#{ z3Xw$n1>-o38c_*n4i%(E7gTXz+C&`oQ5`V|!6oJ9}dX2Xothl5oN!fWn>v$Rcm9!;iq} zA!lq@md7QW8&J^)A-hL=C1i|_+pFpiK^{uUkWiVyI16*AJi$7+sapgYWJE8Aa0m19 z0d0T3EnE;Y$w;i_dUC30ECofw(>rw2`+T*oRjE+>W3p`Vn!j-7x*Cqts{c^O3I5yu z6} zFSaOKt)K$FXYWkZ|KWFwNC5t!Nfoe$#6a)%|5yr$+1pxwm+3Bboo^L8k%+)WAViPw zFuFj6FhqT(iDT@Dv4gc>9!2Md#g1t_{xqyYAAU6aE)u>hEK&CYyy>vxE(@}S7cZY+ zx53#`7?U}pYU122!(sTccz4OW^%kU!j3-2p1j>QQ72D4Zj6kFR;U-&-z$*s6$~5Jt zg-X%p4RN_L6r;(Lm4W>s6y%H^hYkUq9OLwC@R~k~q{4Y&g-jL}MMfB^N|wiFLUMXp zHYMdz9I{1k!fa5?!XtBGKa+mBDM;W@Q;Ee5Yg0%r5L*{zY4yYC*%?t%GRGlTzq00< z6kgU?SWhA7pG?2P6je@%rrRmv8c8a&#>9!|<$4sU?jf&{Nmmk9imS1`Z4zOUlm;V_ z7nU6A%-pwms9X;WM{LoS}G(l-drWVl|4|uB1M--NEE%l*(Gi)O-39c&8jk7TO-CR8%_w9aK$e?*^Z)X6j5evw9223 z995>x_K0ARdN2AS^+Mz{3Tr;3zZT<$XTWlr#5j3xJcqUCq^#7a#mr>DT_!7?SzaDfBC=tV3I8d!u&d-i%48}8l zjo7D*xaC17*pdK#5Q>jDN@TmNPY;Ux3$jBx-Gkb;*rr7SpMzs}Ww52S(|vfJq1_=E zo>1?+TO);wOTXJ+qt9no(eq`&P5lJ}qO#M;;HD%2}aw{>7Pu^n% z`w zE$_>vftayRQ3~BP0-ZojxLgU&L&wD_bMX2G-MxplztUp_B0l4e7G8j7_#X#oepl2b zao?(y0P**liqmoTnPwqJ{WfDXI3-8&+Neh%rQMuy)WS+86yqgj>vz`o9aBloY^bsw zQ`%J@*V5IdUGHVjF5U}id~QxVyRGGyuij1RP(b+XW_&o?Vj*z#qQH2PLe6wz(8m^= z=5F1W;VQ#+0^9$ZVyd$eOdiJboVe^1LbgG7x1z^jPiyUc&JI`Mj9-h9)Ddfic!Kbv zY{4)YDU9_|yIbc?U?BG=&IxVTAr+X3Vr6#+fVBGk6u0$q&I( z=5R%m25llwAkGEz`I_Crsg01GZkXA}v@)zPDPNkj=Rc+E2>j&1t{A6GtDn-B^ubQw zx$3LKZWosS@?<}W;NDw!Uo<>CaxfNG2erYnHfmJ#3b-Wo{xx{~;&6jGBUKqS(A|wj zzwnC1!P9B=C*h^~9>p$$IDC5s43U?azp|z-n_GPX5LGXLJ^x`slx%JPaf`yETIGOR zW8JNwA32db@n+#5Qx)v}13^O3MA;{;h7Au}pDN{~{G8FiV0oRzKNa|4+A3fmPPIKdwER_uG*ji%PaJ8; ztMz?Sblg1J*0ZBVq$9IEwZ3ENos+9ccP4+&>4|?1YuGXv(J1`7W24IM}$* zuLt*COCF`xj3Y6w90iK0lRDCS*$TJU$q}h|s$5GKiWRI3yErYWB7eCk#Clzw2*(LO{XL!Qp_7G8m8% z#Z@8Toqtxg0K(V*T*x4Mf_Hz)imC|GNy>=<+d#iQb{qpr6AaMmZfTDG<0;@X*XM4b z{_`kPJiM76&a+B-GuMTmE8}h_>i~o zZh!~C0118pbOBaKe*&sF0jFl}hYC$zzIFqk-~hJYRRsNU<@zkZ+z0i&dXA_o5S?)l zbu~1$bNuO$|Nh#~Mb|d(3T(ds;E8|W0-w1)uK;QNbCln9P$bNMEZlyFd%$#EB?Uk! z1226il0U9opK&0&`A;}eD`RV575Hyi-unr#nSj*#7=We!gV~0N_rZ$U+5>A=8-PLg z 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 From cd1c80b82d84f8449e3478b08e6bfe21eedf5a9d Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 31 Jul 2013 08:34:52 -0500 Subject: [PATCH 11/34] HHH-8395 - JPA StoredProcedureQuery#getUpdateCount should prefer return -1 rather than throw exceptions --- .../hibernate/loader/custom/CustomLoader.java | 42 ++-- .../hibernate/procedure/ProcedureCall.java | 7 +- .../procedure/ProcedureCallMemento.java | 2 +- .../procedure/internal/ProcedureCallImpl.java | 5 + .../internal/ProcedureResultImpl.java | 34 ++- .../result/NoMoreReturnsException.java | 4 + .../java/org/hibernate/result/Result.java | 12 +- .../hibernate/result/internal/ResultImpl.java | 216 ++++++++++-------- .../sql/storedproc/StoredProcedureTest.java | 55 ++--- .../internal/StoredProcedureQueryImpl.java | 72 ++++-- .../jpa/test/procedure/JpaUsageTest.java | 38 +-- 11 files changed, 298 insertions(+), 189 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java index 77e3f15ebc..cdb424e16e 100755 --- a/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java @@ -613,23 +613,7 @@ public class CustomLoader extends Loader { rowProcessor.columnProcessors[i].performDiscovery( metadata, types, aliases ); } - // lets make sure we did not end up with duplicate aliases. this can occur when the user supplied query - // did not rename same-named columns. e.g.: - // select u.username, u2.username from t_user u, t_user u2 ... - // - // the above will lead to an unworkable situation in most cases (the difference is how the driver/db - // handle this situation. But if the 'aliases' variable contains duplicate names, then we have that - // troublesome condition, so lets throw an error. See HHH-5992 - final HashSet aliasesSet = new HashSet(); - for ( String alias : aliases ) { - boolean alreadyExisted = !aliasesSet.add( alias ); - if ( alreadyExisted ) { - throw new NonUniqueDiscoveredSqlAliasException( - "Encountered a duplicated sql alias [" + alias + - "] during auto-discovery of a native-sql query" - ); - } - } + validateAliases( aliases ); resultTypes = ArrayHelper.toTypeArray( types ); transformerAliases = ArrayHelper.toStringArray( aliases ); @@ -639,6 +623,30 @@ public class CustomLoader extends Loader { } } + private void validateAliases(List aliases) { + // lets make sure we did not end up with duplicate aliases. this can occur when the user supplied query + // did not rename same-named columns. e.g.: + // select u.username, u2.username from t_user u, t_user u2 ... + // + // the above will lead to an unworkable situation in most cases (the difference is how the driver/db + // handle this situation. But if the 'aliases' variable contains duplicate names, then we have that + // troublesome condition, so lets throw an error. See HHH-5992 + final HashSet aliasesSet = new HashSet(); + for ( String alias : aliases ) { + validateAlias( alias ); + boolean alreadyExisted = !aliasesSet.add( alias ); + if ( alreadyExisted ) { + throw new NonUniqueDiscoveredSqlAliasException( + "Encountered a duplicated sql alias [" + alias + + "] during auto-discovery of a native-sql query" + ); + } + } + } + + protected void validateAlias(String alias) { + } + private static class Metadata { private final SessionFactoryImplementor factory; private final ResultSet resultSet; diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java index 62ad4f01ed..9e4460be63 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java @@ -134,11 +134,12 @@ public interface ProcedureCall extends BasicQueryContract, SynchronizeableQuery /** * Retrieves access to outputs of this procedure call. Can be called multiple times, returning the same - * Output instance each time. + * ProcedureResult instance each time. *

- * Note that the procedure will not actually be executed until the outputs are actually accessed. + * If the procedure call has not actually be executed yet, it will be executed and then the ProcedureResult + * will be returned. * - * @return The outputs representation + * @return The ProcedureResult representation */ public ProcedureResult getResult(); diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCallMemento.java b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCallMemento.java index 45d582d8cf..70c63b296e 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCallMemento.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCallMemento.java @@ -29,7 +29,7 @@ import org.hibernate.Session; import org.hibernate.engine.spi.SessionImplementor; /** - * Represents a "memento" of a ProcedureCall + * Represents a "memento" (disconnected, externalizable form) of a ProcedureCall * * @author Steve Ebersole */ diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java index f6235b8aea..b9d3fb6e45 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java @@ -385,6 +385,11 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements // for now assume there are no resultClasses nor mappings defined.. // TOTAL PROOF-OF-CONCEPT!!!!!! + // todo : how to identify calls which should be in the form `{? = call procName...}` ??? (note leading param marker) + // more than likely this will need to be a method on the native API. I can see this as a trigger to + // both: (1) add the `? = ` part and also (2) register a REFCURSOR parameter for DBs (Oracle, PGSQL) that + // need it. + final StringBuilder buffer = new StringBuilder().append( "{call " ) .append( procedureName ) .append( "(" ); diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureResultImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureResultImpl.java index d618e3d6c6..38467892b9 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureResultImpl.java @@ -25,9 +25,7 @@ package org.hibernate.procedure.internal; import java.sql.CallableStatement; import java.sql.ResultSet; -import java.sql.SQLException; -import org.hibernate.JDBCException; import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport; import org.hibernate.procedure.ParameterRegistration; import org.hibernate.procedure.ProcedureResult; @@ -70,19 +68,25 @@ public class ProcedureResultImpl extends ResultImpl implements ProcedureResult { } @Override - protected CurrentReturnDescriptor buildCurrentReturnDescriptor(boolean isResultSet, int updateCount) { - return new ProcedureCurrentReturnDescriptor( isResultSet, updateCount, refCursorParamIndex ); + protected CurrentReturnState buildCurrentReturnDescriptor(boolean isResultSet, int updateCount) { + return new ProcedureCurrentReturnState( isResultSet, updateCount, refCursorParamIndex ); } - protected boolean hasMoreReturns(CurrentReturnDescriptor descriptor) { + protected boolean hasMoreReturns(CurrentReturnState descriptor) { return super.hasMoreReturns( descriptor ) - || ( (ProcedureCurrentReturnDescriptor) descriptor ).refCursorParamIndex < refCursorParameters.length; + || ( (ProcedureCurrentReturnState) descriptor ).refCursorParamIndex < refCursorParameters.length; } @Override - protected Return buildExtendedReturn(CurrentReturnDescriptor returnDescriptor) { + protected boolean hasExtendedReturns(CurrentReturnState currentReturnState) { + return ProcedureCurrentReturnState.class.isInstance( currentReturnState ) + && ( (ProcedureCurrentReturnState) currentReturnState ).refCursorParamIndex < refCursorParameters.length; + } + + @Override + protected Return buildExtendedReturn(CurrentReturnState returnDescriptor) { this.refCursorParamIndex++; - final int refCursorParamIndex = ( (ProcedureCurrentReturnDescriptor) returnDescriptor ).refCursorParamIndex; + final int refCursorParamIndex = ( (ProcedureCurrentReturnState) returnDescriptor ).refCursorParamIndex; final ParameterRegistrationImplementor refCursorParam = refCursorParameters[refCursorParamIndex]; ResultSet resultSet; if ( refCursorParam.getName() != null ) { @@ -95,21 +99,13 @@ public class ProcedureResultImpl extends ResultImpl implements ProcedureResult { .getService( RefCursorSupport.class ) .getResultSet( callableStatement, refCursorParam.getPosition() ); } - return new ResultSetReturn( this, resultSet ); + return new ResultSetReturnImpl( extractResults( resultSet ) ); } - protected JDBCException convert(SQLException e, String message) { - return procedureCall.getSession().getFactory().getSQLExceptionHelper().convert( - e, - message, - procedureCall.getProcedureName() - ); - } - - protected static class ProcedureCurrentReturnDescriptor extends CurrentReturnDescriptor { + protected class ProcedureCurrentReturnState extends CurrentReturnState { private final int refCursorParamIndex; - private ProcedureCurrentReturnDescriptor(boolean isResultSet, int updateCount, int refCursorParamIndex) { + private ProcedureCurrentReturnState(boolean isResultSet, int updateCount, int refCursorParamIndex) { super( isResultSet, updateCount ); this.refCursorParamIndex = refCursorParamIndex; } diff --git a/hibernate-core/src/main/java/org/hibernate/result/NoMoreReturnsException.java b/hibernate-core/src/main/java/org/hibernate/result/NoMoreReturnsException.java index 728a4a3e4e..93dd81ba76 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/NoMoreReturnsException.java +++ b/hibernate-core/src/main/java/org/hibernate/result/NoMoreReturnsException.java @@ -32,4 +32,8 @@ public class NoMoreReturnsException extends HibernateException { public NoMoreReturnsException(String message) { super( message ); } + + public NoMoreReturnsException() { + super( "Results have been exhausted" ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/result/Result.java b/hibernate-core/src/main/java/org/hibernate/result/Result.java index e42eeb8271..40cbf2ba59 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/Result.java +++ b/hibernate-core/src/main/java/org/hibernate/result/Result.java @@ -33,6 +33,13 @@ package org.hibernate.result; * @author Steve Ebersole */ public interface Result { + /** + * Retrieve the current return. + * + * @return The current return. + */ + public Return getCurrentReturn(); + /** * Are there any more returns associated with this result? * @@ -42,9 +49,12 @@ public interface Result { public boolean hasMoreReturns(); /** - * Retrieve the next return. + * Retrieve the next return * * @return The next return. + * + * @throws NoMoreReturnsException Thrown if there are no more returns associated with this Result, as would + * have been indicated by a {@code false} return from {@link #hasMoreReturns()}. */ public Return getNextReturn() throws NoMoreReturnsException; } diff --git a/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java b/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java index 9377303efa..a484d64a1f 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java @@ -40,7 +40,9 @@ import org.hibernate.loader.custom.sql.SQLQueryReturnProcessor; import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.result.NoMoreReturnsException; import org.hibernate.result.Result; +import org.hibernate.result.ResultSetReturn; import org.hibernate.result.Return; +import org.hibernate.result.UpdateCountReturn; import org.hibernate.result.spi.ResultContext; /** @@ -51,102 +53,101 @@ public class ResultImpl implements Result { private final PreparedStatement jdbcStatement; private final CustomLoaderExtension loader; - private CurrentReturnDescriptor currentReturnDescriptor; - - private boolean executed = false; + private CurrentReturnState currentReturnState; public ResultImpl(ResultContext context, PreparedStatement jdbcStatement) { this.context = context; this.jdbcStatement = jdbcStatement; - // For now... + // For now... but see the LoadPlan work; eventually this should just be a ResultSetProcessor. this.loader = buildSpecializedCustomLoader( context ); + + try { + final boolean isResultSet = jdbcStatement.execute(); + currentReturnState = buildCurrentReturnDescriptor( isResultSet ); + } + catch (SQLException e) { + throw convert( e, "Error calling CallableStatement.getMoreResults" ); + } + } + + private CurrentReturnState buildCurrentReturnDescriptor(boolean isResultSet) { + int updateCount = -1; + if ( ! isResultSet ) { + try { + updateCount = jdbcStatement.getUpdateCount(); + } + catch (SQLException e) { + throw convert( e, "Error calling CallableStatement.getUpdateCount" ); + } + } + + return buildCurrentReturnDescriptor( isResultSet, updateCount ); + } + + protected CurrentReturnState buildCurrentReturnDescriptor(boolean isResultSet, int updateCount) { + return new CurrentReturnState( isResultSet, updateCount ); + } + + @Override + public Return getCurrentReturn() { + if ( currentReturnState == null ) { + return null; + } + return currentReturnState.getReturn(); } @Override public boolean hasMoreReturns() { - if ( currentReturnDescriptor == null ) { - final boolean isResultSet; - - if ( executed ) { - try { - isResultSet = jdbcStatement.getMoreResults(); - } - catch (SQLException e) { - throw convert( e, "Error calling CallableStatement.getMoreResults" ); - } - } - else { - try { - isResultSet = jdbcStatement.execute(); - } - catch (SQLException e) { - throw convert( e, "Error calling CallableStatement.execute" ); - } - executed = true; - } - - int updateCount = -1; - if ( ! isResultSet ) { - try { - updateCount = jdbcStatement.getUpdateCount(); - } - catch (SQLException e) { - throw convert( e, "Error calling CallableStatement.getUpdateCount" ); - } - } - - currentReturnDescriptor = buildCurrentReturnDescriptor( isResultSet, updateCount ); + // prepare the next return state + try { + final boolean isResultSet = jdbcStatement.getMoreResults(); + currentReturnState = buildCurrentReturnDescriptor( isResultSet ); + } + catch (SQLException e) { + throw convert( e, "Error calling CallableStatement.getMoreResults" ); } - return hasMoreReturns( currentReturnDescriptor ); + return hasMoreReturns( currentReturnState ); } - protected CurrentReturnDescriptor buildCurrentReturnDescriptor(boolean isResultSet, int updateCount) { - return new CurrentReturnDescriptor( isResultSet, updateCount ); - } - - protected boolean hasMoreReturns(CurrentReturnDescriptor descriptor) { - return descriptor.isResultSet - || descriptor.updateCount >= 0; + protected boolean hasMoreReturns(CurrentReturnState descriptor) { + return descriptor != null && ( descriptor.isResultSet() || descriptor.getUpdateCount() >= 0 ); } @Override public Return getNextReturn() { - if ( currentReturnDescriptor == null ) { - if ( executed ) { - throw new IllegalStateException( "Unexpected condition" ); - } - else { - throw new IllegalStateException( "hasMoreReturns() not called before getNextReturn()" ); - } - } - - if ( ! hasMoreReturns( currentReturnDescriptor ) ) { + if ( !hasMoreReturns() ) { throw new NoMoreReturnsException( "Results have been exhausted" ); } - CurrentReturnDescriptor copyReturnDescriptor = currentReturnDescriptor; - currentReturnDescriptor = null; + return getCurrentReturn(); + } - if ( copyReturnDescriptor.isResultSet ) { - try { - return new ResultSetReturn( this, jdbcStatement.getResultSet() ); - } - catch (SQLException e) { - throw convert( e, "Error calling CallableStatement.getResultSet" ); - } + protected boolean hasExtendedReturns(CurrentReturnState currentReturnState) { + return false; + } + + private List extractCurrentResults() { + try { + return extractResults( jdbcStatement.getResultSet() ); } - else if ( copyReturnDescriptor.updateCount >= 0 ) { - return new UpdateCountReturn( this, copyReturnDescriptor.updateCount ); - } - else { - return buildExtendedReturn( copyReturnDescriptor ); + catch (SQLException e) { + throw convert( e, "Error calling CallableStatement.getResultSet" ); } } - protected Return buildExtendedReturn(CurrentReturnDescriptor copyReturnDescriptor) { - throw new NoMoreReturnsException( "Results have been exhausted" ); + protected List extractResults(ResultSet resultSet) { + try { + return loader.processResultSet( resultSet ); + } + catch (SQLException e) { + throw convert( e, "Error extracting results from CallableStatement" ); + } + } + + protected Return buildExtendedReturn(CurrentReturnState copyReturnDescriptor) { + throw new NoMoreReturnsException(); } protected JDBCException convert(SQLException e, String message) { @@ -157,23 +158,55 @@ public class ResultImpl implements Result { ); } - protected static class CurrentReturnDescriptor { + /** + * Encapsulates the information needed to interpret the current return within a result + */ + protected class CurrentReturnState { private final boolean isResultSet; private final int updateCount; - protected CurrentReturnDescriptor(boolean isResultSet, int updateCount) { + private Return rtn; + + protected CurrentReturnState(boolean isResultSet, int updateCount) { this.isResultSet = isResultSet; this.updateCount = updateCount; } + + public boolean isResultSet() { + return isResultSet; + } + + public int getUpdateCount() { + return updateCount; + } + + public Return getReturn() { + if ( rtn == null ) { + rtn = buildReturn(); + } + return rtn; + } + + protected Return buildReturn() { + if ( isResultSet() ) { + return new ResultSetReturnImpl( extractCurrentResults() ); + } + else if ( getUpdateCount() >= 0 ) { + return new UpdateCountReturnImpl( updateCount ); + } + else if ( hasExtendedReturns( currentReturnState ) ) { + return buildExtendedReturn( currentReturnState ); + } + + throw new NoMoreReturnsException(); + } } - protected static class ResultSetReturn implements org.hibernate.result.ResultSetReturn { - private final ResultImpl storedProcedureOutputs; - private final ResultSet resultSet; + protected static class ResultSetReturnImpl implements ResultSetReturn { + private final List results; - public ResultSetReturn(ResultImpl storedProcedureOutputs, ResultSet resultSet) { - this.storedProcedureOutputs = storedProcedureOutputs; - this.resultSet = resultSet; + public ResultSetReturnImpl(List results) { + this.results = results; } @Override @@ -184,17 +217,12 @@ public class ResultImpl implements Result { @Override @SuppressWarnings("unchecked") public List getResultList() { - try { - return storedProcedureOutputs.loader.processResultSet( resultSet ); - } - catch (SQLException e) { - throw storedProcedureOutputs.convert( e, "Error calling ResultSet.next" ); - } + return results; } @Override public Object getSingleResult() { - List results = getResultList(); + final List results = getResultList(); if ( results == null || results.isEmpty() ) { return null; } @@ -204,12 +232,10 @@ public class ResultImpl implements Result { } } - protected static class UpdateCountReturn implements org.hibernate.result.UpdateCountReturn { - private final ResultImpl result; + protected static class UpdateCountReturnImpl implements UpdateCountReturn { private final int updateCount; - public UpdateCountReturn(ResultImpl result, int updateCount) { - this.result = result; + public UpdateCountReturnImpl(int updateCount) { this.updateCount = updateCount; } @@ -266,6 +292,9 @@ public class ResultImpl implements Result { private QueryParameters queryParameters; private SessionImplementor session; + // temp + private final CustomQuery customQuery; + public CustomLoaderExtension( CustomQuery customQuery, QueryParameters queryParameters, @@ -273,6 +302,8 @@ public class ResultImpl implements Result { super( customQuery, session.getFactory() ); this.queryParameters = queryParameters; this.session = session; + + this.customQuery = customQuery; } // todo : this would be a great way to add locking to stored procedure support (at least where returning entities). @@ -289,5 +320,12 @@ public class ResultImpl implements Result { Collections.emptyList() ); } + + @Override + protected void validateAlias(String alias) { + System.out.println( + "TEMPORARY... discovered result set alias from stored procedure [" + alias + "] : " + customQuery.getSQL() + ); + } } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java index eb664c5223..0b59a59608 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java @@ -44,6 +44,7 @@ import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.ExtraAssertions; +import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -171,11 +172,9 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { ProcedureCall query = session.createStoredProcedureCall( "user"); ProcedureResult procedureResult = query.getResult(); - assertTrue( "Checking ProcedureResult has more returns", procedureResult.hasMoreReturns() ); - Return nextReturn = procedureResult.getNextReturn(); - assertNotNull( nextReturn ); - ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() ); - ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn; + Return currentReturn = procedureResult.getCurrentReturn(); + assertNotNull( currentReturn ); + ResultSetReturn resultSetReturn = assertTyping( ResultSetReturn.class, currentReturn ); String name = (String) resultSetReturn.getSingleResult(); assertEquals( "SA", name ); @@ -190,13 +189,11 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { ProcedureCall query = session.createStoredProcedureCall( "findOneUser" ); ProcedureResult procedureResult = query.getResult(); - assertTrue( "Checking ProcedureResult has more returns", procedureResult.hasMoreReturns() ); - Return nextReturn = procedureResult.getNextReturn(); - assertNotNull( nextReturn ); - ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() ); - ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn; + Return currentReturn = procedureResult.getCurrentReturn(); + assertNotNull( currentReturn ); + ResultSetReturn resultSetReturn = assertTyping( ResultSetReturn.class, currentReturn ); Object result = resultSetReturn.getSingleResult(); - ExtraAssertions.assertTyping( Object[].class, result ); + assertTyping( Object[].class, result ); String name = (String) ( (Object[]) result )[1]; assertEquals( "Steve", name ); @@ -211,16 +208,14 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { ProcedureCall query = session.createStoredProcedureCall( "findUsers" ); ProcedureResult procedureResult = query.getResult(); - assertTrue( "Checking ProcedureResult has more returns", procedureResult.hasMoreReturns() ); - Return nextReturn = procedureResult.getNextReturn(); - assertNotNull( nextReturn ); - ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() ); - ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn; + Return currentReturn = procedureResult.getCurrentReturn(); + assertNotNull( currentReturn ); + ResultSetReturn resultSetReturn = assertTyping( ResultSetReturn.class, currentReturn ); List results = resultSetReturn.getResultList(); assertEquals( 3, results.size() ); for ( Object result : results ) { - ExtraAssertions.assertTyping( Object[].class, result ); + assertTyping( Object[].class, result ); Integer id = (Integer) ( (Object[]) result )[0]; String name = (String) ( (Object[]) result )[1]; if ( id.equals( 1 ) ) { @@ -250,15 +245,13 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { query.registerParameter( "start", Integer.class, ParameterMode.IN ).bindValue( 1 ); query.registerParameter( "end", Integer.class, ParameterMode.IN ).bindValue( 2 ); ProcedureResult procedureResult = query.getResult(); - assertTrue( "Checking ProcedureResult has more returns", procedureResult.hasMoreReturns() ); - Return nextReturn = procedureResult.getNextReturn(); - assertNotNull( nextReturn ); - ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() ); - ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn; + Return currentReturn = procedureResult.getCurrentReturn(); + assertNotNull( currentReturn ); + ResultSetReturn resultSetReturn = assertTyping( ResultSetReturn.class, currentReturn ); List results = resultSetReturn.getResultList(); assertEquals( 1, results.size() ); Object result = results.get( 0 ); - ExtraAssertions.assertTyping( Object[].class, result ); + assertTyping( Object[].class, result ); Integer id = (Integer) ( (Object[]) result )[0]; String name = (String) ( (Object[]) result )[1]; assertEquals( 1, (int) id ); @@ -277,15 +270,13 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { query.registerParameter( 1, Integer.class, ParameterMode.IN ).bindValue( 1 ); query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 ); ProcedureResult procedureResult = query.getResult(); - assertTrue( "Checking ProcedureResult has more returns", procedureResult.hasMoreReturns() ); - Return nextReturn = procedureResult.getNextReturn(); - assertNotNull( nextReturn ); - ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() ); - ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn; + Return currentReturn = procedureResult.getCurrentReturn(); + assertNotNull( currentReturn ); + ResultSetReturn resultSetReturn = assertTyping( ResultSetReturn.class, currentReturn ); List results = resultSetReturn.getResultList(); assertEquals( 1, results.size() ); Object result = results.get( 0 ); - ExtraAssertions.assertTyping( Object[].class, result ); + assertTyping( Object[].class, result ); Integer id = (Integer) ( (Object[]) result )[0]; String name = (String) ( (Object[]) result )[1]; assertEquals( 1, (int) id ); @@ -307,9 +298,8 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { ProcedureCall query = session.createStoredProcedureCall( "findUserRange" ); query.registerParameter( 1, Integer.class, ParameterMode.IN ); query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 ); - ProcedureResult procedureResult = query.getResult(); try { - procedureResult.hasMoreReturns(); + query.getResult(); fail( "Expecting failure due to missing parameter bind" ); } catch (JDBCException expected) { @@ -320,9 +310,8 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { ProcedureCall query = session.createStoredProcedureCall( "findUserRange" ); query.registerParameter( "start", Integer.class, ParameterMode.IN ); query.registerParameter( "end", Integer.class, ParameterMode.IN ).bindValue( 2 ); - ProcedureResult procedureResult = query.getResult(); try { - procedureResult.hasMoreReturns(); + query.getResult(); fail( "Expecting failure due to missing parameter bind" ); } catch (JDBCException expected) { diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/StoredProcedureQueryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/StoredProcedureQueryImpl.java index b232696774..870ee82cbc 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/StoredProcedureQueryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/StoredProcedureQueryImpl.java @@ -25,8 +25,11 @@ package org.hibernate.jpa.internal; import javax.persistence.FlushModeType; import javax.persistence.LockModeType; +import javax.persistence.NoResultException; +import javax.persistence.NonUniqueResultException; import javax.persistence.Parameter; import javax.persistence.ParameterMode; +import javax.persistence.PersistenceException; import javax.persistence.Query; import javax.persistence.StoredProcedureQuery; import javax.persistence.TemporalType; @@ -197,7 +200,8 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro @Override public boolean execute() { - return outputs().hasMoreReturns(); + final Return rtn = outputs().getCurrentReturn(); + return rtn != null && ResultSetReturn.class.isInstance( rtn ); } @Override @@ -212,34 +216,76 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro @Override public int getUpdateCount() { - final Return nextReturn = outputs().getNextReturn(); - if ( nextReturn.isResultSet() ) { + final Return rtn = outputs().getCurrentReturn(); + if ( rtn == null ) { + return -1; + } + else if ( UpdateCountReturn.class.isInstance( rtn ) ) { + return ( (UpdateCountReturn) rtn ).getUpdateCount(); + } + else { return -1; } - return ( (UpdateCountReturn) nextReturn ).getUpdateCount(); } @Override public List getResultList() { - final Return nextReturn = outputs().getNextReturn(); - if ( ! nextReturn.isResultSet() ) { - return null; // todo : what should be thrown/returned here? + final Return rtn = outputs().getCurrentReturn(); + if ( ! ResultSetReturn.class.isInstance( rtn ) ) { + throw new IllegalStateException( "Current CallableStatement was not a ResultSet, but getResultList was called" ); } - return ( (ResultSetReturn) nextReturn ).getResultList(); + + if ( outputs().hasMoreReturns() ) { + outputs().getNextReturn(); + } + + return ( (ResultSetReturn) rtn ).getResultList(); } @Override public Object getSingleResult() { - final Return nextReturn = outputs().getNextReturn(); - if ( ! nextReturn.isResultSet() ) { - return null; // todo : what should be thrown/returned here? + final List resultList = getResultList(); + if ( resultList == null || resultList.isEmpty() ) { + throw new NoResultException( + String.format( + "Call to stored procedure [%s] returned no results", + procedureCall.getProcedureName() + ) + ); } - return ( (ResultSetReturn) nextReturn ).getSingleResult(); + else if ( resultList.size() > 1 ) { + throw new NonUniqueResultException( + String.format( + "Call to stored procedure [%s] returned multiple results", + procedureCall.getProcedureName() + ) + ); + } + + return resultList.get( 0 ); } @Override + @SuppressWarnings("unchecked") public T unwrap(Class cls) { - return null; + if ( ProcedureCall.class.isAssignableFrom( cls ) ) { + return (T) procedureCall; + } + else if ( ProcedureResult.class.isAssignableFrom( cls ) ) { + return (T) outputs(); + } + else if ( BaseQueryImpl.class.isAssignableFrom( cls ) ) { + return (T) this; + } + + throw new PersistenceException( + String.format( + "Unsure how to unwrap %s impl [%s] as requested type [%s]", + StoredProcedureQuery.class.getSimpleName(), + this.getClass().getName(), + cls.getName() + ) + ); } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java index ba9a7cbff5..3aecc24887 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java @@ -44,31 +44,43 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** - * Tests various JPA usage scenarios for performing stored procedures. + * Tests various JPA usage scenarios for performing stored procedures. Inspired by the awesomely well-done JPA TCK * * @author Steve Ebersole */ @RequiresDialect( H2Dialect.class ) -@FailureExpected( jiraKey = "HHH-8389", message = "Waiting clarification from EG" ) public class JpaUsageTest extends BaseEntityManagerFunctionalTestCase { - /** - * Some tests inspired by the awesomely well-done JPA TCK - */ @Test - public void testJpaUsage1() { + public void testMultipleGetUpdateCountCalls() { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); - StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser", User.class ); + StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser" ); // this is what the TCK attempts to do, don't shoot the messenger... query.getUpdateCount(); // yep, twice int updateCount = query.getUpdateCount(); + em.getTransaction().commit(); + em.close(); + } + + @Test + public void testBasicScalarResults() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + + StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser" ); + boolean isResult = query.execute(); + assertTrue( isResult ); + int updateCount = query.getUpdateCount(); + boolean results = false; do { List list = query.getResultList(); + assertEquals( 1, list.size() ); + results = query.hasMoreResults(); // and it only sets the updateCount once lol } while ( results || updateCount != -1); @@ -78,15 +90,15 @@ public class JpaUsageTest extends BaseEntityManagerFunctionalTestCase { } @Test - public void testJpaUsage2() { + @FailureExpected( jiraKey = "HHH-8398" ) + public void testResultClassHandling() { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser", User.class ); boolean isResult = query.execute(); assertTrue( isResult ); -// int updateCount = query.getUpdateCount(); - int updateCount = -1; + int updateCount = query.getUpdateCount(); boolean results = false; do { @@ -122,7 +134,7 @@ public class JpaUsageTest extends BaseEntityManagerFunctionalTestCase { " return rs;\n" + "}\n" + "$$"; - public static final String DROP_CMD = "DROP ALIAS findUser IF EXISTS"; + public static final String DROP_CMD = "DROP ALIAS findOneUser IF EXISTS"; @Override protected void afterEntityManagerFactoryBuilt() { @@ -130,6 +142,7 @@ public class JpaUsageTest extends BaseEntityManagerFunctionalTestCase { } private void execute(String sql) { + System.out.println( "Executing SQL : " + sql ); final SessionFactoryImplementor sf = entityManagerFactory().unwrap( SessionFactoryImplementor.class ); final Connection conn; try { @@ -153,14 +166,13 @@ public class JpaUsageTest extends BaseEntityManagerFunctionalTestCase { } } catch (SQLException e) { - throw new RuntimeException( "Unable to execute SQL : " + sql ); + throw new RuntimeException( "Unable to execute SQL : " + sql, e ); } } @Override public void releaseResources() { execute( DROP_CMD ); - super.releaseResources(); } } From 0a7725432eab6bf388e58c9ae14c57284e22726e Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 31 Jul 2013 12:10:05 -0400 Subject: [PATCH 12/34] HHH-8399 Upgrade to Javassist 3.18.1-Beta1 --- libraries.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries.gradle b/libraries.gradle index 1919c0de3d..63e3423f59 100644 --- a/libraries.gradle +++ b/libraries.gradle @@ -51,7 +51,7 @@ ext { dom4j: 'dom4j:dom4j:1.6.1@jar', // Javassist - javassist: 'org.javassist:javassist:3.18.0-GA', + javassist: 'org.javassist:javassist:3.18.1-Beta1', // javax jpa: 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final', From d210c0cae2d2f2fddd788cc9079fa4fdac8d3366 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 31 Jul 2013 12:26:29 -0400 Subject: [PATCH 13/34] HHH-8389 removed a @FailureExpected --- .../test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java index 3aecc24887..b7ba4ddf2d 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java @@ -90,7 +90,6 @@ public class JpaUsageTest extends BaseEntityManagerFunctionalTestCase { } @Test - @FailureExpected( jiraKey = "HHH-8398" ) public void testResultClassHandling() { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); From 15e9b028b5dc12d45cf067a027e04df719032fd3 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 31 Jul 2013 11:16:40 -0500 Subject: [PATCH 14/34] HHH-8398 - StoredProcedureQuery passed resultClasses chooses incorrect aliases to extract results --- .../hibernate/result/internal/ResultImpl.java | 20 ++++++++----------- ...JpaUsageTest.java => JpaTckUsageTest.java} | 4 ++-- 2 files changed, 10 insertions(+), 14 deletions(-) rename hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/{JpaUsageTest.java => JpaTckUsageTest.java} (97%) diff --git a/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java b/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java index a484d64a1f..2cef2b060c 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java @@ -251,6 +251,8 @@ public class ResultImpl implements Result { } private static CustomLoaderExtension buildSpecializedCustomLoader(final ResultContext context) { + // might be better to just manually construct the Return(s).. SQLQueryReturnProcessor does a lot of + // work that is really unnecessary here. final SQLQueryReturnProcessor processor = new SQLQueryReturnProcessor( context.getQueryReturns(), context.getSession().getFactory() @@ -292,8 +294,7 @@ public class ResultImpl implements Result { private QueryParameters queryParameters; private SessionImplementor session; - // temp - private final CustomQuery customQuery; + private boolean needsDiscovery = true; public CustomLoaderExtension( CustomQuery customQuery, @@ -302,14 +303,16 @@ public class ResultImpl implements Result { super( customQuery, session.getFactory() ); this.queryParameters = queryParameters; this.session = session; - - this.customQuery = customQuery; } // todo : this would be a great way to add locking to stored procedure support (at least where returning entities). public List processResultSet(ResultSet resultSet) throws SQLException { - super.autoDiscoverTypes( resultSet ); + if ( needsDiscovery ) { + super.autoDiscoverTypes( resultSet ); + // todo : EntityAliases discovery + needsDiscovery = false; + } return super.processResultSet( resultSet, queryParameters, @@ -320,12 +323,5 @@ public class ResultImpl implements Result { Collections.emptyList() ); } - - @Override - protected void validateAlias(String alias) { - System.out.println( - "TEMPORARY... discovered result set alias from stored procedure [" + alias + "] : " + customQuery.getSQL() - ); - } } } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaTckUsageTest.java similarity index 97% rename from hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java rename to hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaTckUsageTest.java index b7ba4ddf2d..355c4d6705 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaUsageTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaTckUsageTest.java @@ -49,7 +49,7 @@ import static org.junit.Assert.assertTrue; * @author Steve Ebersole */ @RequiresDialect( H2Dialect.class ) -public class JpaUsageTest extends BaseEntityManagerFunctionalTestCase { +public class JpaTckUsageTest extends BaseEntityManagerFunctionalTestCase { @Test public void testMultipleGetUpdateCountCalls() { @@ -60,7 +60,7 @@ public class JpaUsageTest extends BaseEntityManagerFunctionalTestCase { // this is what the TCK attempts to do, don't shoot the messenger... query.getUpdateCount(); // yep, twice - int updateCount = query.getUpdateCount(); + query.getUpdateCount(); em.getTransaction().commit(); em.close(); From 3102edba8b201c6136bed246d46b5e5b8ece9239 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 31 Jul 2013 14:41:45 -0400 Subject: [PATCH 15/34] HHH-8399 corrected javassist in hibernate-osgi test --- .../quickstart/tutorials/osgi/managed-jpa/features.xml | 2 +- .../quickstart/tutorials/osgi/unmanaged-jpa/features.xml | 2 +- .../quickstart/tutorials/osgi/unmanaged-native/features.xml | 2 +- hibernate-osgi/hibernate-osgi.gradle | 2 +- hibernate-osgi/src/test/resources/bnd/javassist.bnd | 6 ------ .../src/test/resources/felix-framework.properties | 2 +- 6 files changed, 5 insertions(+), 11 deletions(-) delete mode 100644 hibernate-osgi/src/test/resources/bnd/javassist.bnd diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml index 9dec7c4c11..08c0a756e1 100755 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/managed-jpa/features.xml @@ -46,7 +46,6 @@ mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/2.7.7_5 - mvn:org.jboss.javassist/com.springsource.javassist/3.15.0.GA mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr303-api-1.0.0/2.2.0 mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.8.2_2 mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.dom4j/1.6.1_5 @@ -57,6 +56,7 @@ wrap:mvn:org.hibernate.common/hibernate-commons-annotations/4.0.2.Final mvn:com.fasterxml/classmate/0.5.4 mvn:org.jboss.logging/jboss-logging/3.1.0.GA + mvn:org.javassist/javassist/3.18.0-GA mvn:org.hibernate/hibernate-core/4.3.0-SNAPSHOT mvn:org.hibernate/hibernate-entitymanager/4.3.0-SNAPSHOT diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/features.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/features.xml index 27d67036be..42b695ac5e 100644 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/features.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-jpa/features.xml @@ -21,7 +21,6 @@ mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/2.7.7_5 - mvn:org.jboss.javassist/com.springsource.javassist/3.15.0.GA mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr303-api-1.0.0/2.2.0 mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.8.2_2 mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.dom4j/1.6.1_5 @@ -32,6 +31,7 @@ mvn:com.fasterxml/classmate/0.5.4 mvn:org.jboss.logging/jboss-logging/3.1.0.GA + mvn:org.javassist/javassist/3.18.0-GA mvn:org.hibernate/hibernate-core/4.3.0-SNAPSHOT mvn:org.hibernate/hibernate-entitymanager/4.3.0-SNAPSHOT diff --git a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/features.xml b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/features.xml index b8efe8e742..abc9ca26b3 100644 --- a/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/features.xml +++ b/documentation/src/main/docbook/quickstart/tutorials/osgi/unmanaged-native/features.xml @@ -21,7 +21,6 @@ mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/2.7.7_5 - mvn:org.jboss.javassist/com.springsource.javassist/3.15.0.GA mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr303-api-1.0.0/2.2.0 mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.8.2_2 mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.dom4j/1.6.1_5 @@ -38,6 +37,7 @@ mvn:com.fasterxml/classmate/0.5.4 mvn:org.jboss.logging/jboss-logging/3.1.0.GA + mvn:org.javassist/javassist/3.18.0-GA based on auditStrategy (see above) @@ -108,7 +107,7 @@ public final class OneAuditEntityQueryGenerator extends AbstractRelationQueryGen globalCfg, qb, rootParameters, revisionPropertyPath, verEntCfg.getRevisionEndFieldName(), true, referencedIdData, revisionPropertyPath, verEntCfg.getOriginalIdPropName(), REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR, - inclusive + true ); // e.revision_type != DEL rootParameters.addWhereWithNamedParam( getRevisionTypePath(), false, "!=", DEL_REVISION_TYPE_PARAMETER ); @@ -126,7 +125,7 @@ public final class OneAuditEntityQueryGenerator extends AbstractRelationQueryGen // Restrictions to match all rows deleted at exactly given revision. final Parameters removed = disjoint.addSubParameters( "and" ); // Excluding current revision, because we need to match data valid at the previous one. - createValidDataRestrictions( globalCfg, auditStrategy, referencedIdData, remQb, valid, false ); + createValidDataRestrictions( globalCfg, auditStrategy, referencedIdData, remQb, valid ); // e.revision = :revision removed.addWhereWithNamedParam( verEntCfg.getRevisionNumberPath(), false, "=", REVISION_PARAMETER ); // e.revision_type = DEL diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/ThreeEntityQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/ThreeEntityQueryGenerator.java index 5d374b8f54..5477eaf9e7 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/ThreeEntityQueryGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/ThreeEntityQueryGenerator.java @@ -180,7 +180,7 @@ public final class ThreeEntityQueryGenerator extends AbstractRelationQueryGenera originalIdPropertyName, REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR, - inclusive + true ); // (selecting f entities at revision :revision) // --> based on auditStrategy (see above) @@ -188,15 +188,15 @@ public final class ThreeEntityQueryGenerator extends AbstractRelationQueryGenera globalCfg, qb, rootParameters, - REFERENCED_ENTITY_ALIAS + "." + revisionPropertyPath, - REFERENCED_ENTITY_ALIAS + "." + verEntCfg.getRevisionEndFieldName(), + INDEX_ENTITY_ALIAS + "." + revisionPropertyPath, + INDEX_ENTITY_ALIAS + "." + verEntCfg.getRevisionEndFieldName(), false, referencedIdData, revisionPropertyPath, originalIdPropertyName, INDEX_ENTITY_ALIAS, INDEX_ENTITY_ALIAS_DEF_AUD_STR, - inclusive + true ); // (with ee association at revision :revision) // --> based on auditStrategy (see above) diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/TwoEntityQueryGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/TwoEntityQueryGenerator.java index 21ea05cb48..00d0d9fdf1 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/TwoEntityQueryGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/entities/mapper/relation/query/TwoEntityQueryGenerator.java @@ -152,7 +152,7 @@ public final class TwoEntityQueryGenerator extends AbstractRelationQueryGenerato originalIdPropertyName, REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR, - inclusive + true ); // (with ee association at revision :revision) // --> based on auditStrategy (see above) diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/onetomany/CollectionRefIngEntity.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/onetomany/CollectionRefIngEntity.java index 67992c838b..64183b292b 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/onetomany/CollectionRefIngEntity.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/entities/onetomany/CollectionRefIngEntity.java @@ -58,9 +58,9 @@ public class CollectionRefIngEntity implements Serializable { this.reference = reference; } - public CollectionRefIngEntity(String data, CollectionRefEdEntity reference) { + public CollectionRefIngEntity(Integer id, String data) { + this.id = id; this.data = data; - this.reference = reference; } public Integer getId() { diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/joined/notownedrelation/NotOwnedBidirectional.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/joined/notownedrelation/NotOwnedBidirectional.java index 3d0ef4f614..8374ee60d1 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/joined/notownedrelation/NotOwnedBidirectional.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/joined/notownedrelation/NotOwnedBidirectional.java @@ -106,7 +106,6 @@ public class NotOwnedBidirectional extends BaseEnversJPAFunctionalTestCase { @Test public void testHistoryOfPersonalContact() { - System.out.println( getAuditReader().find( PersonalContact.class, pc_id, 1 ).getAddresses() ); assert getAuditReader().find( PersonalContact.class, pc_id, 1 ).getAddresses().equals( TestTools.makeSet( new Address( a1_id, "a1" ) ) ); diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/notownedrelation/NotOwnedBidirectional.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/notownedrelation/NotOwnedBidirectional.java index 5596d82019..77975cc337 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/notownedrelation/NotOwnedBidirectional.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/notownedrelation/NotOwnedBidirectional.java @@ -106,7 +106,6 @@ public class NotOwnedBidirectional extends BaseEnversJPAFunctionalTestCase { @Test public void testHistoryOfPersonalContact() { - System.out.println( getAuditReader().find( PersonalContact.class, pc_id, 1 ).getAddresses() ); assert getAuditReader().find( PersonalContact.class, pc_id, 1 ).getAddresses().equals( TestTools.makeSet( new Address( a1_id, "a1" ) ) ); diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/BasicList.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/BasicList.java index 31818bebac..37ed99a61b 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/BasicList.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/BasicList.java @@ -156,10 +156,10 @@ public class BasicList extends BaseEnversJPAFunctionalTestCase { ListOwnedEntity rev5 = getAuditReader().find( ListOwnedEntity.class, ed1_id, 5 ); assert rev1.getReferencing().equals( Collections.EMPTY_LIST ); - assert TestTools.checkList( rev2.getReferencing(), ing1, ing2 ); - assert TestTools.checkList( rev3.getReferencing(), ing1, ing2 ); - assert TestTools.checkList( rev4.getReferencing(), ing2 ); - assert TestTools.checkList( rev5.getReferencing(), ing2 ); + assert TestTools.checkCollection( rev2.getReferencing(), ing1, ing2 ); + assert TestTools.checkCollection( rev3.getReferencing(), ing1, ing2 ); + assert TestTools.checkCollection( rev4.getReferencing(), ing2 ); + assert TestTools.checkCollection( rev5.getReferencing(), ing2 ); } @Test @@ -174,10 +174,10 @@ public class BasicList extends BaseEnversJPAFunctionalTestCase { ListOwnedEntity rev5 = getAuditReader().find( ListOwnedEntity.class, ed2_id, 5 ); assert rev1.getReferencing().equals( Collections.EMPTY_LIST ); - assert TestTools.checkList( rev2.getReferencing(), ing2 ); - assert TestTools.checkList( rev3.getReferencing(), ing1, ing2 ); - assert TestTools.checkList( rev4.getReferencing(), ing1, ing2 ); - assert TestTools.checkList( rev5.getReferencing(), ing2 ); + assert TestTools.checkCollection( rev2.getReferencing(), ing2 ); + assert TestTools.checkCollection( rev3.getReferencing(), ing1, ing2 ); + assert TestTools.checkCollection( rev4.getReferencing(), ing1, ing2 ); + assert TestTools.checkCollection( rev5.getReferencing(), ing2 ); } @Test @@ -192,9 +192,9 @@ public class BasicList extends BaseEnversJPAFunctionalTestCase { ListOwningEntity rev5 = getAuditReader().find( ListOwningEntity.class, ing1_id, 5 ); assert rev1.getReferences().equals( Collections.EMPTY_LIST ); - assert TestTools.checkList( rev2.getReferences(), ed1 ); - assert TestTools.checkList( rev3.getReferences(), ed1, ed2 ); - assert TestTools.checkList( rev4.getReferences(), ed2 ); + assert TestTools.checkCollection( rev2.getReferences(), ed1 ); + assert TestTools.checkCollection( rev3.getReferences(), ed1, ed2 ); + assert TestTools.checkCollection( rev4.getReferences(), ed2 ); assert rev5.getReferences().equals( Collections.EMPTY_LIST ); } @@ -210,9 +210,9 @@ public class BasicList extends BaseEnversJPAFunctionalTestCase { ListOwningEntity rev5 = getAuditReader().find( ListOwningEntity.class, ing2_id, 5 ); assert rev1.getReferences().equals( Collections.EMPTY_LIST ); - assert TestTools.checkList( rev2.getReferences(), ed1, ed2 ); - assert TestTools.checkList( rev3.getReferences(), ed1, ed2 ); - assert TestTools.checkList( rev4.getReferences(), ed1, ed2 ); - assert TestTools.checkList( rev5.getReferences(), ed1, ed2 ); + assert TestTools.checkCollection( rev2.getReferences(), ed1, ed2 ); + assert TestTools.checkCollection( rev3.getReferences(), ed1, ed2 ); + assert TestTools.checkCollection( rev4.getReferences(), ed1, ed2 ); + assert TestTools.checkCollection( rev5.getReferences(), ed1, ed2 ); } } \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/BasicWhereJoinTable.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/BasicWhereJoinTable.java index ca67bbb217..ec1936156d 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/BasicWhereJoinTable.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/BasicWhereJoinTable.java @@ -150,16 +150,16 @@ public class BasicWhereJoinTable extends BaseEnversJPAFunctionalTestCase { WhereJoinTableEntity rev4 = getAuditReader().find( WhereJoinTableEntity.class, wjte1_id, 4 ); // Checking 1st list - assert TestTools.checkList( rev1.getReferences1() ); - assert TestTools.checkList( rev2.getReferences1(), ite1_1 ); - assert TestTools.checkList( rev3.getReferences1(), ite1_1 ); - assert TestTools.checkList( rev4.getReferences1() ); + assert TestTools.checkCollection( rev1.getReferences1() ); + assert TestTools.checkCollection( rev2.getReferences1(), ite1_1 ); + assert TestTools.checkCollection( rev3.getReferences1(), ite1_1 ); + assert TestTools.checkCollection( rev4.getReferences1() ); // Checking 2nd list - assert TestTools.checkList( rev1.getReferences2() ); - assert TestTools.checkList( rev2.getReferences2(), ite2_1 ); - assert TestTools.checkList( rev3.getReferences2(), ite2_1 ); - assert TestTools.checkList( rev4.getReferences2(), ite2_1 ); + assert TestTools.checkCollection( rev1.getReferences2() ); + assert TestTools.checkCollection( rev2.getReferences2(), ite2_1 ); + assert TestTools.checkCollection( rev3.getReferences2(), ite2_1 ); + assert TestTools.checkCollection( rev4.getReferences2(), ite2_1 ); } @Test @@ -174,15 +174,15 @@ public class BasicWhereJoinTable extends BaseEnversJPAFunctionalTestCase { WhereJoinTableEntity rev4 = getAuditReader().find( WhereJoinTableEntity.class, wjte2_id, 4 ); // Checking 1st list - assert TestTools.checkList( rev1.getReferences1() ); - assert TestTools.checkList( rev2.getReferences1() ); - assert TestTools.checkList( rev3.getReferences1(), ite1_1, ite1_2 ); - assert TestTools.checkList( rev4.getReferences1(), ite1_1, ite1_2 ); + assert TestTools.checkCollection( rev1.getReferences1() ); + assert TestTools.checkCollection( rev2.getReferences1() ); + assert TestTools.checkCollection( rev3.getReferences1(), ite1_1, ite1_2 ); + assert TestTools.checkCollection( rev4.getReferences1(), ite1_1, ite1_2 ); // Checking 2nd list - assert TestTools.checkList( rev1.getReferences2() ); - assert TestTools.checkList( rev2.getReferences2() ); - assert TestTools.checkList( rev3.getReferences2() ); - assert TestTools.checkList( rev4.getReferences2(), ite2_2 ); + assert TestTools.checkCollection( rev1.getReferences2() ); + assert TestTools.checkCollection( rev2.getReferences2() ); + assert TestTools.checkCollection( rev3.getReferences2() ); + assert TestTools.checkCollection( rev4.getReferences2(), ite2_2 ); } } \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/biowned/BasicBiowned.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/biowned/BasicBiowned.java index b9af0e5b08..1b964a042d 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/biowned/BasicBiowned.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/biowned/BasicBiowned.java @@ -163,11 +163,11 @@ public class BasicBiowned extends BaseEnversJPAFunctionalTestCase { ListBiowning1Entity rev4 = getAuditReader().find( ListBiowning1Entity.class, o1_1_id, 4 ); ListBiowning1Entity rev5 = getAuditReader().find( ListBiowning1Entity.class, o1_1_id, 5 ); - assert TestTools.checkList( rev1.getReferences() ); - assert TestTools.checkList( rev2.getReferences(), o2_1 ); - assert TestTools.checkList( rev3.getReferences(), o2_1, o2_2 ); - assert TestTools.checkList( rev4.getReferences() ); - assert TestTools.checkList( rev5.getReferences(), o2_2 ); + assert TestTools.checkCollection( rev1.getReferences() ); + assert TestTools.checkCollection( rev2.getReferences(), o2_1 ); + assert TestTools.checkCollection( rev3.getReferences(), o2_1, o2_2 ); + assert TestTools.checkCollection( rev4.getReferences() ); + assert TestTools.checkCollection( rev5.getReferences(), o2_2 ); } @Test @@ -181,12 +181,11 @@ public class BasicBiowned extends BaseEnversJPAFunctionalTestCase { ListBiowning1Entity rev4 = getAuditReader().find( ListBiowning1Entity.class, o1_2_id, 4 ); ListBiowning1Entity rev5 = getAuditReader().find( ListBiowning1Entity.class, o1_2_id, 5 ); - assert TestTools.checkList( rev1.getReferences() ); - assert TestTools.checkList( rev2.getReferences(), o2_2 ); - assert TestTools.checkList( rev3.getReferences(), o2_2 ); - assert TestTools.checkList( rev4.getReferences(), o2_1, o2_2 ); - System.out.println( "rev5.getReferences() = " + rev5.getReferences() ); - assert TestTools.checkList( rev5.getReferences(), o2_2 ); + assert TestTools.checkCollection( rev1.getReferences() ); + assert TestTools.checkCollection( rev2.getReferences(), o2_2 ); + assert TestTools.checkCollection( rev3.getReferences(), o2_2 ); + assert TestTools.checkCollection( rev4.getReferences(), o2_1, o2_2 ); + assert TestTools.checkCollection( rev5.getReferences(), o2_2 ); } @Test @@ -200,11 +199,11 @@ public class BasicBiowned extends BaseEnversJPAFunctionalTestCase { ListBiowning2Entity rev4 = getAuditReader().find( ListBiowning2Entity.class, o2_1_id, 4 ); ListBiowning2Entity rev5 = getAuditReader().find( ListBiowning2Entity.class, o2_1_id, 5 ); - assert TestTools.checkList( rev1.getReferences() ); - assert TestTools.checkList( rev2.getReferences(), o1_1 ); - assert TestTools.checkList( rev3.getReferences(), o1_1 ); - assert TestTools.checkList( rev4.getReferences(), o1_2 ); - assert TestTools.checkList( rev5.getReferences() ); + assert TestTools.checkCollection( rev1.getReferences() ); + assert TestTools.checkCollection( rev2.getReferences(), o1_1 ); + assert TestTools.checkCollection( rev3.getReferences(), o1_1 ); + assert TestTools.checkCollection( rev4.getReferences(), o1_2 ); + assert TestTools.checkCollection( rev5.getReferences() ); } @Test @@ -218,10 +217,10 @@ public class BasicBiowned extends BaseEnversJPAFunctionalTestCase { ListBiowning2Entity rev4 = getAuditReader().find( ListBiowning2Entity.class, o2_2_id, 4 ); ListBiowning2Entity rev5 = getAuditReader().find( ListBiowning2Entity.class, o2_2_id, 5 ); - assert TestTools.checkList( rev1.getReferences() ); - assert TestTools.checkList( rev2.getReferences(), o1_2 ); - assert TestTools.checkList( rev3.getReferences(), o1_1, o1_2 ); - assert TestTools.checkList( rev4.getReferences(), o1_2 ); - assert TestTools.checkList( rev5.getReferences(), o1_1, o1_2 ); + assert TestTools.checkCollection( rev1.getReferences() ); + assert TestTools.checkCollection( rev2.getReferences(), o1_2 ); + assert TestTools.checkCollection( rev3.getReferences(), o1_1, o1_2 ); + assert TestTools.checkCollection( rev4.getReferences(), o1_2 ); + assert TestTools.checkCollection( rev5.getReferences(), o1_1, o1_2 ); } } \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/sametable/BasicSametable.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/sametable/BasicSametable.java index d33b29fc0b..b4b3e1483a 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/sametable/BasicSametable.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/sametable/BasicSametable.java @@ -199,17 +199,17 @@ public class BasicSametable extends BaseEnversJPAFunctionalTestCase { ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p1_id, 4 ); ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p1_id, 5 ); - assert TestTools.checkList( rev1.getChildren1() ); - assert TestTools.checkList( rev2.getChildren1(), c1_1 ); - assert TestTools.checkList( rev3.getChildren1(), c1_1, c1_2 ); - assert TestTools.checkList( rev4.getChildren1(), c1_2 ); - assert TestTools.checkList( rev5.getChildren1() ); + assert TestTools.checkCollection( rev1.getChildren1() ); + assert TestTools.checkCollection( rev2.getChildren1(), c1_1 ); + assert TestTools.checkCollection( rev3.getChildren1(), c1_1, c1_2 ); + assert TestTools.checkCollection( rev4.getChildren1(), c1_2 ); + assert TestTools.checkCollection( rev5.getChildren1() ); - assert TestTools.checkList( rev1.getChildren2() ); - assert TestTools.checkList( rev2.getChildren2() ); - assert TestTools.checkList( rev3.getChildren2(), c2_2 ); - assert TestTools.checkList( rev4.getChildren2(), c2_2 ); - assert TestTools.checkList( rev5.getChildren2(), c2_2 ); + assert TestTools.checkCollection( rev1.getChildren2() ); + assert TestTools.checkCollection( rev2.getChildren2() ); + assert TestTools.checkCollection( rev3.getChildren2(), c2_2 ); + assert TestTools.checkCollection( rev4.getChildren2(), c2_2 ); + assert TestTools.checkCollection( rev5.getChildren2(), c2_2 ); } @Test @@ -224,17 +224,17 @@ public class BasicSametable extends BaseEnversJPAFunctionalTestCase { ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p2_id, 4 ); ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p2_id, 5 ); - assert TestTools.checkList( rev1.getChildren1() ); - assert TestTools.checkList( rev2.getChildren1() ); - assert TestTools.checkList( rev3.getChildren1(), c1_1 ); - assert TestTools.checkList( rev4.getChildren1(), c1_1 ); - assert TestTools.checkList( rev5.getChildren1(), c1_1 ); + assert TestTools.checkCollection( rev1.getChildren1() ); + assert TestTools.checkCollection( rev2.getChildren1() ); + assert TestTools.checkCollection( rev3.getChildren1(), c1_1 ); + assert TestTools.checkCollection( rev4.getChildren1(), c1_1 ); + assert TestTools.checkCollection( rev5.getChildren1(), c1_1 ); - assert TestTools.checkList( rev1.getChildren2() ); - assert TestTools.checkList( rev2.getChildren2(), c2_1 ); - assert TestTools.checkList( rev3.getChildren2(), c2_1 ); - assert TestTools.checkList( rev4.getChildren2(), c2_1, c2_2 ); - assert TestTools.checkList( rev5.getChildren2(), c2_1 ); + assert TestTools.checkCollection( rev1.getChildren2() ); + assert TestTools.checkCollection( rev2.getChildren2(), c2_1 ); + assert TestTools.checkCollection( rev3.getChildren2(), c2_1 ); + assert TestTools.checkCollection( rev4.getChildren2(), c2_1, c2_2 ); + assert TestTools.checkCollection( rev5.getChildren2(), c2_1 ); } @Test @@ -248,11 +248,11 @@ public class BasicSametable extends BaseEnversJPAFunctionalTestCase { Child1Entity rev4 = getAuditReader().find( Child1Entity.class, c1_1_id, 4 ); Child1Entity rev5 = getAuditReader().find( Child1Entity.class, c1_1_id, 5 ); - assert TestTools.checkList( rev1.getParents() ); - assert TestTools.checkList( rev2.getParents(), p1 ); - assert TestTools.checkList( rev3.getParents(), p1, p2 ); - assert TestTools.checkList( rev4.getParents(), p2 ); - assert TestTools.checkList( rev5.getParents(), p2 ); + assert TestTools.checkCollection( rev1.getParents() ); + assert TestTools.checkCollection( rev2.getParents(), p1 ); + assert TestTools.checkCollection( rev3.getParents(), p1, p2 ); + assert TestTools.checkCollection( rev4.getParents(), p2 ); + assert TestTools.checkCollection( rev5.getParents(), p2 ); } // TODO: was disabled? @@ -266,11 +266,11 @@ public class BasicSametable extends BaseEnversJPAFunctionalTestCase { Child1Entity rev4 = getAuditReader().find( Child1Entity.class, c1_2_id, 4 ); Child1Entity rev5 = getAuditReader().find( Child1Entity.class, c1_2_id, 5 ); - assert TestTools.checkList( rev1.getParents() ); - assert TestTools.checkList( rev2.getParents() ); - assert TestTools.checkList( rev3.getParents(), p1 ); - assert TestTools.checkList( rev4.getParents(), p1 ); - assert TestTools.checkList( rev5.getParents() ); + assert TestTools.checkCollection( rev1.getParents() ); + assert TestTools.checkCollection( rev2.getParents() ); + assert TestTools.checkCollection( rev3.getParents(), p1 ); + assert TestTools.checkCollection( rev4.getParents(), p1 ); + assert TestTools.checkCollection( rev5.getParents() ); } @Test @@ -283,11 +283,11 @@ public class BasicSametable extends BaseEnversJPAFunctionalTestCase { Child2Entity rev4 = getAuditReader().find( Child2Entity.class, c2_1_id, 4 ); Child2Entity rev5 = getAuditReader().find( Child2Entity.class, c2_1_id, 5 ); - assert TestTools.checkList( rev1.getParents() ); - assert TestTools.checkList( rev2.getParents(), p2 ); - assert TestTools.checkList( rev3.getParents(), p2 ); - assert TestTools.checkList( rev4.getParents(), p2 ); - assert TestTools.checkList( rev5.getParents(), p2 ); + assert TestTools.checkCollection( rev1.getParents() ); + assert TestTools.checkCollection( rev2.getParents(), p2 ); + assert TestTools.checkCollection( rev3.getParents(), p2 ); + assert TestTools.checkCollection( rev4.getParents(), p2 ); + assert TestTools.checkCollection( rev5.getParents(), p2 ); } @Test @@ -301,10 +301,10 @@ public class BasicSametable extends BaseEnversJPAFunctionalTestCase { Child2Entity rev4 = getAuditReader().find( Child2Entity.class, c2_2_id, 4 ); Child2Entity rev5 = getAuditReader().find( Child2Entity.class, c2_2_id, 5 ); - assert TestTools.checkList( rev1.getParents() ); - assert TestTools.checkList( rev2.getParents() ); - assert TestTools.checkList( rev3.getParents(), p1 ); - assert TestTools.checkList( rev4.getParents(), p1, p2 ); - assert TestTools.checkList( rev5.getParents(), p1 ); + assert TestTools.checkCollection( rev1.getParents() ); + assert TestTools.checkCollection( rev2.getParents() ); + assert TestTools.checkCollection( rev3.getParents(), p1 ); + assert TestTools.checkCollection( rev4.getParents(), p1, p2 ); + assert TestTools.checkCollection( rev5.getParents(), p1 ); } } \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/BasicUniList.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/BasicUniList.java index 04654084bc..f04eb9d415 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/BasicUniList.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/BasicUniList.java @@ -151,9 +151,9 @@ public class BasicUniList extends BaseEnversJPAFunctionalTestCase { ListUniEntity rev5 = getAuditReader().find( ListUniEntity.class, ing1_id, 5 ); assert rev1.getReferences().equals( Collections.EMPTY_LIST ); - assert TestTools.checkList( rev2.getReferences(), ed1 ); - assert TestTools.checkList( rev3.getReferences(), ed1, ed2 ); - assert TestTools.checkList( rev4.getReferences(), ed2 ); + assert TestTools.checkCollection( rev2.getReferences(), ed1 ); + assert TestTools.checkCollection( rev3.getReferences(), ed1, ed2 ); + assert TestTools.checkCollection( rev4.getReferences(), ed2 ); assert rev5.getReferences().equals( Collections.EMPTY_LIST ); } @@ -169,9 +169,9 @@ public class BasicUniList extends BaseEnversJPAFunctionalTestCase { ListUniEntity rev5 = getAuditReader().find( ListUniEntity.class, ing2_id, 5 ); assert rev1.getReferences().equals( Collections.EMPTY_LIST ); - assert TestTools.checkList( rev2.getReferences(), ed1, ed2 ); - assert TestTools.checkList( rev3.getReferences(), ed1, ed2 ); - assert TestTools.checkList( rev4.getReferences(), ed1, ed2 ); - assert TestTools.checkList( rev5.getReferences(), ed1, ed2 ); + assert TestTools.checkCollection( rev2.getReferences(), ed1, ed2 ); + assert TestTools.checkCollection( rev3.getReferences(), ed1, ed2 ); + assert TestTools.checkCollection( rev4.getReferences(), ed1, ed2 ); + assert TestTools.checkCollection( rev5.getReferences(), ed1, ed2 ); } } \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/M2MIndexedListNotAuditedTarget.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/M2MIndexedListNotAuditedTarget.java index 63c8c56c3d..0a09f669a9 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/M2MIndexedListNotAuditedTarget.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/M2MIndexedListNotAuditedTarget.java @@ -11,7 +11,7 @@ import org.hibernate.envers.test.entities.manytomany.unidirectional.M2MIndexedLi import org.junit.Test; -import static org.hibernate.envers.test.tools.TestTools.checkList; +import static org.hibernate.envers.test.tools.TestTools.checkCollection; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -114,9 +114,9 @@ public class M2MIndexedListNotAuditedTarget extends BaseEnversJPAFunctionalTestC 3 ); - assertTrue( checkList( rev1.getReferences(), uste1, uste2 ) ); - assertTrue( checkList( rev2.getReferences(), uste1, uste2 ) ); - assertTrue( checkList( rev3.getReferences(), uste2, uste1 ) ); + assertTrue( checkCollection( rev1.getReferences(), uste1, uste2 ) ); + assertTrue( checkCollection( rev2.getReferences(), uste1, uste2 ) ); + assertTrue( checkCollection( rev3.getReferences(), uste2, uste1 ) ); } @Test @@ -138,7 +138,7 @@ public class M2MIndexedListNotAuditedTarget extends BaseEnversJPAFunctionalTestC ); assertNull( rev1 ); - assertTrue( checkList( rev2.getReferences(), uste2 ) ); - assertTrue( checkList( rev3.getReferences(), uste2 ) ); + assertTrue( checkCollection( rev2.getReferences(), uste2 ) ); + assertTrue( checkCollection( rev3.getReferences(), uste2 ) ); } } diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/M2MRelationNotAuditedTarget.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/M2MRelationNotAuditedTarget.java index 2e7d567128..c672d7fd1a 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/M2MRelationNotAuditedTarget.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/M2MRelationNotAuditedTarget.java @@ -35,7 +35,7 @@ import org.hibernate.envers.test.entities.manytomany.unidirectional.M2MTargetNot import org.junit.Test; -import static org.hibernate.envers.test.tools.TestTools.checkList; +import static org.hibernate.envers.test.tools.TestTools.checkCollection; import static org.junit.Assert.assertTrue; /** @@ -153,10 +153,10 @@ public class M2MRelationNotAuditedTarget extends BaseEnversJPAFunctionalTestCase M2MTargetNotAuditedEntity rev3 = getAuditReader().find( M2MTargetNotAuditedEntity.class, tnae1_id, 3 ); M2MTargetNotAuditedEntity rev4 = getAuditReader().find( M2MTargetNotAuditedEntity.class, tnae1_id, 4 ); - assertTrue( checkList( rev1.getReferences() ) ); - assertTrue( checkList( rev2.getReferences(), uste1 ) ); - assertTrue( checkList( rev3.getReferences(), uste1 ) ); - assertTrue( checkList( rev4.getReferences(), uste1, uste2 ) ); + assertTrue( checkCollection( rev1.getReferences() ) ); + assertTrue( checkCollection( rev2.getReferences(), uste1 ) ); + assertTrue( checkCollection( rev3.getReferences(), uste1 ) ); + assertTrue( checkCollection( rev4.getReferences(), uste1, uste2 ) ); } @Test @@ -169,9 +169,9 @@ public class M2MRelationNotAuditedTarget extends BaseEnversJPAFunctionalTestCase M2MTargetNotAuditedEntity rev3 = getAuditReader().find( M2MTargetNotAuditedEntity.class, tnae2_id, 3 ); M2MTargetNotAuditedEntity rev4 = getAuditReader().find( M2MTargetNotAuditedEntity.class, tnae2_id, 4 ); - assertTrue( checkList( rev1.getReferences(), uste1, uste2 ) ); - assertTrue( checkList( rev2.getReferences(), uste2 ) ); - assertTrue( checkList( rev3.getReferences() ) ); - assertTrue( checkList( rev4.getReferences(), uste1 ) ); + assertTrue( checkCollection( rev1.getReferences(), uste1, uste2 ) ); + assertTrue( checkCollection( rev2.getReferences(), uste2 ) ); + assertTrue( checkCollection( rev3.getReferences() ) ); + assertTrue( checkCollection( rev4.getReferences(), uste1 ) ); } } diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/BasicList.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/BasicList.java index 267e5e191e..3095e43561 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/BasicList.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/BasicList.java @@ -118,9 +118,9 @@ public class BasicList extends BaseEnversJPAFunctionalTestCase { ListRefEdEntity rev2 = getAuditReader().find( ListRefEdEntity.class, ed1_id, 2 ); ListRefEdEntity rev3 = getAuditReader().find( ListRefEdEntity.class, ed1_id, 3 ); - assert TestTools.checkList( rev1.getReffering(), ing1, ing2 ); - assert TestTools.checkList( rev2.getReffering(), ing2 ); - assert TestTools.checkList( rev3.getReffering() ); + assert TestTools.checkCollection( rev1.getReffering(), ing1, ing2 ); + assert TestTools.checkCollection( rev2.getReffering(), ing2 ); + assert TestTools.checkCollection( rev3.getReffering() ); } @Test @@ -132,9 +132,9 @@ public class BasicList extends BaseEnversJPAFunctionalTestCase { ListRefEdEntity rev2 = getAuditReader().find( ListRefEdEntity.class, ed2_id, 2 ); ListRefEdEntity rev3 = getAuditReader().find( ListRefEdEntity.class, ed2_id, 3 ); - assert TestTools.checkList( rev1.getReffering() ); - assert TestTools.checkList( rev2.getReffering(), ing1 ); - assert TestTools.checkList( rev3.getReffering(), ing1, ing2 ); + assert TestTools.checkCollection( rev1.getReffering() ); + assert TestTools.checkCollection( rev2.getReffering(), ing1 ); + assert TestTools.checkCollection( rev3.getReffering(), ing1, ing2 ); } @Test diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/BasicDetachedList.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/BasicDetachedList.java index 682b52ed63..820b45bde1 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/BasicDetachedList.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/BasicDetachedList.java @@ -126,10 +126,10 @@ public class BasicDetachedList extends BaseEnversJPAFunctionalTestCase { ListRefCollEntity rev3 = getAuditReader().find( ListRefCollEntity.class, coll1_id, 3 ); ListRefCollEntity rev4 = getAuditReader().find( ListRefCollEntity.class, coll1_id, 4 ); - assert TestTools.checkList( rev1.getCollection(), str1 ); - assert TestTools.checkList( rev2.getCollection(), str1, str2 ); - assert TestTools.checkList( rev3.getCollection(), str2 ); - assert TestTools.checkList( rev4.getCollection() ); + assert TestTools.checkCollection( rev1.getCollection(), str1 ); + assert TestTools.checkCollection( rev2.getCollection(), str1, str2 ); + assert TestTools.checkCollection( rev3.getCollection(), str2 ); + assert TestTools.checkCollection( rev4.getCollection() ); assert "coll1".equals( rev1.getData() ); assert "coll1".equals( rev2.getData() ); diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/DoubleJoinColumnBidirectionalList.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/DoubleJoinColumnBidirectionalList.java index a25e76827b..3fdf1bb109 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/DoubleJoinColumnBidirectionalList.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/DoubleJoinColumnBidirectionalList.java @@ -34,7 +34,7 @@ import org.hibernate.envers.test.entities.onetomany.detached.DoubleListJoinColum import org.junit.Test; -import static org.hibernate.envers.test.tools.TestTools.checkList; +import static org.hibernate.envers.test.tools.TestTools.checkCollection; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -266,15 +266,15 @@ public class DoubleJoinColumnBidirectionalList extends BaseEnversJPAFunctionalTe 4 ); - assertTrue( checkList( rev1.getReferences1(), ed1_1_fromRev1 ) ); - assertTrue( checkList( rev2.getReferences1(), ed1_1_fromRev1, ed1_2 ) ); - assertTrue( checkList( rev3.getReferences1(), ed1_1_fromRev3, ed1_2 ) ); - assertTrue( checkList( rev4.getReferences1() ) ); + assertTrue( checkCollection( rev1.getReferences1(), ed1_1_fromRev1 ) ); + assertTrue( checkCollection( rev2.getReferences1(), ed1_1_fromRev1, ed1_2 ) ); + assertTrue( checkCollection( rev3.getReferences1(), ed1_1_fromRev3, ed1_2 ) ); + assertTrue( checkCollection( rev4.getReferences1() ) ); - assertTrue( checkList( rev1.getReferences2(), ed2_1 ) ); - assertTrue( checkList( rev2.getReferences2(), ed2_1, ed2_2_fromRev1 ) ); - assertTrue( checkList( rev3.getReferences2(), ed2_1, ed2_2_fromRev3 ) ); - assertTrue( checkList( rev4.getReferences2(), ed2_2_fromRev3 ) ); + assertTrue( checkCollection( rev1.getReferences2(), ed2_1 ) ); + assertTrue( checkCollection( rev2.getReferences2(), ed2_1, ed2_2_fromRev1 ) ); + assertTrue( checkCollection( rev3.getReferences2(), ed2_1, ed2_2_fromRev3 ) ); + assertTrue( checkCollection( rev4.getReferences2(), ed2_2_fromRev3 ) ); } @Test @@ -319,15 +319,15 @@ public class DoubleJoinColumnBidirectionalList extends BaseEnversJPAFunctionalTe 4 ); - assertTrue( checkList( rev1.getReferences1(), ed1_2 ) ); - assertTrue( checkList( rev2.getReferences1() ) ); - assertTrue( checkList( rev3.getReferences1() ) ); - assertTrue( checkList( rev4.getReferences1(), ed1_1_fromRev3, ed1_2 ) ); + assertTrue( checkCollection( rev1.getReferences1(), ed1_2 ) ); + assertTrue( checkCollection( rev2.getReferences1() ) ); + assertTrue( checkCollection( rev3.getReferences1() ) ); + assertTrue( checkCollection( rev4.getReferences1(), ed1_1_fromRev3, ed1_2 ) ); - assertTrue( checkList( rev1.getReferences2(), ed2_2_fromRev1 ) ); - assertTrue( checkList( rev2.getReferences2() ) ); - assertTrue( checkList( rev3.getReferences2() ) ); - assertTrue( checkList( rev4.getReferences2(), ed2_1 ) ); + assertTrue( checkCollection( rev1.getReferences2(), ed2_2_fromRev1 ) ); + assertTrue( checkCollection( rev2.getReferences2() ) ); + assertTrue( checkCollection( rev3.getReferences2() ) ); + assertTrue( checkCollection( rev4.getReferences2(), ed2_1 ) ); } @Test diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java index 173fc57639..b2e5011202 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java @@ -33,7 +33,7 @@ import org.hibernate.envers.test.entities.onetomany.detached.ListJoinColumnBidir import org.junit.Test; -import static org.hibernate.envers.test.tools.TestTools.checkList; +import static org.hibernate.envers.test.tools.TestTools.checkCollection; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -199,10 +199,10 @@ public class JoinColumnBidirectionalList extends BaseEnversJPAFunctionalTestCase 4 ); - assertTrue( checkList( rev1.getReferences(), ed1_fromRev1 ) ); - assertTrue( checkList( rev2.getReferences(), ed1_fromRev1, ed2 ) ); - assertTrue( checkList( rev3.getReferences(), ed1_fromRev3, ed2 ) ); - assertTrue( checkList( rev4.getReferences() ) ); + assertTrue( checkCollection( rev1.getReferences(), ed1_fromRev1 ) ); + assertTrue( checkCollection( rev2.getReferences(), ed1_fromRev1, ed2 ) ); + assertTrue( checkCollection( rev3.getReferences(), ed1_fromRev3, ed2 ) ); + assertTrue( checkCollection( rev4.getReferences() ) ); } @Test @@ -237,10 +237,10 @@ public class JoinColumnBidirectionalList extends BaseEnversJPAFunctionalTestCase 4 ); - assertTrue( checkList( rev1.getReferences(), ed2 ) ); - assertTrue( checkList( rev2.getReferences() ) ); - assertTrue( checkList( rev3.getReferences() ) ); - assertTrue( checkList( rev4.getReferences(), ed1, ed2 ) ); + assertTrue( checkCollection( rev1.getReferences(), ed2 ) ); + assertTrue( checkCollection( rev2.getReferences() ) ); + assertTrue( checkCollection( rev3.getReferences() ) ); + assertTrue( checkCollection( rev4.getReferences(), ed1, ed2 ) ); } @Test diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalListWithInheritance.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalListWithInheritance.java index f592416a19..566957b447 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalListWithInheritance.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalListWithInheritance.java @@ -34,7 +34,7 @@ import org.hibernate.envers.test.entities.onetomany.detached.ListJoinColumnBidir import org.junit.Test; -import static org.hibernate.envers.test.tools.TestTools.checkList; +import static org.hibernate.envers.test.tools.TestTools.checkCollection; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -169,8 +169,8 @@ public class JoinColumnBidirectionalListWithInheritance extends BaseEnversJPAFun 2 ); - assertTrue( checkList( rev1.getReferences(), ed1 ) ); - assertTrue( checkList( rev2.getReferences(), ed1, ed2 ) ); + assertTrue( checkCollection( rev1.getReferences(), ed1 ) ); + assertTrue( checkCollection( rev2.getReferences(), ed1, ed2 ) ); } @Test @@ -191,8 +191,8 @@ public class JoinColumnBidirectionalListWithInheritance extends BaseEnversJPAFun 2 ); - assertTrue( checkList( rev1.getReferences(), ed2 ) ); - assertTrue( checkList( rev2.getReferences() ) ); + assertTrue( checkCollection( rev1.getReferences(), ed2 ) ); + assertTrue( checkCollection( rev2.getReferences() ) ); } @Test diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/hierarchy/HierarchyTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/hierarchy/HierarchyTest.java index d9dbd07e60..924cd3206a 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/hierarchy/HierarchyTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/hierarchy/HierarchyTest.java @@ -75,17 +75,17 @@ public class HierarchyTest extends BaseEnversJPAFunctionalTestCase { Node ver1 = getAuditReader().find( Node.class, parentId, 1 ); Assert.assertEquals( parent, ver1 ); - Assert.assertTrue( TestTools.checkList( ver1.getChildren(), child1, child2 ) ); + Assert.assertTrue( TestTools.checkCollection( ver1.getChildren(), child1, child2 ) ); child1.setData( "child1 modified" ); Node ver2 = getAuditReader().find( Node.class, parentId, 2 ); Assert.assertEquals( parent, ver2 ); - Assert.assertTrue( TestTools.checkList( ver2.getChildren(), child1, child2 ) ); + Assert.assertTrue( TestTools.checkCollection( ver2.getChildren(), child1, child2 ) ); Node ver3 = getAuditReader().find( Node.class, parentId, 3 ); Assert.assertEquals( parent, ver3 ); - Assert.assertTrue( TestTools.checkList( ver3.getChildren(), child1 ) ); + Assert.assertTrue( TestTools.checkCollection( ver3.getChildren(), child1 ) ); } @Test diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/proxy/RemovedObjectQueryTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/proxy/RemovedObjectQueryTest.java index 5fb9cc8d3d..3f862afae9 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/proxy/RemovedObjectQueryTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/proxy/RemovedObjectQueryTest.java @@ -1,6 +1,7 @@ package org.hibernate.envers.test.integration.proxy; import javax.persistence.EntityManager; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -17,9 +18,13 @@ import org.hibernate.envers.test.entities.IntTestPrivSeqEntity; import org.hibernate.envers.test.entities.StrTestPrivSeqEntity; import org.hibernate.envers.test.entities.UnversionedStrTestEntity; import org.hibernate.envers.test.entities.collection.StringSetEntity; +import org.hibernate.envers.test.entities.manytomany.ListOwnedEntity; +import org.hibernate.envers.test.entities.manytomany.ListOwningEntity; import org.hibernate.envers.test.entities.manytomany.SetOwnedEntity; import org.hibernate.envers.test.entities.manytomany.SetOwningEntity; import org.hibernate.envers.test.entities.manytomany.unidirectional.M2MIndexedListTargetNotAuditedEntity; +import org.hibernate.envers.test.entities.onetomany.CollectionRefEdEntity; +import org.hibernate.envers.test.entities.onetomany.CollectionRefIngEntity; import org.hibernate.envers.test.entities.onetomany.SetRefEdEntity; import org.hibernate.envers.test.entities.onetomany.SetRefIngEntity; import org.hibernate.envers.test.integration.manytomany.ternary.TernaryMapEntity; @@ -55,7 +60,8 @@ public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase { return new Class[] { SetRefEdEntity.class, SetRefIngEntity.class, SetOwnedEntity.class, SetOwningEntity.class, StringSetEntity.class, UnversionedStrTestEntity.class, M2MIndexedListTargetNotAuditedEntity.class, - TernaryMapEntity.class, StrTestPrivSeqEntity.class, IntTestPrivSeqEntity.class + TernaryMapEntity.class, StrTestPrivSeqEntity.class, IntTestPrivSeqEntity.class, + CollectionRefEdEntity.class, CollectionRefIngEntity.class, ListOwnedEntity.class, ListOwningEntity.class }; } @@ -212,30 +218,108 @@ public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase { ternaryMapId = mapEntity.getId(); - // Revision 16 - removing ternary map + // Revision 16 - updating ternary map + em.getTransaction().begin(); + intEntity2 = em.find( IntTestPrivSeqEntity.class, intEntity2.getId() ); + intEntity2.setNumber( 3 ); + intEntity2 = em.merge( intEntity2 ); + stringEntity2 = em.find( StrTestPrivSeqEntity.class, stringEntity2.getId() ); + stringEntity2.setStr( "Value 3" ); + stringEntity2 = em.merge( stringEntity2 ); + em.getTransaction().commit(); + + // Revision 17 - removing ternary map em.getTransaction().begin(); mapEntity = em.find( TernaryMapEntity.class, mapEntity.getId() ); em.remove( mapEntity ); em.getTransaction().commit(); + CollectionRefEdEntity collEd1 = new CollectionRefEdEntity( 1, "data_ed_1" ); + CollectionRefIngEntity collIng1 = new CollectionRefIngEntity( 2, "data_ing_1", collEd1 ); + collEd1.setReffering( new ArrayList() ); + collEd1.getReffering().add( collIng1 ); + + // Revision 18 - testing one-to-many collection + em.getTransaction().begin(); + em.persist( collEd1 ); + em.persist( collIng1 ); + em.getTransaction().commit(); + + // Revision 19 + em.getTransaction().begin(); + collIng1 = em.find( CollectionRefIngEntity.class, collIng1.getId() ); + collIng1.setData( "modified data_ing_1" ); + collIng1 = em.merge( collIng1 ); + em.getTransaction().commit(); + + // Revision 20 + em.getTransaction().begin(); + collEd1 = em.find( CollectionRefEdEntity.class, collEd1.getId() ); + collIng1 = em.find( CollectionRefIngEntity.class, collIng1.getId() ); + em.remove( collIng1 ); + em.remove( collEd1 ); + em.getTransaction().commit(); + + ListOwnedEntity listEd1 = new ListOwnedEntity( 1, "data_ed_1" ); + ListOwningEntity listIng1 = new ListOwningEntity( 2, "data_ing_1" ); + listEd1.setReferencing( new ArrayList() ); + listIng1.setReferences( new ArrayList() ); + listEd1.getReferencing().add( listIng1 ); + listIng1.getReferences().add( listEd1 ); + + // Revision 21 - testing many-to-many collection + em.getTransaction().begin(); + em.persist( listEd1 ); + em.persist( listIng1 ); + em.getTransaction().commit(); + + // Revision 22 + em.getTransaction().begin(); + listIng1 = em.find( ListOwningEntity.class, listIng1.getId() ); + listIng1.setData( "modified data_ing_1" ); + listIng1 = em.merge( listIng1 ); + em.getTransaction().commit(); + + // Revision 23 + em.getTransaction().begin(); + listIng1 = em.find( ListOwningEntity.class, listIng1.getId() ); + listEd1 = em.find( ListOwnedEntity.class, listEd1.getId() ); + em.remove( listIng1 ); + em.remove( listEd1 ); + em.getTransaction().commit(); + em.close(); } @Test public void testTernaryMap() { + final TernaryMapEntity ternaryMap = new TernaryMapEntity(); + ternaryMap.setId( ternaryMapId ); + ternaryMap.getMap().put( intEntity1, stringEntity1 ); + ternaryMap.getMap().put( new IntTestPrivSeqEntity( 2, intEntity2.getId() ) , new StrTestPrivSeqEntity( "Value 2", stringEntity2.getId() ) ); + + TernaryMapEntity entity = getAuditReader().find( TernaryMapEntity.class, ternaryMapId, 15 ); + + Assert.assertEquals( ternaryMap.getMap(), entity.getMap() ); + + ternaryMap.getMap().clear(); + ternaryMap.getMap().put( intEntity1, stringEntity1 ); + ternaryMap.getMap().put( intEntity2, stringEntity2 ); + + entity = getAuditReader().find( TernaryMapEntity.class, ternaryMapId, 16 ); + + Assert.assertEquals( ternaryMap.getMap(), entity.getMap() ); + List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( TernaryMapEntity.class, false, true ) .add( AuditEntity.id().eq( ternaryMapId ) ) .add( AuditEntity.revisionType().eq( RevisionType.DEL ) ) .getResultList(); Object[] objArray = (Object[]) queryResult.get( 0 ); - Assert.assertEquals( 16, getRevisionNumber( objArray[1] ) ); + Assert.assertEquals( 17, getRevisionNumber( objArray[1] ) ); - TernaryMapEntity mapEntity = (TernaryMapEntity) objArray[0]; - Assert.assertEquals( - TestTools.makeMap( intEntity1, stringEntity1, intEntity2, stringEntity2 ), - mapEntity.getMap() - ); + entity = (TernaryMapEntity) objArray[0]; + Assert.assertEquals( ternaryMap.getMap(), entity.getMap() ); } @Test @@ -251,7 +335,7 @@ public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase { M2MIndexedListTargetNotAuditedEntity relationNotAuditedEntity = (M2MIndexedListTargetNotAuditedEntity) objArray[0]; Assert.assertTrue( - TestTools.checkList( + TestTools.checkCollection( relationNotAuditedEntity.getReferences(), unversionedEntity1, unversionedEntity2 ) @@ -274,6 +358,34 @@ public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase { // One to many tests. + @Test + public void testOneToManyCollectionSemantics() { + final CollectionRefEdEntity edVer1 = new CollectionRefEdEntity( 1, "data_ed_1" ); + final CollectionRefIngEntity ingVer1 = new CollectionRefIngEntity( 2, "data_ing_1" ); + final CollectionRefIngEntity ingVer2 = new CollectionRefIngEntity( 2, "modified data_ing_1" ); + + CollectionRefEdEntity entity = getAuditReader().find( CollectionRefEdEntity.class, 1, 18 ); + + Assert.assertEquals( edVer1, entity ); + Assert.assertTrue( TestTools.checkCollection( entity.getReffering(), ingVer1 ) ); + + entity = getAuditReader().find( CollectionRefEdEntity.class, 1, 19 ); + + Assert.assertTrue( TestTools.checkCollection( entity.getReffering(), ingVer2 ) ); + + List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( CollectionRefEdEntity.class, false, true ) + .add( AuditEntity.id().eq( 1 ) ) + .add( AuditEntity.revisionType().eq( RevisionType.DEL ) ) + .getResultList(); + Object[] objArray = (Object[]) queryResult.get( 0 ); + + Assert.assertEquals( 20, getRevisionNumber( objArray[1] ) ); + + entity = (CollectionRefEdEntity) objArray[0]; + Assert.assertEquals( "data_ed_1", entity.getData() ); + Assert.assertTrue( TestTools.checkCollection( entity.getReffering(), ingVer2 ) ); + } + @Test public void testReferencedOneToManySameRevision() { List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetRefIngEntity.class, false, true ) @@ -360,6 +472,34 @@ public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase { // Many to many tests. + @Test + public void testManyToManyCollectionSemantics() { + final ListOwnedEntity edVer1 = new ListOwnedEntity( 1, "data_ed_1" ); + final ListOwningEntity ingVer1 = new ListOwningEntity( 2, "data_ing_1" ); + final ListOwningEntity ingVer2 = new ListOwningEntity( 2, "modified data_ing_1" ); + + ListOwnedEntity entity = getAuditReader().find( ListOwnedEntity.class, 1, 21 ); + + Assert.assertEquals( edVer1, entity ); + Assert.assertTrue( TestTools.checkCollection( entity.getReferencing(), ingVer1 ) ); + + entity = getAuditReader().find( ListOwnedEntity.class, 1, 22 ); + + Assert.assertTrue( TestTools.checkCollection( entity.getReferencing(), ingVer2 ) ); + + List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( ListOwnedEntity.class, false, true ) + .add( AuditEntity.id().eq( 1 ) ) + .add( AuditEntity.revisionType().eq( RevisionType.DEL ) ) + .getResultList(); + Object[] objArray = (Object[]) queryResult.get( 0 ); + + Assert.assertEquals( 23, getRevisionNumber( objArray[1] ) ); + + entity = (ListOwnedEntity) objArray[0]; + Assert.assertEquals( "data_ed_1", entity.getData() ); + Assert.assertTrue( TestTools.checkCollection( entity.getReferencing(), ingVer2 ) ); + } + @Test public void testOwnedManyToManySameRevision() { List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetOwningEntity.class, false, true ) diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/AggregateQuery.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/AggregateQuery.java index d0a2c04295..e71f170266 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/AggregateQuery.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/AggregateQuery.java @@ -134,7 +134,7 @@ public class AggregateQuery extends BaseEnversJPAFunctionalTestCase { .add( AuditEntity.id().between( 2, 3 ) ) .getResultList(); Assert.assertTrue( - TestTools.checkList( + TestTools.checkCollection( list, new IntTestEntity( 10, 2 ), new IntTestEntity( 8, 3 ), new IntTestEntity( 52, 2 ) ) diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/SimpleQuery.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/SimpleQuery.java index 88237e2a25..282a58e680 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/SimpleQuery.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/SimpleQuery.java @@ -311,7 +311,7 @@ public class SimpleQuery extends BaseEnversJPAFunctionalTestCase { .addProjection( AuditEntity.revisionType() ).add( AuditEntity.id().eq( id1 ) ) .getSingleResult(); - Assert.assertTrue( TestTools.checkList( result, site1, site2, site3 ) ); + Assert.assertTrue( TestTools.checkCollection( result, site1, site2, site3 ) ); Assert.assertEquals( revisionType, RevisionType.ADD ); } @@ -330,7 +330,7 @@ public class SimpleQuery extends BaseEnversJPAFunctionalTestCase { .addProjection( AuditEntity.revisionType() ).add( AuditEntity.id().eq( id1 ) ) .getSingleResult(); - Assert.assertTrue( TestTools.checkList( result, site1, site2 ) ); + Assert.assertTrue( TestTools.checkCollection( result, site1, site2 ) ); Assert.assertEquals( revisionType, RevisionType.MOD ); } @@ -348,7 +348,7 @@ public class SimpleQuery extends BaseEnversJPAFunctionalTestCase { .addProjection( AuditEntity.revisionType() ).add( AuditEntity.id().eq( id1 ) ) .getSingleResult(); - Assert.assertTrue( TestTools.checkList( result, site1 ) ); + Assert.assertTrue( TestTools.checkCollection( result, site1 ) ); Assert.assertEquals( revisionType, RevisionType.DEL ); } diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/reventity/trackmodifiedentities/DefaultTrackingEntitiesTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/reventity/trackmodifiedentities/DefaultTrackingEntitiesTest.java index 2cf39b0101..4f73a6699f 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/reventity/trackmodifiedentities/DefaultTrackingEntitiesTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/reventity/trackmodifiedentities/DefaultTrackingEntitiesTest.java @@ -91,14 +91,14 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase StrTestEntity ste = new StrTestEntity( "x", steId ); StrIntTestEntity site = new StrIntTestEntity( "y", 1, siteId ); - assert TestTools.checkList( getCrossTypeRevisionChangesReader().findEntities( 1 ), ste, site ); + assert TestTools.checkCollection( getCrossTypeRevisionChangesReader().findEntities( 1 ), ste, site ); } @Test public void testTrackModifiedEntities() { StrIntTestEntity site = new StrIntTestEntity( "y", 2, siteId ); - assert TestTools.checkList( getCrossTypeRevisionChangesReader().findEntities( 2 ), site ); + assert TestTools.checkCollection( getCrossTypeRevisionChangesReader().findEntities( 2 ), site ); } @Test @@ -106,7 +106,7 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase StrTestEntity ste = new StrTestEntity( null, steId ); StrIntTestEntity site = new StrIntTestEntity( null, null, siteId ); - assert TestTools.checkList( getCrossTypeRevisionChangesReader().findEntities( 3 ), site, ste ); + assert TestTools.checkCollection( getCrossTypeRevisionChangesReader().findEntities( 3 ), site, ste ); } @Test @@ -120,9 +120,9 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase StrIntTestEntity site = new StrIntTestEntity( "y", 1, siteId ); Map> result = getCrossTypeRevisionChangesReader().findEntitiesGroupByRevisionType( 1 ); - assert TestTools.checkList( result.get( RevisionType.ADD ), site, ste ); - assert TestTools.checkList( result.get( RevisionType.MOD ) ); - assert TestTools.checkList( result.get( RevisionType.DEL ) ); + assert TestTools.checkCollection( result.get( RevisionType.ADD ), site, ste ); + assert TestTools.checkCollection( result.get( RevisionType.MOD ) ); + assert TestTools.checkCollection( result.get( RevisionType.DEL ) ); } @Test @@ -130,9 +130,9 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase StrIntTestEntity site = new StrIntTestEntity( "y", 2, siteId ); Map> result = getCrossTypeRevisionChangesReader().findEntitiesGroupByRevisionType( 2 ); - assert TestTools.checkList( result.get( RevisionType.ADD ) ); - assert TestTools.checkList( result.get( RevisionType.MOD ), site ); - assert TestTools.checkList( result.get( RevisionType.DEL ) ); + assert TestTools.checkCollection( result.get( RevisionType.ADD ) ); + assert TestTools.checkCollection( result.get( RevisionType.MOD ), site ); + assert TestTools.checkCollection( result.get( RevisionType.DEL ) ); } @Test @@ -141,9 +141,9 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase StrIntTestEntity site = new StrIntTestEntity( null, null, siteId ); Map> result = getCrossTypeRevisionChangesReader().findEntitiesGroupByRevisionType( 3 ); - assert TestTools.checkList( result.get( RevisionType.ADD ) ); - assert TestTools.checkList( result.get( RevisionType.MOD ) ); - assert TestTools.checkList( result.get( RevisionType.DEL ), site, ste ); + assert TestTools.checkCollection( result.get( RevisionType.ADD ) ); + assert TestTools.checkCollection( result.get( RevisionType.MOD ) ); + assert TestTools.checkCollection( result.get( RevisionType.DEL ), site, ste ); } @Test @@ -151,7 +151,7 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase StrTestEntity ste = new StrTestEntity( "x", steId ); StrIntTestEntity site = new StrIntTestEntity( "y", 1, siteId ); - assert TestTools.checkList( + assert TestTools.checkCollection( getCrossTypeRevisionChangesReader().findEntities( 1, RevisionType.ADD ), ste, site @@ -162,7 +162,10 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase public void testFindChangedEntitiesByRevisionTypeMOD() { StrIntTestEntity site = new StrIntTestEntity( "y", 2, siteId ); - assert TestTools.checkList( getCrossTypeRevisionChangesReader().findEntities( 2, RevisionType.MOD ), site ); + assert TestTools.checkCollection( + getCrossTypeRevisionChangesReader().findEntities( 2, RevisionType.MOD ), + site + ); } @Test @@ -170,7 +173,7 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase StrTestEntity ste = new StrTestEntity( null, steId ); StrIntTestEntity site = new StrIntTestEntity( null, null, siteId ); - assert TestTools.checkList( + assert TestTools.checkCollection( getCrossTypeRevisionChangesReader().findEntities( 3, RevisionType.DEL ), ste, site diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTestCustomRevEnt.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTestCustomRevEnt.java index 214f57f724..af4c2943f1 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTestCustomRevEnt.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTestCustomRevEnt.java @@ -291,17 +291,17 @@ public class ValidityAuditStrategyRevEndTestCustomRevEnt extends BaseEnversJPAFu ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p1_id, 4 ); ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p1_id, 5 ); - assert TestTools.checkList( rev1.getChildren1() ); - assert TestTools.checkList( rev2.getChildren1(), c1_1 ); - assert TestTools.checkList( rev3.getChildren1(), c1_1, c1_2 ); - assert TestTools.checkList( rev4.getChildren1(), c1_2 ); - assert TestTools.checkList( rev5.getChildren1() ); + assert TestTools.checkCollection( rev1.getChildren1() ); + assert TestTools.checkCollection( rev2.getChildren1(), c1_1 ); + assert TestTools.checkCollection( rev3.getChildren1(), c1_1, c1_2 ); + assert TestTools.checkCollection( rev4.getChildren1(), c1_2 ); + assert TestTools.checkCollection( rev5.getChildren1() ); - assert TestTools.checkList( rev1.getChildren2() ); - assert TestTools.checkList( rev2.getChildren2() ); - assert TestTools.checkList( rev3.getChildren2(), c2_2 ); - assert TestTools.checkList( rev4.getChildren2(), c2_2 ); - assert TestTools.checkList( rev5.getChildren2(), c2_2 ); + assert TestTools.checkCollection( rev1.getChildren2() ); + assert TestTools.checkCollection( rev2.getChildren2() ); + assert TestTools.checkCollection( rev3.getChildren2(), c2_2 ); + assert TestTools.checkCollection( rev4.getChildren2(), c2_2 ); + assert TestTools.checkCollection( rev5.getChildren2(), c2_2 ); } @Test @@ -319,17 +319,17 @@ public class ValidityAuditStrategyRevEndTestCustomRevEnt extends BaseEnversJPAFu ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p2_id, 4 ); ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p2_id, 5 ); - assert TestTools.checkList( rev1.getChildren1() ); - assert TestTools.checkList( rev2.getChildren1() ); - assert TestTools.checkList( rev3.getChildren1(), c1_1 ); - assert TestTools.checkList( rev4.getChildren1(), c1_1 ); - assert TestTools.checkList( rev5.getChildren1(), c1_1 ); + assert TestTools.checkCollection( rev1.getChildren1() ); + assert TestTools.checkCollection( rev2.getChildren1() ); + assert TestTools.checkCollection( rev3.getChildren1(), c1_1 ); + assert TestTools.checkCollection( rev4.getChildren1(), c1_1 ); + assert TestTools.checkCollection( rev5.getChildren1(), c1_1 ); - assert TestTools.checkList( rev1.getChildren2() ); - assert TestTools.checkList( rev2.getChildren2(), c2_1 ); - assert TestTools.checkList( rev3.getChildren2(), c2_1 ); - assert TestTools.checkList( rev4.getChildren2(), c2_1, c2_2 ); - assert TestTools.checkList( rev5.getChildren2(), c2_1 ); + assert TestTools.checkCollection( rev1.getChildren2() ); + assert TestTools.checkCollection( rev2.getChildren2(), c2_1 ); + assert TestTools.checkCollection( rev3.getChildren2(), c2_1 ); + assert TestTools.checkCollection( rev4.getChildren2(), c2_1, c2_2 ); + assert TestTools.checkCollection( rev5.getChildren2(), c2_1 ); } @Test @@ -358,11 +358,11 @@ public class ValidityAuditStrategyRevEndTestCustomRevEnt extends BaseEnversJPAFu 5 ); - assert TestTools.checkList( rev1.getParents() ); - assert TestTools.checkList( rev2.getParents(), p1 ); - assert TestTools.checkList( rev3.getParents(), p1, p2 ); - assert TestTools.checkList( rev4.getParents(), p2 ); - assert TestTools.checkList( rev5.getParents(), p2 ); + assert TestTools.checkCollection( rev1.getParents() ); + assert TestTools.checkCollection( rev2.getParents(), p1 ); + assert TestTools.checkCollection( rev3.getParents(), p1, p2 ); + assert TestTools.checkCollection( rev4.getParents(), p2 ); + assert TestTools.checkCollection( rev5.getParents(), p2 ); } // TODO: this was disabled? @@ -391,11 +391,11 @@ public class ValidityAuditStrategyRevEndTestCustomRevEnt extends BaseEnversJPAFu 5 ); - assert TestTools.checkList( rev1.getParents() ); - assert TestTools.checkList( rev2.getParents() ); - assert TestTools.checkList( rev3.getParents(), p1 ); - assert TestTools.checkList( rev4.getParents(), p1 ); - assert TestTools.checkList( rev5.getParents() ); + assert TestTools.checkCollection( rev1.getParents() ); + assert TestTools.checkCollection( rev2.getParents() ); + assert TestTools.checkCollection( rev3.getParents(), p1 ); + assert TestTools.checkCollection( rev4.getParents(), p1 ); + assert TestTools.checkCollection( rev5.getParents() ); } @Test @@ -423,11 +423,11 @@ public class ValidityAuditStrategyRevEndTestCustomRevEnt extends BaseEnversJPAFu 5 ); - assert TestTools.checkList( rev1.getParents() ); - assert TestTools.checkList( rev2.getParents(), p2 ); - assert TestTools.checkList( rev3.getParents(), p2 ); - assert TestTools.checkList( rev4.getParents(), p2 ); - assert TestTools.checkList( rev5.getParents(), p2 ); + assert TestTools.checkCollection( rev1.getParents() ); + assert TestTools.checkCollection( rev2.getParents(), p2 ); + assert TestTools.checkCollection( rev3.getParents(), p2 ); + assert TestTools.checkCollection( rev4.getParents(), p2 ); + assert TestTools.checkCollection( rev5.getParents(), p2 ); } @Test @@ -456,11 +456,11 @@ public class ValidityAuditStrategyRevEndTestCustomRevEnt extends BaseEnversJPAFu 5 ); - assert TestTools.checkList( rev1.getParents() ); - assert TestTools.checkList( rev2.getParents() ); - assert TestTools.checkList( rev3.getParents(), p1 ); - assert TestTools.checkList( rev4.getParents(), p1, p2 ); - assert TestTools.checkList( rev5.getParents(), p1 ); + assert TestTools.checkCollection( rev1.getParents() ); + assert TestTools.checkCollection( rev2.getParents() ); + assert TestTools.checkCollection( rev3.getParents(), p1 ); + assert TestTools.checkCollection( rev4.getParents(), p1, p2 ); + assert TestTools.checkCollection( rev5.getParents(), p1 ); } private List> getRevisions( diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTsTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTsTest.java index 0957f120ea..95491e1cda 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTsTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/strategy/ValidityAuditStrategyRevEndTsTest.java @@ -289,17 +289,17 @@ public class ValidityAuditStrategyRevEndTsTest extends BaseEnversJPAFunctionalTe ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p1_id, 4 ); ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p1_id, 5 ); - assert TestTools.checkList( rev1.getChildren1() ); - assert TestTools.checkList( rev2.getChildren1(), c1_1 ); - assert TestTools.checkList( rev3.getChildren1(), c1_1, c1_2 ); - assert TestTools.checkList( rev4.getChildren1(), c1_2 ); - assert TestTools.checkList( rev5.getChildren1() ); + assert TestTools.checkCollection( rev1.getChildren1() ); + assert TestTools.checkCollection( rev2.getChildren1(), c1_1 ); + assert TestTools.checkCollection( rev3.getChildren1(), c1_1, c1_2 ); + assert TestTools.checkCollection( rev4.getChildren1(), c1_2 ); + assert TestTools.checkCollection( rev5.getChildren1() ); - assert TestTools.checkList( rev1.getChildren2() ); - assert TestTools.checkList( rev2.getChildren2() ); - assert TestTools.checkList( rev3.getChildren2(), c2_2 ); - assert TestTools.checkList( rev4.getChildren2(), c2_2 ); - assert TestTools.checkList( rev5.getChildren2(), c2_2 ); + assert TestTools.checkCollection( rev1.getChildren2() ); + assert TestTools.checkCollection( rev2.getChildren2() ); + assert TestTools.checkCollection( rev3.getChildren2(), c2_2 ); + assert TestTools.checkCollection( rev4.getChildren2(), c2_2 ); + assert TestTools.checkCollection( rev5.getChildren2(), c2_2 ); } @Test @@ -317,17 +317,17 @@ public class ValidityAuditStrategyRevEndTsTest extends BaseEnversJPAFunctionalTe ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p2_id, 4 ); ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p2_id, 5 ); - assert TestTools.checkList( rev1.getChildren1() ); - assert TestTools.checkList( rev2.getChildren1() ); - assert TestTools.checkList( rev3.getChildren1(), c1_1 ); - assert TestTools.checkList( rev4.getChildren1(), c1_1 ); - assert TestTools.checkList( rev5.getChildren1(), c1_1 ); + assert TestTools.checkCollection( rev1.getChildren1() ); + assert TestTools.checkCollection( rev2.getChildren1() ); + assert TestTools.checkCollection( rev3.getChildren1(), c1_1 ); + assert TestTools.checkCollection( rev4.getChildren1(), c1_1 ); + assert TestTools.checkCollection( rev5.getChildren1(), c1_1 ); - assert TestTools.checkList( rev1.getChildren2() ); - assert TestTools.checkList( rev2.getChildren2(), c2_1 ); - assert TestTools.checkList( rev3.getChildren2(), c2_1 ); - assert TestTools.checkList( rev4.getChildren2(), c2_1, c2_2 ); - assert TestTools.checkList( rev5.getChildren2(), c2_1 ); + assert TestTools.checkCollection( rev1.getChildren2() ); + assert TestTools.checkCollection( rev2.getChildren2(), c2_1 ); + assert TestTools.checkCollection( rev3.getChildren2(), c2_1 ); + assert TestTools.checkCollection( rev4.getChildren2(), c2_1, c2_2 ); + assert TestTools.checkCollection( rev5.getChildren2(), c2_1 ); } @Test @@ -356,11 +356,11 @@ public class ValidityAuditStrategyRevEndTsTest extends BaseEnversJPAFunctionalTe 5 ); - assert TestTools.checkList( rev1.getParents() ); - assert TestTools.checkList( rev2.getParents(), p1 ); - assert TestTools.checkList( rev3.getParents(), p1, p2 ); - assert TestTools.checkList( rev4.getParents(), p2 ); - assert TestTools.checkList( rev5.getParents(), p2 ); + assert TestTools.checkCollection( rev1.getParents() ); + assert TestTools.checkCollection( rev2.getParents(), p1 ); + assert TestTools.checkCollection( rev3.getParents(), p1, p2 ); + assert TestTools.checkCollection( rev4.getParents(), p2 ); + assert TestTools.checkCollection( rev5.getParents(), p2 ); } // TODO: this was disabled? @@ -389,11 +389,11 @@ public class ValidityAuditStrategyRevEndTsTest extends BaseEnversJPAFunctionalTe 5 ); - assert TestTools.checkList( rev1.getParents() ); - assert TestTools.checkList( rev2.getParents() ); - assert TestTools.checkList( rev3.getParents(), p1 ); - assert TestTools.checkList( rev4.getParents(), p1 ); - assert TestTools.checkList( rev5.getParents() ); + assert TestTools.checkCollection( rev1.getParents() ); + assert TestTools.checkCollection( rev2.getParents() ); + assert TestTools.checkCollection( rev3.getParents(), p1 ); + assert TestTools.checkCollection( rev4.getParents(), p1 ); + assert TestTools.checkCollection( rev5.getParents() ); } @Test @@ -421,11 +421,11 @@ public class ValidityAuditStrategyRevEndTsTest extends BaseEnversJPAFunctionalTe 5 ); - assert TestTools.checkList( rev1.getParents() ); - assert TestTools.checkList( rev2.getParents(), p2 ); - assert TestTools.checkList( rev3.getParents(), p2 ); - assert TestTools.checkList( rev4.getParents(), p2 ); - assert TestTools.checkList( rev5.getParents(), p2 ); + assert TestTools.checkCollection( rev1.getParents() ); + assert TestTools.checkCollection( rev2.getParents(), p2 ); + assert TestTools.checkCollection( rev3.getParents(), p2 ); + assert TestTools.checkCollection( rev4.getParents(), p2 ); + assert TestTools.checkCollection( rev5.getParents(), p2 ); } @Test @@ -454,11 +454,11 @@ public class ValidityAuditStrategyRevEndTsTest extends BaseEnversJPAFunctionalTe 5 ); - assert TestTools.checkList( rev1.getParents() ); - assert TestTools.checkList( rev2.getParents() ); - assert TestTools.checkList( rev3.getParents(), p1 ); - assert TestTools.checkList( rev4.getParents(), p1, p2 ); - assert TestTools.checkList( rev5.getParents(), p1 ); + assert TestTools.checkCollection( rev1.getParents() ); + assert TestTools.checkCollection( rev2.getParents() ); + assert TestTools.checkCollection( rev3.getParents(), p1 ); + assert TestTools.checkCollection( rev4.getParents(), p1, p2 ); + assert TestTools.checkCollection( rev5.getParents(), p1 ); } private List> getRevisions( diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/tools/TestTools.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/tools/TestTools.java index aa1b56083d..429df8c5f7 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/tools/TestTools.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/tools/TestTools.java @@ -25,6 +25,7 @@ package org.hibernate.envers.test.tools; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -63,7 +64,7 @@ public class TestTools { return ret; } - public static boolean checkList(List list, T... objects) { + public static boolean checkCollection(Collection list, T... objects) { if ( list.size() != objects.length ) { return false; } From 49a2ee04b937e2c67ffaafafedde227267e52d2d Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Fri, 2 Aug 2013 10:41:11 -0400 Subject: [PATCH 26/34] HHH-8414 enforce osgi test method ordering --- .../src/test/java/org/hibernate/osgi/test/OsgiTestCase.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hibernate-osgi/src/test/java/org/hibernate/osgi/test/OsgiTestCase.java b/hibernate-osgi/src/test/java/org/hibernate/osgi/test/OsgiTestCase.java index a72946cb94..4cc7734888 100644 --- a/hibernate-osgi/src/test/java/org/hibernate/osgi/test/OsgiTestCase.java +++ b/hibernate-osgi/src/test/java/org/hibernate/osgi/test/OsgiTestCase.java @@ -28,6 +28,7 @@ import org.hibernate.osgi.OsgiSessionFactoryService; import org.hibernate.osgi.test.result.OsgiTestResults; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.junit.InSequence; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.osgi.metadata.OSGiManifestBuilder; import org.jboss.shrinkwrap.api.ShrinkWrap; @@ -112,6 +113,7 @@ public class OsgiTestCase { * @throws Exception */ @Test + @InSequence(1) public void testClientBundle() throws Exception { commonTests(); @@ -136,6 +138,9 @@ public class OsgiTestCase { * @throws Exception */ @Test + // Arquillian does not restart the container between runs (afaik). Without the ordering, the tests will + // intermittently fail since this method stops the bundle. + @InSequence(2) public void testStop() throws Exception { commonTests(); From 59d2bff81b2c676673d731c20f59b0338a7a5068 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 2 Aug 2013 09:58:28 -0500 Subject: [PATCH 27/34] HHH-8413 - Rename ProcedureResults -> ProcedureOutputs --- .../hibernate/procedure/ProcedureCall.java | 2 +- ...edureResult.java => ProcedureOutputs.java} | 8 ++-- .../procedure/internal/ProcedureCallImpl.java | 10 ++-- ...ultImpl.java => ProcedureOutputsImpl.java} | 28 +++++------ .../result/{Return.java => Output.java} | 14 +++--- .../result/{Result.java => Outputs.java} | 28 +++++------ ...ultSetReturn.java => ResultSetOutput.java} | 2 +- ...ountReturn.java => UpdateCountOutput.java} | 2 +- .../{ResultImpl.java => OutputsImpl.java} | 44 ++++++++--------- .../sql/storedproc/StoredProcedureTest.java | 47 +++++++++---------- .../internal/StoredProcedureQueryImpl.java | 33 +++++++------ 11 files changed, 108 insertions(+), 110 deletions(-) rename hibernate-core/src/main/java/org/hibernate/procedure/{ProcedureResult.java => ProcedureOutputs.java} (89%) rename hibernate-core/src/main/java/org/hibernate/procedure/internal/{ProcedureResultImpl.java => ProcedureOutputsImpl.java} (76%) rename hibernate-core/src/main/java/org/hibernate/result/{Return.java => Output.java} (80%) rename hibernate-core/src/main/java/org/hibernate/result/{Result.java => Outputs.java} (64%) rename hibernate-core/src/main/java/org/hibernate/result/{ResultSetReturn.java => ResultSetOutput.java} (96%) rename hibernate-core/src/main/java/org/hibernate/result/{UpdateCountReturn.java => UpdateCountOutput.java} (96%) rename hibernate-core/src/main/java/org/hibernate/result/internal/{ResultImpl.java => OutputsImpl.java} (89%) diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java index 9e4460be63..7ecd45d545 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java @@ -141,7 +141,7 @@ public interface ProcedureCall extends BasicQueryContract, SynchronizeableQuery * * @return The ProcedureResult representation */ - public ProcedureResult getResult(); + public ProcedureOutputs getResult(); /** * Extract the disconnected representation of this call. Used in HEM to allow redefining a named query diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureResult.java b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureOutputs.java similarity index 89% rename from hibernate-core/src/main/java/org/hibernate/procedure/ProcedureResult.java rename to hibernate-core/src/main/java/org/hibernate/procedure/ProcedureOutputs.java index c680d9e39e..95e60183f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureResult.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureOutputs.java @@ -23,20 +23,20 @@ */ package org.hibernate.procedure; -import org.hibernate.result.Result; +import org.hibernate.result.Outputs; /** - * Specialization of the {@link Result} contract providing access to the stored procedure's registered + * Specialization of the {@link org.hibernate.result.Outputs} contract providing access to the stored procedure's registered * output parameters. * * @author Steve Ebersole */ -public interface ProcedureResult extends Result { +public interface ProcedureOutputs extends Outputs { /** * Retrieve the value of an OUTPUT parameter by the parameter's registration memento. *

* Should NOT be called for parameters registered as REF_CURSOR. REF_CURSOR parameters should be - * accessed via the returns (see {@link #getNextReturn} + * accessed via the returns (see {@link #getNextOutput} * * @param parameterRegistration The parameter's registration memento. * diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java index b9d3fb6e45..c8db3430fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java @@ -53,7 +53,7 @@ import org.hibernate.procedure.NamedParametersNotSupportedException; import org.hibernate.procedure.ParameterRegistration; import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCallMemento; -import org.hibernate.procedure.ProcedureResult; +import org.hibernate.procedure.ProcedureOutputs; import org.hibernate.result.spi.ResultContext; import org.hibernate.type.Type; @@ -75,7 +75,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements private Set synchronizedQuerySpaces; - private ProcedureResultImpl outputs; + private ProcedureOutputsImpl outputs; /** * The no-returns form. @@ -367,7 +367,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements } @Override - public ProcedureResult getResult() { + public ProcedureOutputs getResult() { if ( outputs == null ) { outputs = buildOutputs(); } @@ -375,7 +375,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements return outputs; } - private ProcedureResultImpl buildOutputs() { + private ProcedureOutputsImpl buildOutputs() { // todo : going to need a very specialized Loader for this. // or, might be a good time to look at splitting Loader up into: // 1) building statement objects @@ -419,7 +419,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements i += parameter.getSqlTypes().length; } - return new ProcedureResultImpl( this, statement ); + return new ProcedureOutputsImpl( this, statement ); } catch (SQLException e) { throw getSession().getFactory().getSQLExceptionHelper().convert( diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureResultImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java similarity index 76% rename from hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureResultImpl.java rename to hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java index abb4948ddb..701f205901 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java @@ -28,23 +28,23 @@ import java.sql.ResultSet; import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport; import org.hibernate.procedure.ParameterRegistration; -import org.hibernate.procedure.ProcedureResult; -import org.hibernate.result.Return; -import org.hibernate.result.internal.ResultImpl; +import org.hibernate.procedure.ProcedureOutputs; +import org.hibernate.result.Output; +import org.hibernate.result.internal.OutputsImpl; /** * Implementation of ProcedureResult. Defines centralized access to all of the results of a procedure call. * * @author Steve Ebersole */ -public class ProcedureResultImpl extends ResultImpl implements ProcedureResult { +public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutputs { private final ProcedureCallImpl procedureCall; private final CallableStatement callableStatement; private final ParameterRegistrationImplementor[] refCursorParameters; private int refCursorParamIndex; - ProcedureResultImpl(ProcedureCallImpl procedureCall, CallableStatement callableStatement) { + ProcedureOutputsImpl(ProcedureCallImpl procedureCall, CallableStatement callableStatement) { super( procedureCall, callableStatement ); this.procedureCall = procedureCall; this.callableStatement = callableStatement; @@ -83,7 +83,7 @@ public class ProcedureResultImpl extends ResultImpl implements ProcedureResult { @Override public boolean indicatesMoreReturns() { return super.indicatesMoreReturns() - || ProcedureResultImpl.this.refCursorParamIndex < ProcedureResultImpl.this.refCursorParameters.length; + || ProcedureOutputsImpl.this.refCursorParamIndex < ProcedureOutputsImpl.this.refCursorParameters.length; } @Override @@ -92,21 +92,21 @@ public class ProcedureResultImpl extends ResultImpl implements ProcedureResult { } @Override - protected Return buildExtendedReturn() { - ProcedureResultImpl.this.refCursorParamIndex++; - final ParameterRegistrationImplementor refCursorParam = ProcedureResultImpl.this.refCursorParameters[refCursorParamIndex]; + protected Output buildExtendedReturn() { + ProcedureOutputsImpl.this.refCursorParamIndex++; + final ParameterRegistrationImplementor refCursorParam = ProcedureOutputsImpl.this.refCursorParameters[refCursorParamIndex]; ResultSet resultSet; if ( refCursorParam.getName() != null ) { - resultSet = ProcedureResultImpl.this.procedureCall.getSession().getFactory().getServiceRegistry() + resultSet = ProcedureOutputsImpl.this.procedureCall.getSession().getFactory().getServiceRegistry() .getService( RefCursorSupport.class ) - .getResultSet( ProcedureResultImpl.this.callableStatement, refCursorParam.getName() ); + .getResultSet( ProcedureOutputsImpl.this.callableStatement, refCursorParam.getName() ); } else { - resultSet = ProcedureResultImpl.this.procedureCall.getSession().getFactory().getServiceRegistry() + resultSet = ProcedureOutputsImpl.this.procedureCall.getSession().getFactory().getServiceRegistry() .getService( RefCursorSupport.class ) - .getResultSet( ProcedureResultImpl.this.callableStatement, refCursorParam.getPosition() ); + .getResultSet( ProcedureOutputsImpl.this.callableStatement, refCursorParam.getPosition() ); } - return new ResultSetReturnImpl( extractResults( resultSet ) ); + return new ResultSetOutputImpl( extractResults( resultSet ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/result/Return.java b/hibernate-core/src/main/java/org/hibernate/result/Output.java similarity index 80% rename from hibernate-core/src/main/java/org/hibernate/result/Return.java rename to hibernate-core/src/main/java/org/hibernate/result/Output.java index 93c75356ac..cfaee61a5f 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/Return.java +++ b/hibernate-core/src/main/java/org/hibernate/result/Output.java @@ -24,18 +24,18 @@ package org.hibernate.result; /** - * Common contract for individual return objects which can be either results ({@link ResultSetReturn}) or update - * counts ({@link UpdateCountReturn}). + * Common contract for individual return objects which can be either results ({@link ResultSetOutput}) or update + * counts ({@link UpdateCountOutput}). * * @author Steve Ebersole */ -public interface Return { +public interface Output { /** - * Determine if this return is a result (castable to {@link ResultSetReturn}). The alternative is that it is - * an update count (castable to {@link UpdateCountReturn}). + * Determine if this return is a result (castable to {@link ResultSetOutput}). The alternative is that it is + * an update count (castable to {@link UpdateCountOutput}). * - * @return {@code true} indicates that {@code this} can be safely cast to {@link ResultSetReturn}), other wise - * it can be cast to {@link UpdateCountReturn}. + * @return {@code true} indicates that {@code this} can be safely cast to {@link ResultSetOutput}), other wise + * it can be cast to {@link UpdateCountOutput}. */ public boolean isResultSet(); } diff --git a/hibernate-core/src/main/java/org/hibernate/result/Result.java b/hibernate-core/src/main/java/org/hibernate/result/Outputs.java similarity index 64% rename from hibernate-core/src/main/java/org/hibernate/result/Result.java rename to hibernate-core/src/main/java/org/hibernate/result/Outputs.java index 40cbf2ba59..8016eb8dfd 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/Result.java +++ b/hibernate-core/src/main/java/org/hibernate/result/Outputs.java @@ -24,29 +24,29 @@ package org.hibernate.result; /** - * Represents the result of executing a JDBC statement accounting for mixing of result sets and update counts hiding the - * complexity (IMO) of how this is exposed in the JDBC API. - * - * A result is made up of group of {@link Return} objects, each representing a single result set or update count. + * Represents the outputs of executing a JDBC statement accounting for mixing of result sets and update counts + * hiding the complexity (IMO) of how this is exposed in the JDBC API. + *

+ * The outputs are exposed as a group of {@link Output} objects, each representing a single result set or update count. * Conceptually, Result presents those Returns as an iterator. * * @author Steve Ebersole */ -public interface Result { +public interface Outputs { /** - * Retrieve the current return. + * Retrieve the current Output object. * - * @return The current return. + * @return The current Output object. Can be {@code null} */ - public Return getCurrentReturn(); + public Output getCurrentOutput(); /** - * Are there any more returns associated with this result? + * Are there any more Output objects associated with {@code this}? * - * @return {@code true} means there are more returns available via {@link #getNextReturn()}; {@code false} - * indicates that calling {@link #getNextReturn()} will certainly result in an exception. + * @return {@code true} means there are more Output objects available via {@link #getNextOutput()}; {@code false} + * indicates that calling {@link #getNextOutput()} will certainly result in an exception. */ - public boolean hasMoreReturns(); + public boolean hasMoreOutput(); /** * Retrieve the next return @@ -54,7 +54,7 @@ public interface Result { * @return The next return. * * @throws NoMoreReturnsException Thrown if there are no more returns associated with this Result, as would - * have been indicated by a {@code false} return from {@link #hasMoreReturns()}. + * have been indicated by a {@code false} return from {@link #hasMoreOutput()}. */ - public Return getNextReturn() throws NoMoreReturnsException; + public Output getNextOutput() throws NoMoreReturnsException; } diff --git a/hibernate-core/src/main/java/org/hibernate/result/ResultSetReturn.java b/hibernate-core/src/main/java/org/hibernate/result/ResultSetOutput.java similarity index 96% rename from hibernate-core/src/main/java/org/hibernate/result/ResultSetReturn.java rename to hibernate-core/src/main/java/org/hibernate/result/ResultSetOutput.java index 9c520d8ada..9bc6f34493 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/ResultSetReturn.java +++ b/hibernate-core/src/main/java/org/hibernate/result/ResultSetOutput.java @@ -30,7 +30,7 @@ import java.util.List; * * @author Steve Ebersole */ -public interface ResultSetReturn extends Return { +public interface ResultSetOutput extends Output { /** * Consume the underlying {@link java.sql.ResultSet} and return the resulting List. * diff --git a/hibernate-core/src/main/java/org/hibernate/result/UpdateCountReturn.java b/hibernate-core/src/main/java/org/hibernate/result/UpdateCountOutput.java similarity index 96% rename from hibernate-core/src/main/java/org/hibernate/result/UpdateCountReturn.java rename to hibernate-core/src/main/java/org/hibernate/result/UpdateCountOutput.java index 5574b3f70f..6d63ff3578 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/UpdateCountReturn.java +++ b/hibernate-core/src/main/java/org/hibernate/result/UpdateCountOutput.java @@ -28,7 +28,7 @@ package org.hibernate.result; * * @author Steve Ebersole */ -public interface UpdateCountReturn extends Return { +public interface UpdateCountOutput extends Output { /** * Retrieve the associated update count * diff --git a/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java b/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java similarity index 89% rename from hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java rename to hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java index 1f50f07ef2..721b215004 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/internal/ResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java @@ -42,17 +42,17 @@ import org.hibernate.loader.custom.CustomQuery; import org.hibernate.loader.custom.sql.SQLQueryReturnProcessor; import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.result.NoMoreReturnsException; -import org.hibernate.result.Result; -import org.hibernate.result.ResultSetReturn; -import org.hibernate.result.Return; -import org.hibernate.result.UpdateCountReturn; +import org.hibernate.result.Outputs; +import org.hibernate.result.ResultSetOutput; +import org.hibernate.result.Output; +import org.hibernate.result.UpdateCountOutput; import org.hibernate.result.spi.ResultContext; /** * @author Steve Ebersole */ -public class ResultImpl implements Result { - private static final Logger log = CoreLogging.logger( ResultImpl.class ); +public class OutputsImpl implements Outputs { + private static final Logger log = CoreLogging.logger( OutputsImpl.class ); private final ResultContext context; private final PreparedStatement jdbcStatement; @@ -60,7 +60,7 @@ public class ResultImpl implements Result { private CurrentReturnState currentReturnState; - public ResultImpl(ResultContext context, PreparedStatement jdbcStatement) { + public OutputsImpl(ResultContext context, PreparedStatement jdbcStatement) { this.context = context; this.jdbcStatement = jdbcStatement; @@ -95,7 +95,7 @@ public class ResultImpl implements Result { } @Override - public Return getCurrentReturn() { + public Output getCurrentOutput() { if ( currentReturnState == null ) { return null; } @@ -103,7 +103,7 @@ public class ResultImpl implements Result { } @Override - public boolean hasMoreReturns() { + public boolean hasMoreOutput() { // prepare the next return state try { final boolean isResultSet = jdbcStatement.getMoreResults(); @@ -117,12 +117,12 @@ public class ResultImpl implements Result { } @Override - public Return getNextReturn() { - if ( !hasMoreReturns() ) { + public Output getNextOutput() { + if ( !hasMoreOutput() ) { throw new NoMoreReturnsException( "Results have been exhausted" ); } - return getCurrentReturn(); + return getCurrentOutput(); } private List extractCurrentResults() { @@ -158,7 +158,7 @@ public class ResultImpl implements Result { private final boolean isResultSet; private final int updateCount; - private Return rtn; + private Output rtn; protected CurrentReturnState(boolean isResultSet, int updateCount) { this.isResultSet = isResultSet; @@ -177,14 +177,14 @@ public class ResultImpl implements Result { return updateCount; } - public Return getReturn() { + public Output getReturn() { if ( rtn == null ) { rtn = buildReturn(); } return rtn; } - protected Return buildReturn() { + protected Output buildReturn() { if ( log.isDebugEnabled() ) { log.debugf( "Building Return [isResultSet=%s, updateCount=%s, extendedReturn=%s", @@ -204,10 +204,10 @@ public class ResultImpl implements Result { ); if ( isResultSet() ) { - return new ResultSetReturnImpl( extractCurrentResults() ); + return new ResultSetOutputImpl( extractCurrentResults() ); } else if ( getUpdateCount() >= 0 ) { - return new UpdateCountReturnImpl( updateCount ); + return new UpdateCountOutputImpl( updateCount ); } else if ( hasExtendedReturns() ) { return buildExtendedReturn(); @@ -222,15 +222,15 @@ public class ResultImpl implements Result { return false; } - protected Return buildExtendedReturn() { + protected Output buildExtendedReturn() { throw new IllegalStateException( "State does not define extended returns" ); } } - protected static class ResultSetReturnImpl implements ResultSetReturn { + protected static class ResultSetOutputImpl implements ResultSetOutput { private final List results; - public ResultSetReturnImpl(List results) { + public ResultSetOutputImpl(List results) { this.results = results; } @@ -257,10 +257,10 @@ public class ResultImpl implements Result { } } - protected static class UpdateCountReturnImpl implements UpdateCountReturn { + protected static class UpdateCountOutputImpl implements UpdateCountOutput { private final int updateCount; - public UpdateCountReturnImpl(int updateCount) { + public UpdateCountOutputImpl(int updateCount) { this.updateCount = updateCount; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java index 0b59a59608..de451b842e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java @@ -33,16 +33,15 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.Mapping; import org.hibernate.mapping.AuxiliaryDatabaseObject; import org.hibernate.procedure.ProcedureCall; -import org.hibernate.procedure.ProcedureResult; -import org.hibernate.result.ResultSetReturn; -import org.hibernate.result.Return; +import org.hibernate.procedure.ProcedureOutputs; +import org.hibernate.result.ResultSetOutput; +import org.hibernate.result.Output; import org.hibernate.dialect.H2Dialect; import org.junit.Test; import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; -import org.hibernate.testing.junit4.ExtraAssertions; import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; import static org.junit.Assert.assertEquals; @@ -171,10 +170,10 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { session.beginTransaction(); ProcedureCall query = session.createStoredProcedureCall( "user"); - ProcedureResult procedureResult = query.getResult(); - Return currentReturn = procedureResult.getCurrentReturn(); - assertNotNull( currentReturn ); - ResultSetReturn resultSetReturn = assertTyping( ResultSetReturn.class, currentReturn ); + ProcedureOutputs procedureResult = query.getResult(); + Output currentOutput = procedureResult.getCurrentOutput(); + assertNotNull( currentOutput ); + ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); String name = (String) resultSetReturn.getSingleResult(); assertEquals( "SA", name ); @@ -188,10 +187,10 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { session.beginTransaction(); ProcedureCall query = session.createStoredProcedureCall( "findOneUser" ); - ProcedureResult procedureResult = query.getResult(); - Return currentReturn = procedureResult.getCurrentReturn(); - assertNotNull( currentReturn ); - ResultSetReturn resultSetReturn = assertTyping( ResultSetReturn.class, currentReturn ); + ProcedureOutputs procedureResult = query.getResult(); + Output currentOutput = procedureResult.getCurrentOutput(); + assertNotNull( currentOutput ); + ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); Object result = resultSetReturn.getSingleResult(); assertTyping( Object[].class, result ); String name = (String) ( (Object[]) result )[1]; @@ -207,10 +206,10 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { session.beginTransaction(); ProcedureCall query = session.createStoredProcedureCall( "findUsers" ); - ProcedureResult procedureResult = query.getResult(); - Return currentReturn = procedureResult.getCurrentReturn(); - assertNotNull( currentReturn ); - ResultSetReturn resultSetReturn = assertTyping( ResultSetReturn.class, currentReturn ); + ProcedureOutputs procedureResult = query.getResult(); + Output currentOutput = procedureResult.getCurrentOutput(); + assertNotNull( currentOutput ); + ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); List results = resultSetReturn.getResultList(); assertEquals( 3, results.size() ); @@ -244,10 +243,10 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { ProcedureCall query = session.createStoredProcedureCall( "findUserRange" ); query.registerParameter( "start", Integer.class, ParameterMode.IN ).bindValue( 1 ); query.registerParameter( "end", Integer.class, ParameterMode.IN ).bindValue( 2 ); - ProcedureResult procedureResult = query.getResult(); - Return currentReturn = procedureResult.getCurrentReturn(); - assertNotNull( currentReturn ); - ResultSetReturn resultSetReturn = assertTyping( ResultSetReturn.class, currentReturn ); + ProcedureOutputs procedureResult = query.getResult(); + Output currentOutput = procedureResult.getCurrentOutput(); + assertNotNull( currentOutput ); + ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); List results = resultSetReturn.getResultList(); assertEquals( 1, results.size() ); Object result = results.get( 0 ); @@ -269,10 +268,10 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { ProcedureCall query = session.createStoredProcedureCall( "findUserRange" ); query.registerParameter( 1, Integer.class, ParameterMode.IN ).bindValue( 1 ); query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 ); - ProcedureResult procedureResult = query.getResult(); - Return currentReturn = procedureResult.getCurrentReturn(); - assertNotNull( currentReturn ); - ResultSetReturn resultSetReturn = assertTyping( ResultSetReturn.class, currentReturn ); + ProcedureOutputs procedureResult = query.getResult(); + Output currentOutput = procedureResult.getCurrentOutput(); + assertNotNull( currentOutput ); + ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); List results = resultSetReturn.getResultList(); assertEquals( 1, results.size() ); Object result = results.get( 0 ); diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/StoredProcedureQueryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/StoredProcedureQueryImpl.java index ea023b4f67..94a5fee442 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/StoredProcedureQueryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/StoredProcedureQueryImpl.java @@ -41,14 +41,13 @@ import java.util.List; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockMode; -import org.hibernate.procedure.ParameterRegistration; import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCallMemento; -import org.hibernate.procedure.ProcedureResult; +import org.hibernate.procedure.ProcedureOutputs; import org.hibernate.result.NoMoreReturnsException; -import org.hibernate.result.ResultSetReturn; -import org.hibernate.result.Return; -import org.hibernate.result.UpdateCountReturn; +import org.hibernate.result.ResultSetOutput; +import org.hibernate.result.Output; +import org.hibernate.result.UpdateCountOutput; import org.hibernate.jpa.spi.BaseQueryImpl; import org.hibernate.jpa.spi.HibernateEntityManagerImplementor; @@ -57,7 +56,7 @@ import org.hibernate.jpa.spi.HibernateEntityManagerImplementor; */ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredProcedureQuery { private final ProcedureCall procedureCall; - private ProcedureResult procedureResult; + private ProcedureOutputs procedureResult; public StoredProcedureQueryImpl(ProcedureCall procedureCall, HibernateEntityManagerImplementor entityManager) { super( entityManager ); @@ -200,7 +199,7 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro // outputs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - private ProcedureResult outputs() { + private ProcedureOutputs outputs() { if ( procedureResult == null ) { procedureResult = procedureCall.getResult(); } @@ -220,8 +219,8 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro @Override public boolean execute() { try { - final Return rtn = outputs().getCurrentReturn(); - return rtn != null && ResultSetReturn.class.isInstance( rtn ); + final Output rtn = outputs().getCurrentOutput(); + return rtn != null && ResultSetOutput.class.isInstance( rtn ); } catch (NoMoreReturnsException e) { return false; @@ -238,18 +237,18 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro @Override public boolean hasMoreResults() { - return outputs().hasMoreReturns() && ResultSetReturn.class.isInstance( outputs().getCurrentReturn() ); + return outputs().hasMoreOutput() && ResultSetOutput.class.isInstance( outputs().getCurrentOutput() ); } @Override public int getUpdateCount() { try { - final Return rtn = outputs().getCurrentReturn(); + final Output rtn = outputs().getCurrentOutput(); if ( rtn == null ) { return -1; } - else if ( UpdateCountReturn.class.isInstance( rtn ) ) { - return ( (UpdateCountReturn) rtn ).getUpdateCount(); + else if ( UpdateCountOutput.class.isInstance( rtn ) ) { + return ( (UpdateCountOutput) rtn ).getUpdateCount(); } else { return -1; @@ -263,12 +262,12 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro @Override public List getResultList() { try { - final Return rtn = outputs().getCurrentReturn(); - if ( ! ResultSetReturn.class.isInstance( rtn ) ) { + final Output rtn = outputs().getCurrentOutput(); + if ( ! ResultSetOutput.class.isInstance( rtn ) ) { throw new IllegalStateException( "Current CallableStatement return was not a ResultSet, but getResultList was called" ); } - return ( (ResultSetReturn) rtn ).getResultList(); + return ( (ResultSetOutput) rtn ).getResultList(); } catch (NoMoreReturnsException e) { // todo : the spec is completely silent on these type of edge-case scenarios. @@ -307,7 +306,7 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro if ( ProcedureCall.class.isAssignableFrom( cls ) ) { return (T) procedureCall; } - else if ( ProcedureResult.class.isAssignableFrom( cls ) ) { + else if ( ProcedureOutputs.class.isAssignableFrom( cls ) ) { return (T) outputs(); } else if ( BaseQueryImpl.class.isAssignableFrom( cls ) ) { From 4f1c8a4ba6902604c15aa70f07067f8f5fef7a56 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Thu, 1 Aug 2013 21:55:12 +0100 Subject: [PATCH 28/34] HHH-8407 Missing synchronization in DriverManagerConnectionProviderImpl#stop --- .../DriverManagerConnectionProviderImpl.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java index b21f694879..c1e6566f18 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java @@ -67,6 +67,7 @@ public class DriverManagerConnectionProviderImpl private int poolSize; private boolean autocommit; + //Access guarded by synchronization on the pool instance private final ArrayList pool = new ArrayList(); private final AtomicInteger checkedOut = new AtomicInteger(); @@ -166,15 +167,17 @@ public class DriverManagerConnectionProviderImpl public void stop() { LOG.cleaningUpConnectionPool( url ); - for ( Connection connection : pool ) { - try { - connection.close(); - } - catch (SQLException sqle) { - LOG.unableToClosePooledConnection( sqle ); + synchronized ( pool ) { + for ( Connection connection : pool ) { + try { + connection.close(); + } + catch (SQLException sqle) { + LOG.unableToClosePooledConnection( sqle ); + } } + pool.clear(); } - pool.clear(); stopped = true; } From 1ea823e0b535d7d7ba49924e415156dd5d3a4fdf Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Thu, 1 Aug 2013 22:40:45 +0100 Subject: [PATCH 29/34] HHH-8408 PooledLoOptimizer.noTenantState is accessed with inconsistent synchronization --- .../main/java/org/hibernate/id/enhanced/HiLoOptimizer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/HiLoOptimizer.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/HiLoOptimizer.java index 60403f9975..5582fdf1ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/HiLoOptimizer.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/HiLoOptimizer.java @@ -152,7 +152,7 @@ public class HiLoOptimizer extends AbstractOptimizer { } @Override - public IntegralDataTypeHolder getLastSourceValue() { + public synchronized IntegralDataTypeHolder getLastSourceValue() { return noTenantGenerationState().lastSourceValue; } @@ -168,7 +168,7 @@ public class HiLoOptimizer extends AbstractOptimizer { * * @return Value for property 'lastValue'. */ - public IntegralDataTypeHolder getLastValue() { + public synchronized IntegralDataTypeHolder getLastValue() { return noTenantGenerationState().value.copy().decrement(); } @@ -179,7 +179,7 @@ public class HiLoOptimizer extends AbstractOptimizer { * * @return Value for property 'upperLimit'. */ - public IntegralDataTypeHolder getHiValue() { + public synchronized IntegralDataTypeHolder getHiValue() { return noTenantGenerationState().upperLimit; } } From 1bed5873553937f111a5b3724a9966e625ac1440 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Thu, 1 Aug 2013 22:50:42 +0100 Subject: [PATCH 30/34] HHH-8408 LegacyHiLoAlgorithmOptimizer.noTenantState is accessed with inconsistent synchronization --- .../hibernate/id/enhanced/LegacyHiLoAlgorithmOptimizer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/LegacyHiLoAlgorithmOptimizer.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/LegacyHiLoAlgorithmOptimizer.java index abd33a9ca2..c7a460b19f 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/LegacyHiLoAlgorithmOptimizer.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/LegacyHiLoAlgorithmOptimizer.java @@ -125,7 +125,7 @@ public class LegacyHiLoAlgorithmOptimizer extends AbstractOptimizer { } @Override - public IntegralDataTypeHolder getLastSourceValue() { + public synchronized IntegralDataTypeHolder getLastSourceValue() { return noTenantGenerationState().lastSourceValue.copy(); } @@ -142,7 +142,7 @@ public class LegacyHiLoAlgorithmOptimizer extends AbstractOptimizer { * @return Value for property 'lastValue'. */ @SuppressWarnings( {"UnusedDeclaration"}) - public IntegralDataTypeHolder getLastValue() { + public synchronized IntegralDataTypeHolder getLastValue() { return noTenantGenerationState().value; } } From 168c06d062eb8d3adf72587536cfad1677121fdc Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Thu, 1 Aug 2013 23:09:46 +0100 Subject: [PATCH 31/34] HHH-8409 Improve usage of ConcurrentMaps by using putIfAbsent in StatefulPersistenceContext --- .../engine/internal/StatefulPersistenceContext.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index 5c10005a1b..4fd417d933 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -37,6 +37,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.ConcurrentMap; import org.jboss.logging.Logger; @@ -109,7 +110,7 @@ public class StatefulPersistenceContext implements PersistenceContext { // private Map entityEntries; // Entity proxies, by EntityKey - private Map proxiesByKey; + private ConcurrentMap proxiesByKey; // Snapshots of current database state for entities // that have *not* been loaded @@ -563,9 +564,7 @@ public class StatefulPersistenceContext implements PersistenceContext { final EntityPersister persister = session.getFactory().getEntityPersister( li.getEntityName() ); final EntityKey key = session.generateEntityKey( li.getIdentifier(), persister ); // any earlier proxy takes precedence - if ( !proxiesByKey.containsKey( key ) ) { - proxiesByKey.put( key, proxy ); - } + proxiesByKey.putIfAbsent( key, proxy ); proxy.getHibernateLazyInitializer().setSession( session ); } } From 8aff7db2e0214db110627dd25a5440dab92eb661 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Thu, 1 Aug 2013 23:28:26 +0100 Subject: [PATCH 32/34] HHH-8410 NaturalIdXrefDelegate could miss some cached entries from naturalIdResolutionCacheMap --- .../engine/internal/NaturalIdXrefDelegate.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdXrefDelegate.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdXrefDelegate.java index 29dee5d946..906de573de 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdXrefDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/NaturalIdXrefDelegate.java @@ -55,7 +55,7 @@ public class NaturalIdXrefDelegate { private static final Logger LOG = Logger.getLogger( NaturalIdXrefDelegate.class ); private final StatefulPersistenceContext persistenceContext; - private final Map naturalIdResolutionCacheMap = new ConcurrentHashMap(); + private final ConcurrentHashMap naturalIdResolutionCacheMap = new ConcurrentHashMap(); /** * Constructs a NaturalIdXrefDelegate @@ -92,7 +92,10 @@ public class NaturalIdXrefDelegate { NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister ); if ( entityNaturalIdResolutionCache == null ) { entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister ); - naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache ); + NaturalIdResolutionCache previousInstance = naturalIdResolutionCacheMap.putIfAbsent( persister, entityNaturalIdResolutionCache ); + if ( previousInstance != null ) { + entityNaturalIdResolutionCache = previousInstance; + } } return entityNaturalIdResolutionCache.cache( pk, naturalIdValues ); } @@ -279,7 +282,10 @@ public class NaturalIdXrefDelegate { if ( entityNaturalIdResolutionCache == null ) { entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister ); - naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache ); + NaturalIdResolutionCache existingCache = naturalIdResolutionCacheMap.putIfAbsent( persister, entityNaturalIdResolutionCache ); + if ( existingCache != null ) { + entityNaturalIdResolutionCache = existingCache; + } } entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, cachedNaturalId ); From aea6b767ae6778d77a9ff9c1117ee973f4022462 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Thu, 1 Aug 2013 23:28:44 +0100 Subject: [PATCH 33/34] Micro-tuning for StandardListenerFactory --- .../jpa/event/internal/jpa/StandardListenerFactory.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/StandardListenerFactory.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/StandardListenerFactory.java index feb2d02dcb..f11c1ac77b 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/StandardListenerFactory.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/StandardListenerFactory.java @@ -24,7 +24,6 @@ package org.hibernate.jpa.event.internal.jpa; import javax.persistence.PersistenceException; -import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.hibernate.jpa.event.spi.jpa.ListenerFactory; @@ -36,7 +35,8 @@ import org.hibernate.jpa.event.spi.jpa.ListenerFactory; * @author Steve Ebersole */ public class StandardListenerFactory implements ListenerFactory { - private Map listenerInstances = new ConcurrentHashMap(); + + private final ConcurrentHashMap listenerInstances = new ConcurrentHashMap(); @Override @SuppressWarnings("unchecked") @@ -52,7 +52,10 @@ public class StandardListenerFactory implements ListenerFactory { e ); } - listenerInstances.put( listenerClass, listenerInstance ); + Object existing = listenerInstances.putIfAbsent( listenerClass, listenerInstance ); + if ( existing != null ) { + listenerInstance = existing; + } } return (T) listenerInstance; } From 6beb5acb4bdcbe07e981a6017d69b3c4f71a4480 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 2 Aug 2013 19:17:31 -0500 Subject: [PATCH 34/34] HHH-8415 - Throw exception types expected by JPA spec wrt StoredProcedureQuery --- .../procedure/NoSuchParameterException.java | 35 +++++ .../procedure/ParameterStrategyException.java | 35 +++++ .../hibernate/procedure/ProcedureCall.java | 6 + .../hibernate/procedure/ProcedureOutputs.java | 6 + .../procedure/internal/ProcedureCallImpl.java | 23 ++-- .../internal/ProcedureOutputsImpl.java | 8 +- .../java/org/hibernate/result/Outputs.java | 19 +-- .../result/internal/OutputsImpl.java | 129 +++++++----------- .../result/internal/ResultSetOutputImpl.java | 63 +++++++++ .../internal/UpdateCountOutputImpl.java | 49 +++++++ .../sql/storedproc/StoredProcedureTest.java | 14 +- .../internal/StoredProcedureQueryImpl.java | 72 ++++++---- .../org/hibernate/jpa/spi/BaseQueryImpl.java | 22 ++- .../jpa/test/procedure/JpaTckUsageTest.java | 81 +++++++++++ 14 files changed, 425 insertions(+), 137 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/procedure/NoSuchParameterException.java create mode 100644 hibernate-core/src/main/java/org/hibernate/procedure/ParameterStrategyException.java create mode 100644 hibernate-core/src/main/java/org/hibernate/result/internal/ResultSetOutputImpl.java create mode 100644 hibernate-core/src/main/java/org/hibernate/result/internal/UpdateCountOutputImpl.java diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/NoSuchParameterException.java b/hibernate-core/src/main/java/org/hibernate/procedure/NoSuchParameterException.java new file mode 100644 index 0000000000..263bd60e54 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/procedure/NoSuchParameterException.java @@ -0,0 +1,35 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.procedure; + +import org.hibernate.HibernateException; + +/** + * @author Steve Ebersole + */ +public class NoSuchParameterException extends HibernateException { + public NoSuchParameterException(String message) { + super( message ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/ParameterStrategyException.java b/hibernate-core/src/main/java/org/hibernate/procedure/ParameterStrategyException.java new file mode 100644 index 0000000000..e6dbafcab1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/procedure/ParameterStrategyException.java @@ -0,0 +1,35 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.procedure; + +import org.hibernate.HibernateException; + +/** + * @author Steve Ebersole + */ +public class ParameterStrategyException extends HibernateException { + public ParameterStrategyException(String message) { + super( message ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java index 7ecd45d545..e4c9206cbb 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureCall.java @@ -82,6 +82,9 @@ public interface ProcedureCall extends BasicQueryContract, SynchronizeableQuery * @param position The parameter position * * @return The parameter registration memento + * + * @throws ParameterStrategyException If the ProcedureCall is defined using named parameters + * @throws NoSuchParameterException If no parameter with that position exists */ public ParameterRegistration getParameterRegistration(int position); @@ -122,6 +125,9 @@ public interface ProcedureCall extends BasicQueryContract, SynchronizeableQuery * @param name The parameter name * * @return The parameter registration memento + * + * @throws ParameterStrategyException If the ProcedureCall is defined using positional parameters + * @throws NoSuchParameterException If no parameter with that name exists */ public ParameterRegistration getParameterRegistration(String name); diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureOutputs.java b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureOutputs.java index 95e60183f5..442b33b6dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureOutputs.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/ProcedureOutputs.java @@ -53,6 +53,9 @@ public interface ProcedureOutputs extends Outputs { * * @return The output value. * + * @throws ParameterStrategyException If the ProcedureCall is defined using positional parameters + * @throws NoSuchParameterException If no parameter with that name exists + * * @see ProcedureCall#registerParameter(String, Class, javax.persistence.ParameterMode) */ public Object getOutputParameterValue(String name); @@ -64,6 +67,9 @@ public interface ProcedureOutputs extends Outputs { * * @return The output value. * + * @throws ParameterStrategyException If the ProcedureCall is defined using named parameters + * @throws NoSuchParameterException If no parameter with that position exists + * * @see ProcedureCall#registerParameter(int, Class, javax.persistence.ParameterMode) */ public Object getOutputParameterValue(int position); diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java index c8db3430fc..b6639043c9 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java @@ -50,7 +50,9 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.procedure.NamedParametersNotSupportedException; +import org.hibernate.procedure.NoSuchParameterException; import org.hibernate.procedure.ParameterRegistration; +import org.hibernate.procedure.ParameterStrategyException; import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCallMemento; import org.hibernate.procedure.ProcedureOutputs; @@ -321,21 +323,22 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements @Override public ParameterRegistrationImplementor getParameterRegistration(int position) { if ( parameterStrategy != ParameterStrategy.POSITIONAL ) { - throw new IllegalArgumentException( "Positions were not used to register parameters with this stored procedure call" ); + throw new ParameterStrategyException( + "Attempt to access positional parameter [" + position + "] but ProcedureCall using named parameters" + ); } - try { - return registeredParameters.get( position ); - } - catch ( Exception e ) { - throw new QueryException( "Could not locate parameter registered using that position [" + position + "]" ); + for ( ParameterRegistrationImplementor parameter : registeredParameters ) { + if ( position == parameter.getPosition() ) { + return parameter; + } } + throw new NoSuchParameterException( "Could not locate parameter registered using that position [" + position + "]" ); } @Override @SuppressWarnings("unchecked") public ParameterRegistration registerParameter(String name, Class type, ParameterMode mode) { - final NamedParameterRegistration parameterRegistration - = new NamedParameterRegistration( this, name, mode, type ); + final NamedParameterRegistration parameterRegistration = new NamedParameterRegistration( this, name, mode, type ); registerParameter( parameterRegistration ); return parameterRegistration; } @@ -350,14 +353,14 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements @Override public ParameterRegistrationImplementor getParameterRegistration(String name) { if ( parameterStrategy != ParameterStrategy.NAMED ) { - throw new IllegalArgumentException( "Names were not used to register parameters with this stored procedure call" ); + throw new ParameterStrategyException( "Names were not used to register parameters with this stored procedure call" ); } for ( ParameterRegistrationImplementor parameter : registeredParameters ) { if ( name.equals( parameter.getName() ) ) { return parameter; } } - throw new IllegalArgumentException( "Could not locate parameter registered under that name [" + name + "]" ); + throw new NoSuchParameterException( "Could not locate parameter registered under that name [" + name + "]" ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java index 701f205901..bade61376d 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureOutputsImpl.java @@ -68,7 +68,7 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput } @Override - protected CurrentReturnState buildCurrentReturnDescriptor(boolean isResultSet, int updateCount) { + protected CurrentReturnState buildCurrentReturnState(boolean isResultSet, int updateCount) { return new ProcedureCurrentReturnState( isResultSet, updateCount, refCursorParamIndex ); } @@ -81,8 +81,8 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput } @Override - public boolean indicatesMoreReturns() { - return super.indicatesMoreReturns() + public boolean indicatesMoreOutputs() { + return super.indicatesMoreOutputs() || ProcedureOutputsImpl.this.refCursorParamIndex < ProcedureOutputsImpl.this.refCursorParameters.length; } @@ -106,7 +106,7 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput .getService( RefCursorSupport.class ) .getResultSet( ProcedureOutputsImpl.this.callableStatement, refCursorParam.getPosition() ); } - return new ResultSetOutputImpl( extractResults( resultSet ) ); + return buildResultSetOutput( extractResults( resultSet ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/result/Outputs.java b/hibernate-core/src/main/java/org/hibernate/result/Outputs.java index 8016eb8dfd..90830161bf 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/Outputs.java +++ b/hibernate-core/src/main/java/org/hibernate/result/Outputs.java @@ -38,23 +38,18 @@ public interface Outputs { * * @return The current Output object. Can be {@code null} */ - public Output getCurrentOutput(); + public Output getCurrent(); /** - * Are there any more Output objects associated with {@code this}? + * Go to the next Output object (if any), returning an indication of whether there was another (aka, will + * the next call to {@link #getCurrent()} return {@code null}? * - * @return {@code true} means there are more Output objects available via {@link #getNextOutput()}; {@code false} - * indicates that calling {@link #getNextOutput()} will certainly result in an exception. + * @return {@code true} if the next call to {@link #getCurrent()} will return a non-{@code null} value. */ - public boolean hasMoreOutput(); + public boolean goToNext(); /** - * Retrieve the next return - * - * @return The next return. - * - * @throws NoMoreReturnsException Thrown if there are no more returns associated with this Result, as would - * have been indicated by a {@code false} return from {@link #hasMoreOutput()}. + * Eagerly release any resources held by this Outputs. */ - public Output getNextOutput() throws NoMoreReturnsException; + public void release(); } diff --git a/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java b/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java index 721b215004..8320c2a1ed 100644 --- a/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/result/internal/OutputsImpl.java @@ -43,9 +43,7 @@ import org.hibernate.loader.custom.sql.SQLQueryReturnProcessor; import org.hibernate.loader.spi.AfterLoadAction; import org.hibernate.result.NoMoreReturnsException; import org.hibernate.result.Outputs; -import org.hibernate.result.ResultSetOutput; import org.hibernate.result.Output; -import org.hibernate.result.UpdateCountOutput; import org.hibernate.result.spi.ResultContext; /** @@ -69,14 +67,14 @@ public class OutputsImpl implements Outputs { try { final boolean isResultSet = jdbcStatement.execute(); - currentReturnState = buildCurrentReturnDescriptor( isResultSet ); + currentReturnState = buildCurrentReturnState( isResultSet ); } catch (SQLException e) { throw convert( e, "Error calling CallableStatement.getMoreResults" ); } } - private CurrentReturnState buildCurrentReturnDescriptor(boolean isResultSet) { + private CurrentReturnState buildCurrentReturnState(boolean isResultSet) { int updateCount = -1; if ( ! isResultSet ) { try { @@ -87,42 +85,57 @@ public class OutputsImpl implements Outputs { } } - return buildCurrentReturnDescriptor( isResultSet, updateCount ); + return buildCurrentReturnState( isResultSet, updateCount ); } - protected CurrentReturnState buildCurrentReturnDescriptor(boolean isResultSet, int updateCount) { + protected CurrentReturnState buildCurrentReturnState(boolean isResultSet, int updateCount) { return new CurrentReturnState( isResultSet, updateCount ); } - @Override - public Output getCurrentOutput() { - if ( currentReturnState == null ) { - return null; - } - return currentReturnState.getReturn(); + protected JDBCException convert(SQLException e, String message) { + return context.getSession().getFactory().getSQLExceptionHelper().convert( + e, + message, + context.getSql() + ); } @Override - public boolean hasMoreOutput() { + public Output getCurrent() { + if ( currentReturnState == null ) { + return null; + } + return currentReturnState.getOutput(); + } + + @Override + public boolean goToNext() { + if ( currentReturnState == null ) { + return false; + } + + if ( currentReturnState.indicatesMoreOutputs() ) // prepare the next return state try { final boolean isResultSet = jdbcStatement.getMoreResults(); - currentReturnState = buildCurrentReturnDescriptor( isResultSet ); + currentReturnState = buildCurrentReturnState( isResultSet ); } catch (SQLException e) { throw convert( e, "Error calling CallableStatement.getMoreResults" ); } - return currentReturnState != null && currentReturnState.indicatesMoreReturns(); + // and return + return currentReturnState != null && currentReturnState.indicatesMoreOutputs(); } @Override - public Output getNextOutput() { - if ( !hasMoreOutput() ) { - throw new NoMoreReturnsException( "Results have been exhausted" ); + public void release() { + try { + jdbcStatement.close(); + } + catch (SQLException e) { + log.debug( "Unable to close PreparedStatement", e ); } - - return getCurrentOutput(); } private List extractCurrentResults() { @@ -143,14 +156,6 @@ public class OutputsImpl implements Outputs { } } - protected JDBCException convert(SQLException e, String message) { - return context.getSession().getFactory().getSQLExceptionHelper().convert( - e, - message, - context.getSql() - ); - } - /** * Encapsulates the information needed to interpret the current return within a result */ @@ -165,7 +170,7 @@ public class OutputsImpl implements Outputs { this.updateCount = updateCount; } - public boolean indicatesMoreReturns() { + public boolean indicatesMoreOutputs() { return isResultSet() || getUpdateCount() >= 0; } @@ -177,14 +182,14 @@ public class OutputsImpl implements Outputs { return updateCount; } - public Output getReturn() { + public Output getOutput() { if ( rtn == null ) { - rtn = buildReturn(); + rtn = buildOutput(); } return rtn; } - protected Output buildReturn() { + protected Output buildOutput() { if ( log.isDebugEnabled() ) { log.debugf( "Building Return [isResultSet=%s, updateCount=%s, extendedReturn=%s", @@ -204,10 +209,10 @@ public class OutputsImpl implements Outputs { ); if ( isResultSet() ) { - return new ResultSetOutputImpl( extractCurrentResults() ); + return buildResultSetOutput( extractCurrentResults() ); } else if ( getUpdateCount() >= 0 ) { - return new UpdateCountOutputImpl( updateCount ); + return buildUpdateCountOutput( updateCount ); } else if ( hasExtendedReturns() ) { return buildExtendedReturn(); @@ -218,6 +223,14 @@ public class OutputsImpl implements Outputs { // hooks for stored procedure (out param) processing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + protected Output buildResultSetOutput(List list) { + return new ResultSetOutputImpl( list ); + } + + protected Output buildUpdateCountOutput(int updateCount) { + return new UpdateCountOutputImpl( updateCount ); + } + protected boolean hasExtendedReturns() { return false; } @@ -227,53 +240,9 @@ public class OutputsImpl implements Outputs { } } - protected static class ResultSetOutputImpl implements ResultSetOutput { - private final List results; - public ResultSetOutputImpl(List results) { - this.results = results; - } - - @Override - public boolean isResultSet() { - return true; - } - - @Override - @SuppressWarnings("unchecked") - public List getResultList() { - return results; - } - - @Override - public Object getSingleResult() { - final List results = getResultList(); - if ( results == null || results.isEmpty() ) { - return null; - } - else { - return results.get( 0 ); - } - } - } - - protected static class UpdateCountOutputImpl implements UpdateCountOutput { - private final int updateCount; - - public UpdateCountOutputImpl(int updateCount) { - this.updateCount = updateCount; - } - - @Override - public int getUpdateCount() { - return updateCount; - } - - @Override - public boolean isResultSet() { - return false; - } - } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Hooks into Hibernate's Loader hierarchy for ResultSet -> Object mapping private static CustomLoaderExtension buildSpecializedCustomLoader(final ResultContext context) { // might be better to just manually construct the Return(s).. SQLQueryReturnProcessor does a lot of diff --git a/hibernate-core/src/main/java/org/hibernate/result/internal/ResultSetOutputImpl.java b/hibernate-core/src/main/java/org/hibernate/result/internal/ResultSetOutputImpl.java new file mode 100644 index 0000000000..c4c1741d0e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/result/internal/ResultSetOutputImpl.java @@ -0,0 +1,63 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.result.internal; + +import java.util.List; + +import org.hibernate.result.ResultSetOutput; + +/** + * Implementation of ResultSetOutput + * + * @author Steve Ebersole + */ +class ResultSetOutputImpl implements ResultSetOutput { + private final List results; + + public ResultSetOutputImpl(List results) { + this.results = results; + } + + @Override + public boolean isResultSet() { + return true; + } + + @Override + @SuppressWarnings("unchecked") + public List getResultList() { + return results; + } + + @Override + public Object getSingleResult() { + final List results = getResultList(); + if ( results == null || results.isEmpty() ) { + return null; + } + else { + return results.get( 0 ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/result/internal/UpdateCountOutputImpl.java b/hibernate-core/src/main/java/org/hibernate/result/internal/UpdateCountOutputImpl.java new file mode 100644 index 0000000000..842437d778 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/result/internal/UpdateCountOutputImpl.java @@ -0,0 +1,49 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.result.internal; + +import org.hibernate.result.UpdateCountOutput; + +/** + * Implementation of UpdateCountOutput + * + * @author Steve Ebersole + */ +class UpdateCountOutputImpl implements UpdateCountOutput { + private final int updateCount; + + public UpdateCountOutputImpl(int updateCount) { + this.updateCount = updateCount; + } + + @Override + public int getUpdateCount() { + return updateCount; + } + + @Override + public boolean isResultSet() { + return false; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java index de451b842e..cc3032398a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/sql/storedproc/StoredProcedureTest.java @@ -169,9 +169,9 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { Session session = openSession(); session.beginTransaction(); - ProcedureCall query = session.createStoredProcedureCall( "user"); - ProcedureOutputs procedureResult = query.getResult(); - Output currentOutput = procedureResult.getCurrentOutput(); + ProcedureCall procedureCall = session.createStoredProcedureCall( "user"); + ProcedureOutputs procedureOutputs = procedureCall.getResult(); + Output currentOutput = procedureOutputs.getCurrent(); assertNotNull( currentOutput ); ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); String name = (String) resultSetReturn.getSingleResult(); @@ -188,7 +188,7 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { ProcedureCall query = session.createStoredProcedureCall( "findOneUser" ); ProcedureOutputs procedureResult = query.getResult(); - Output currentOutput = procedureResult.getCurrentOutput(); + Output currentOutput = procedureResult.getCurrent(); assertNotNull( currentOutput ); ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); Object result = resultSetReturn.getSingleResult(); @@ -207,7 +207,7 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { ProcedureCall query = session.createStoredProcedureCall( "findUsers" ); ProcedureOutputs procedureResult = query.getResult(); - Output currentOutput = procedureResult.getCurrentOutput(); + Output currentOutput = procedureResult.getCurrent(); assertNotNull( currentOutput ); ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); List results = resultSetReturn.getResultList(); @@ -244,7 +244,7 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { query.registerParameter( "start", Integer.class, ParameterMode.IN ).bindValue( 1 ); query.registerParameter( "end", Integer.class, ParameterMode.IN ).bindValue( 2 ); ProcedureOutputs procedureResult = query.getResult(); - Output currentOutput = procedureResult.getCurrentOutput(); + Output currentOutput = procedureResult.getCurrent(); assertNotNull( currentOutput ); ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); List results = resultSetReturn.getResultList(); @@ -269,7 +269,7 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase { query.registerParameter( 1, Integer.class, ParameterMode.IN ).bindValue( 1 ); query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 ); ProcedureOutputs procedureResult = query.getResult(); - Output currentOutput = procedureResult.getCurrentOutput(); + Output currentOutput = procedureResult.getCurrent(); assertNotNull( currentOutput ); ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput ); List results = resultSetReturn.getResultList(); diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/StoredProcedureQueryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/StoredProcedureQueryImpl.java index 94a5fee442..6ddb0bdf4b 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/StoredProcedureQueryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/StoredProcedureQueryImpl.java @@ -41,6 +41,8 @@ import java.util.List; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockMode; +import org.hibernate.procedure.NoSuchParameterException; +import org.hibernate.procedure.ParameterStrategyException; import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCallMemento; import org.hibernate.procedure.ProcedureOutputs; @@ -199,27 +201,10 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro // outputs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - private ProcedureOutputs outputs() { - if ( procedureResult == null ) { - procedureResult = procedureCall.getResult(); - } - return procedureResult; - } - - @Override - public Object getOutputParameterValue(int position) { - return outputs().getOutputParameterValue( position ); - } - - @Override - public Object getOutputParameterValue(String parameterName) { - return outputs().getOutputParameterValue( parameterName ); - } - @Override public boolean execute() { try { - final Output rtn = outputs().getCurrentOutput(); + final Output rtn = outputs().getCurrent(); return rtn != null && ResultSetOutput.class.isInstance( rtn ); } catch (NoMoreReturnsException e) { @@ -227,23 +212,64 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro } } + protected ProcedureOutputs outputs() { + if ( procedureResult == null ) { + procedureResult = procedureCall.getResult(); + } + return procedureResult; + } + @Override public int executeUpdate() { if ( ! entityManager().isTransactionInProgress() ) { throw new TransactionRequiredException( "javax.persistence.Query.executeUpdate requires active transaction" ); } - return getUpdateCount(); + + // the expectation is that there is just one Output, of type UpdateCountOutput + try { + execute(); + return getUpdateCount(); + } + finally { + outputs().release(); + } + } + + @Override + public Object getOutputParameterValue(int position) { + try { + return outputs().getOutputParameterValue( position ); + } + catch (ParameterStrategyException e) { + throw new IllegalArgumentException( "Invalid mix of named and positional parameters", e ); + } + catch (NoSuchParameterException e) { + throw new IllegalArgumentException( e.getMessage(), e ); + } + } + + @Override + public Object getOutputParameterValue(String parameterName) { + try { + return outputs().getOutputParameterValue( parameterName ); + } + catch (ParameterStrategyException e) { + throw new IllegalArgumentException( "Invalid mix of named and positional parameters", e ); + } + catch (NoSuchParameterException e) { + throw new IllegalArgumentException( e.getMessage(), e ); + } } @Override public boolean hasMoreResults() { - return outputs().hasMoreOutput() && ResultSetOutput.class.isInstance( outputs().getCurrentOutput() ); + return outputs().goToNext() && ResultSetOutput.class.isInstance( outputs().getCurrent() ); } @Override public int getUpdateCount() { try { - final Output rtn = outputs().getCurrentOutput(); + final Output rtn = outputs().getCurrent(); if ( rtn == null ) { return -1; } @@ -262,9 +288,9 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro @Override public List getResultList() { try { - final Output rtn = outputs().getCurrentOutput(); + final Output rtn = outputs().getCurrent(); if ( ! ResultSetOutput.class.isInstance( rtn ) ) { - throw new IllegalStateException( "Current CallableStatement return was not a ResultSet, but getResultList was called" ); + throw new IllegalStateException( "Current CallableStatement ou was not a ResultSet, but getResultList was called" ); } return ( (ResultSetOutput) rtn ).getResultList(); diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/spi/BaseQueryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/spi/BaseQueryImpl.java index ad239f8c67..9424aa265f 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/spi/BaseQueryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/spi/BaseQueryImpl.java @@ -51,6 +51,8 @@ import org.hibernate.jpa.internal.EntityManagerMessageLogger; import org.hibernate.jpa.internal.util.CacheModeHelper; import org.hibernate.jpa.internal.util.ConfigurationHelper; import org.hibernate.jpa.internal.util.LockModeTypeHelper; +import org.hibernate.procedure.NoSuchParameterException; +import org.hibernate.procedure.ParameterStrategyException; import static org.hibernate.jpa.QueryHints.HINT_CACHEABLE; import static org.hibernate.jpa.QueryHints.HINT_CACHE_MODE; @@ -439,7 +441,8 @@ public abstract class BaseQueryImpl implements Query { protected abstract boolean isJpaPositionalParameter(int position); /** - * Hibernate specific extension to the JPA {@link javax.persistence.Parameter} contract. + * Hibernate specific extension to the JPA {@link javax.persistence.Parameter} contract. Used here to track + * information known about the parameter. */ protected static interface ParameterRegistration extends Parameter { /** @@ -450,6 +453,12 @@ public abstract class BaseQueryImpl implements Query { */ public ParameterMode getMode(); + /** + * Can we bind (set) values on this parameter? Generally this is {@code true}, but would not be in the case + * of parameters with OUT or REF_CURSOR mode. + * + * @return Whether the parameter is bindable (can set be called). + */ public boolean isBindable(); public void bindValue(T value); @@ -459,6 +468,11 @@ public abstract class BaseQueryImpl implements Query { public ParameterBind getBind(); } + /** + * Represents the value currently bound to a particular parameter. + * + * @param + */ protected static interface ParameterBind { public T getValue(); @@ -648,6 +662,12 @@ public abstract class BaseQueryImpl implements Query { try { findParameterRegistration( position ).bindValue( value, temporalType ); } + catch (ParameterStrategyException e) { + throw new IllegalArgumentException( "Invalid mix of named and positional parameters", e ); + } + catch (NoSuchParameterException e) { + throw new IllegalArgumentException( e.getMessage(), e ); + } catch (QueryParameterException e) { throw new IllegalArgumentException( e ); } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaTckUsageTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaTckUsageTest.java index a834be7717..e734c79887 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaTckUsageTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/procedure/JpaTckUsageTest.java @@ -54,7 +54,9 @@ import org.hibernate.testing.junit4.BaseUnitTestCase; import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * Tests various JPA usage scenarios for performing stored procedures. Inspired by the awesomely well-done JPA TCK @@ -107,6 +109,42 @@ public class JpaTckUsageTest extends BaseUnitTestCase { } } + @Test + @FailureExpected( jiraKey = "HHH-8416", message = "JPA TCK challenge" ) + public void testHasMoreResultsHandlingTckChallenge() { + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + + try { + StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser", User.class ); + assertTrue( query.execute() ); + assertTrue( query.hasMoreResults() ); + query.getResultList(); + assertFalse( query.hasMoreResults() ); + } + finally { + em.getTransaction().commit(); + em.close(); + } + } + + @Test + public void testHasMoreResultsHandling() { + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + + try { + StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser", User.class ); + assertTrue( query.execute() ); + query.getResultList(); + assertFalse( query.hasMoreResults() ); + } + finally { + em.getTransaction().commit(); + em.close(); + } + } + @Test public void testResultClassHandling() { EntityManager em = entityManagerFactory.createEntityManager(); @@ -148,6 +186,38 @@ public class JpaTckUsageTest extends BaseUnitTestCase { } } + @Test + public void testSettingNonExistingParams() { + EntityManager em = entityManagerFactory.createEntityManager(); + em.getTransaction().begin(); + + try { + // non-existing positional param + try { + StoredProcedureQuery query = em.createNamedStoredProcedureQuery( "positional-param" ); + query.setParameter( 99, 1 ); + fail( "Expecting an exception" ); + } + catch (IllegalArgumentException expected) { + // this is the expected condition + } + + // non-existing named param + try { + StoredProcedureQuery query = em.createNamedStoredProcedureQuery( "positional-param" ); + query.setParameter( "does-not-exist", 1 ); + fail( "Expecting an exception" ); + } + catch (IllegalArgumentException expected) { + // this is the expected condition + } + } + finally { + em.getTransaction().commit(); + em.close(); + } + } + @Test @FailureExpected( jiraKey = "HHH-8395", message = "Out of the frying pan into the fire: https://issues.apache.org/jira/browse/DERBY-211" ) public void testExecuteUpdate() { @@ -167,6 +237,10 @@ public class JpaTckUsageTest extends BaseUnitTestCase { } } + public void testParameterRegistration() { + + } + // todo : look at ways to allow "Auxiliary DB Objects" to the db via EMF bootstrapping. // public static final String findOneUser_CREATE_CMD = "CREATE ALIAS findOneUser AS $$\n" + @@ -317,6 +391,13 @@ public class JpaTckUsageTest extends BaseUnitTestCase { conn.close(); } + public static void findUserIds(ResultSet[] results) throws SQLException { + Connection conn = DriverManager.getConnection( "jdbc:default:connection" ); + PreparedStatement ps = conn.prepareStatement( "select id from t_user" ); + results[0] = ps.executeQuery(); + conn.close(); + } + public static void deleteAllUsers() throws SQLException { // afaict the only way to return update counts here is to actually perform some DML Connection conn = DriverManager.getConnection( "jdbc:default:connection" );