diff --git a/fine-spring/lib/connector-api-1.5.jar b/fine-spring/lib/connector-api-1.5.jar
new file mode 100644
index 000000000..683762e1f
Binary files /dev/null and b/fine-spring/lib/connector-api-1.5.jar differ
diff --git a/fine-spring/lib/javax.transaction-api-1.2.jar b/fine-spring/lib/javax.transaction-api-1.2.jar
new file mode 100644
index 000000000..e87adb89f
Binary files /dev/null and b/fine-spring/lib/javax.transaction-api-1.2.jar differ
diff --git a/fine-spring/resources/META-INF/com/fr/third/springframework/ldap/config/spring-ldap-2.0.xsd b/fine-spring/resources/META-INF/com/fr/third/springframework/ldap/config/spring-ldap-2.0.xsd
new file mode 100644
index 000000000..67ef7bb90
--- /dev/null
+++ b/fine-spring/resources/META-INF/com/fr/third/springframework/ldap/config/spring-ldap-2.0.xsd
@@ -0,0 +1,685 @@
+
+
+
+
+
+
+
+
+
+ A bean identifier, used for referring to the bean elsewhere in the context.
+ "contextSource".
+
+
+
+
+
+
+ Defines whether read-only operations will be performed using an anonymous (unauthenticated) context.
+
+
+
+
+
+
+ Id of the AuthenticationSource instance to use. If not specified, a SimpleAuthenticationSource will
+ be used.
+
+
+
+
+
+
+ Id of the DirContextAuthenticationStrategy instance to use. If not specified, a SimpleDirContextAuthenticationStrategy
+ will be used.
+
+
+
+
+
+
+ The base DN. If configured, all LDAP operations on contexts retrieved from this ContextSource will
+ be relative to this DN. Default is an empty distinguished name (i.e. all operations will be
+ relative to the directory root).
+
+
+
+
+
+
+ The password to use for authentication.
+
+
+
+
+
+
+ Specify whether native Java LDAP connection pooling should be used. Default is false.
+
+
+
+
+
+
+ Defines the strategy to handle referrals, as described on http://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html.
+ Default is null.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ URL of the LDAP server to use. If fail-over functionality is desired, more than one URL can
+ be specified, separated using comma (,).
+
+
+
+
+
+
+ The username (principal) to use for authentication. This will normally be the distinguished name
+ of an admin user.
+
+
+
+
+
+
+ Reference to a Map of custom environment properties that should supplied with the environment
+ sent to the DirContext on construction.
+
+
+
+
+
+
+
+
+
+ The maximum number of active connections of each type (read-only|read-write)
+ that can be allocated from the pool at the same time, or non-positive for no limit.
+ Default is 8.
+
+
+
+
+
+
+ The overall maximum number of active connections (for all types) that can be allocated from
+ this pool at the same time, or non-positive for no limit. Default is -1 (no limit).
+
+
+
+
+
+
+ The maximum number of active connections of each type (read-only|read-write) that can remain idle in the pool,
+ without extra ones being released, or non-positive for no limit. Default is 8.
+
+
+
+
+
+
+ The minimum number of active connections of each type (read-only|read-write) that can remain
+ idle in the pool, without extra ones being created, or zero to create none. Default is 0.
+
+
+
+
+
+
+ The maximum number of milliseconds that the pool will wait (when there are no available connections)
+ for a connection to be returned before throwing an exception, or non-positive to wait indefinitely.
+ Default is -1.
+
+
+
+
+
+
+ Specifies the behaviour when the pool is exhausted.
+
+
+
+
+
+
+
+ Throw a NoSuchElementException when the pool is exhausted
+
+
+
+
+
+
+ Wait until a new object is available. If max-wait is positive a NoSuchElementException
+ is thrown if no new object is available after the maxWait time expires.
+
+
+
+
+
+
+ Create and return a new object (essentially making maxActive meaningless).
+
+
+
+
+
+
+
+
+
+ The indication of whether objects will be validated before being borrowed from the pool.
+ If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made.
+ Default is false.
+
+
+
+
+
+
+ The indication of whether objects will be validated before being returned to the pool.
+ Default is false.
+
+
+
+
+
+
+ The indication of whether objects will be validated by the idle object evictor (if any).
+ If an object fails to validate, it will be dropped from the pool.
+ Default is false.
+
+
+
+
+
+
+ The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive,
+ no idle object evictor thread will be run. Default is -1.
+
+
+
+
+
+
+ The number of objects to examine during each run of the idle object evictor thread (if any).
+ Default is 3.
+
+
+
+
+
+
+ The minimum amount of time an object may sit idle in the pool before it is eligible
+ for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30.
+
+
+
+
+
+
+ The base dn to use for validation searches. Default is LdapUtils.emptyPath().
+
+
+
+
+
+
+ The filter to use for validation queries. Default is (objectclass=*).
+
+
+
+
+
+
+ Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;
+ countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].
+
+
+
+
+
+
+ Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;
+ countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].
+
+
+
+
+
+
+
+
+
+ The overall maximum number of active connections (for all types) that can be allocated from
+ this pool at the same time, or non-positive for no limit. Default is -1 (no limit).
+
+
+
+
+
+
+ The limit on the number of object instances allocated by the pool (checked out or idle),
+ per key. When the limit is reached, the sub-pool is said to be exhausted. A negative value
+ indicates no limit. Default is 8.
+
+
+
+
+
+
+ The maximum number of active connections per type (read-only|read-write) that can remain idle in the pool,
+ without extra ones being released, or non-positive for no limit. Default is 8.
+
+
+
+
+
+
+ The minimum number of active connections per type (read-only|read-write) that can remain
+ idle in the pool, without extra ones being created, or zero to create none. Default is 0.
+
+
+
+
+
+
+ The maximum number of milliseconds that the pool will wait (when there are no available connections)
+ for a connection to be returned before throwing an exception, or non-positive to wait indefinitely.
+ Default is -1.
+
+
+
+
+
+
+ Sets to wait until a new object is available. If max-wait is positive a NoSuchElementException
+ is thrown if no new object is available after the maxWait time expires..
+
+
+
+
+
+
+ Sets whether objects created for the pool will be validated before borrowing. If the object
+ fails to validate, then borrowing will fail. Default is false.
+
+
+
+
+
+
+ The indication of whether objects will be validated before being borrowed from the pool.
+ If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made.
+ Default is false.
+
+
+
+
+
+
+ The indication of whether objects will be validated before being returned to the pool.
+ Default is false.
+
+
+
+
+
+
+ The indication of whether objects will be validated by the idle object evictor (if any).
+ If an object fails to validate, it will be dropped from the pool.
+ Default is false.
+
+
+
+
+
+
+ The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive,
+ no idle object evictor thread will be run. Default is -1.
+
+
+
+
+
+
+ The number of objects to examine during each run of the idle object evictor thread (if any).
+ Default is 3.
+
+
+
+
+
+
+ The minimum amount of time an object may sit idle in the pool before it is eligible
+ for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30.
+
+
+
+
+
+
+ The minimum amount of time an object may sit idle in the pool before it is eligible for
+ eviction by the idle object evictor, with the extra condition that at least minimum number
+ of object instances per key remain in the pool. This settings is overridden by min-evictable-time-millis if
+ it is set to a positive value. Default is -1.
+
+
+
+
+
+
+ The name of the eviction policy implementation that is used by this pool. The Pool will
+ attempt to load the class using the thread context class loader. If that fails, the Pool
+ will attempt to load the class using the class loader that loaded this class. Default is
+ com.fr.third.org.apache.commons.pool2.impl.DefaultEvictionPolicy.
+
+
+
+
+
+
+ Sets whether or not the pool serves threads waiting to borrow connections fairly.
+ True means that waiting threads are served as if waiting in a FIFO queue. Default is false.
+
+
+
+
+
+
+ Sets whether JMX will be enabled with the platform MBean server for the pool. Default
+ is true.
+
+
+
+
+
+
+ The value of the JMX name base that will be used as part of the name assigned
+ to JMX enabled pools. Default is null.
+
+
+
+
+
+
+ The value of the JMX name prefix that will be used as part of the name assigned
+ to JMX enabled pools. Default value is pool.
+
+
+
+
+
+
+ Sets whether the pool has LIFO (last in, first out) behaviour with
+ respect to idle objects - always returning the most recently used object
+ from the pool, or as a FIFO (first in, first out) queue, where the pool
+ always returns the oldest object in the idle object pool. Default is true.
+
+
+
+
+
+
+ The base dn to use for validation searches. Default is LdapUtils.emptyPath().
+
+
+
+
+
+
+ The filter to use for validation queries. Default is (objectclass=*).
+
+
+
+
+
+
+ Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;
+ countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].
+
+
+
+
+
+
+ Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;
+ countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].
+
+
+
+
+
+
+
+
+ Creates a ContextSource instance to be used to get LdapContexts for communicating with an LDAP server.
+
+
+
+
+
+
+
+
+ Defines the settings to use for the Spring LDAP connection pooling support.
+
+
+
+
+
+
+
+
+
+
+
+ Defines the settings to use for the Spring LDAP connection pooling support based on commons-pool2 library.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ A bean identifier, used for referring to the bean elsewhere in the context.
+ Default is "ldapTemplate".
+
+
+
+
+
+
+ Id of the ContextSource instance to use. Default is "contextSource".
+
+
+
+
+
+
+ The default count limit for searches. Default is 0 (no limit).
+
+
+
+
+
+
+ The default time limit for searches. Default is 0 (no limit).
+
+
+
+
+
+
+ The default search scope for searches. Default is SUBTREE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Specifies whether NameNotFoundException should be ignored in searches. Setting this
+ attribute to true will cause errors caused by invalid search base to be silently swallowed.
+ Default is false.
+
+
+
+
+
+
+ Specifies whether PartialResultException should be ignored in searches. Some LDAP servers
+ have problems with referrals; these should normally be followed automatically, but if this
+ doesn't work it will manifest itself with a PartialResultException. Setting this attribute
+ to true presents a work-around to this problem. Default is false.
+
+
+
+
+
+
+ Id of the ObjectDirectoryMapper instance to use. Default is a default-configured DefaultObjectDirectoryMapper.
+
+
+
+
+
+
+
+
+ Creates an LdapTemplate instance.
+
+
+
+
+
+
+
+
+
+
+
+ Id of this instance. Default is "transactionManager".
+
+
+
+
+
+
+ Id of the ContextSource instance to use. "contextSource".
+
+
+
+
+
+
+ Id of the DataSource instance to use.
+
+
+
+
+
+
+ Id of the Hibernate SessionFactory instance to use.
+
+
+
+
+
+
+
+
+ Creates an ContextSourceTransactionManager. If data-source-ref or session-factory-ref is specified,
+ a DataSourceAndContextSourceTransactionManager/HibernateAndContextSourceTransactionManager will be
+ created.
+
+
+
+
+
+
+
+ The default (simplistic) TempEntryRenamingStrategy. Please note that this
+ strategy will not work for more advanced scenarios. See reference documentation
+ for details.
+
+
+
+
+
+
+ The default suffix that will be added to modified entries.
+ Default is "_temp".
+
+
+
+
+
+
+
+
+ TempEntryRenamingStrategy that moves the entry to a different subtree than
+ the original entry.
+
+
+
+
+
+
+ The subtree base where changed entries should be moved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The reference to an LdapTemplate. Will default to 'ldapTemplate'.
+
+
+
+
+
+
+
+
diff --git a/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-2.0.xsd b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-2.0.xsd
new file mode 100644
index 000000000..740ce9e4a
--- /dev/null
+++ b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-2.0.xsd
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-2.5.xsd b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-2.5.xsd
new file mode 100644
index 000000000..41212b527
--- /dev/null
+++ b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-2.5.xsd
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-3.0.xsd b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-3.0.xsd
new file mode 100644
index 000000000..7fc987776
--- /dev/null
+++ b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-3.0.xsd
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-3.1.xsd b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-3.1.xsd
new file mode 100644
index 000000000..7536daf3d
--- /dev/null
+++ b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-3.1.xsd
@@ -0,0 +1,247 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-3.2.xsd b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-3.2.xsd
new file mode 100644
index 000000000..b5d9ac100
--- /dev/null
+++ b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-3.2.xsd
@@ -0,0 +1,247 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-4.0.xsd b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-4.0.xsd
new file mode 100644
index 000000000..ebd5c8df9
--- /dev/null
+++ b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx-4.0.xsd
@@ -0,0 +1,247 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx.gif b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx.gif
new file mode 100644
index 000000000..20ed1f9a4
Binary files /dev/null and b/fine-spring/resources/META-INF/com/fr/third/springframework/transaction/config/spring-tx.gif differ
diff --git a/fine-spring/resources/META-INF/fine-spring.handlers b/fine-spring/resources/META-INF/fine-spring.handlers
index f0c858aba..13996c1e4 100644
--- a/fine-spring/resources/META-INF/fine-spring.handlers
+++ b/fine-spring/resources/META-INF/fine-spring.handlers
@@ -5,3 +5,6 @@ http\://www.springframework.org/schema/task=com.fr.third.springframework.schedul
http\://www.springframework.org/schema/cache=com.fr.third.springframework.cache.config.CacheNamespaceHandler
http\://www.springframework.org/schema/mvc=com.fr.third.springframework.web.servlet.config.MvcNamespaceHandler
+http\://www.springframework.org/schema/tx=com.fr.third.springframework.transaction.config.TxNamespaceHandler
+
+http\://www.springframework.org/schema/ldap=com.fr.third.springframework.ldap.config.LdapNamespaceHandler
\ No newline at end of file
diff --git a/fine-spring/resources/META-INF/fine-spring.schemas b/fine-spring/resources/META-INF/fine-spring.schemas
index dc698ce0d..a908c9b79 100644
--- a/fine-spring/resources/META-INF/fine-spring.schemas
+++ b/fine-spring/resources/META-INF/fine-spring.schemas
@@ -53,3 +53,14 @@ http\://www.springframework.org/schema/tool/spring-tool-3.1.xsd=com/fr/third/spr
http\://www.springframework.org/schema/tool/spring-tool-3.2.xsd=com/fr/third/springframework/beans/factory/xml/spring-tool-3.2.xsd
http\://www.springframework.org/schema/tool/spring-tool-4.0.xsd=com/fr/third/springframework/beans/factory/xml/spring-tool-4.0.xsd
http\://www.springframework.org/schema/tool/spring-tool.xsd=com/fr/third/springframework/beans/factory/xml/spring-tool-4.0.xsd
+
+http\://www.springframework.org/schema/tx/spring-tx-2.0.xsd=com/fr/third/springframework/transaction/config/spring-tx-2.0.xsd
+http\://www.springframework.org/schema/tx/spring-tx-2.5.xsd=com/fr/third/springframework/transaction/config/spring-tx-2.5.xsd
+http\://www.springframework.org/schema/tx/spring-tx-3.0.xsd=com/fr/third/springframework/transaction/config/spring-tx-3.0.xsd
+http\://www.springframework.org/schema/tx/spring-tx-3.1.xsd=com/fr/third/springframework/transaction/config/spring-tx-3.1.xsd
+http\://www.springframework.org/schema/tx/spring-tx-3.2.xsd=com/fr/third/springframework/transaction/config/spring-tx-3.2.xsd
+http\://www.springframework.org/schema/tx/spring-tx-4.0.xsd=com/fr/third/springframework/transaction/config/spring-tx-4.0.xsd
+http\://www.springframework.org/schema/tx/spring-tx.xsd=com/fr/third/springframework/transaction/config/spring-tx-4.0.xsd
+
+http\://www.springframework.org/schema/ldap/spring-ldap.xsd=com/fr/third/springframework/ldap/config/spring-ldap-2.0.xsd
+http\://www.springframework.org/schema/ldap/spring-ldap-2.0.xsd=com/fr/third/springframework/ldap/config/spring-ldap-2.0.xsd
\ No newline at end of file
diff --git a/fine-spring/resources/META-INF/fine-spring.tooling b/fine-spring/resources/META-INF/fine-spring.tooling
index 9c620f349..c8e96c805 100644
--- a/fine-spring/resources/META-INF/fine-spring.tooling
+++ b/fine-spring/resources/META-INF/fine-spring.tooling
@@ -22,3 +22,9 @@ http\://www.springframework.org/schema/lang@icon=org/springframework/scripting/c
http\://www.springframework.org/schema/cache@name=cache Namespace
http\://www.springframework.org/schema/cache@prefix=cache
http\://www.springframework.org/schema/cache@icon=org/springframework/cache/config/spring-cache.gif
+
+# Tooling related information for the tx namespace
+http\://www.springframework.org/schema/tx@name=tx Namespace
+http\://www.springframework.org/schema/tx@prefix=tx
+http\://www.springframework.org/schema/tx@icon=org/springframework/transaction/config/spring-tx.gif
+
diff --git a/fine-spring/src/com/fr/third/springframework/LdapDataEntry.java b/fine-spring/src/com/fr/third/springframework/LdapDataEntry.java
new file mode 100644
index 000000000..1ce6454d1
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/LdapDataEntry.java
@@ -0,0 +1,212 @@
+package com.fr.third.springframework;
+
+import javax.naming.Name;
+import javax.naming.directory.Attributes;
+import java.util.SortedSet;
+
+/**
+ * Common data access methods for entries in an LDAP tree.
+ *
+ * @author Mattias Hellborg Arthursson
+ * @since 2.0
+ */
+public interface LdapDataEntry {
+ /**
+ * Get the value of a String attribute. If more than one attribute value
+ * exists for the specified attribute, only the first one will be returned.
+ * If an attribute has no value, null will be returned.
+ *
+ * @param name name of the attribute.
+ * @return the value of the attribute if it exists, or null if
+ * the attribute doesn't exist or if it exists but with no value.
+ * @throws ClassCastException if the value of the entry is not a String.
+ */
+ String getStringAttribute(String name);
+
+ /**
+ * Get the value of an Object attribute. If more than one attribute value
+ * exists for the specified attribute, only the first one will be returned.
+ * If an attribute has no value, null will be returned.
+ *
+ * @param name name of the attribute.
+ * @return the attribute value as an object if it exists, or
+ * null if the attribute doesn't exist or if it exists but with
+ * no value.
+ */
+ Object getObjectAttribute(String name);
+
+ /**
+ * Check if an Object attribute exists, regardless of whether it has a value
+ * or not.
+ *
+ * @param name name of the attribute
+ * @return true if the attribute exists, false
+ * otherwise
+ */
+ boolean attributeExists(String name);
+
+ /**
+ * Set the with the name name to the value.
+ * If the value is a {@link Name} instance, equality for Distinguished
+ * Names will be used for calculating attribute modifications.
+ *
+ * @param name name of the attribute.
+ * @param value value to set the attribute to.
+ * @throws IllegalArgumentException if the value is a {@link Name} instance
+ * and one or several of the currently present attribute values is not
+ * {@link Name} instances or Strings representing valid Distinguished Names.
+ */
+ void setAttributeValue(String name, Object value);
+
+ /**
+ * Sets a multivalue attribute, disregarding the order of the values.
+ *
+ * If value is null or value.length == 0 then the attribute will be removed.
+ *
+ * If update mode, changes will be made only if the array has more or less
+ * objects or if one or more object has changed. Reordering the objects will
+ * not cause an update.
+ *
+ * If the values are {@link Name} instances, equality for Distinguished
+ * Names will be used for calculating attribute modifications.
+ *
+ * @param name The id of the attribute.
+ * @param values Attribute values.
+ * @throws IllegalArgumentException if value is a {@link Name} instance
+ * and one or several of the currently present attribute values is not
+ * {@link Name} instances or Strings representing valid Distinguished Names.
+ */
+ void setAttributeValues(String name, Object[] values);
+
+ /**
+ * Sets a multivalue attribute.
+ *
+ * If value is null or value.length == 0 then the attribute will be removed.
+ *
+ * If update mode, changes will be made if the array has more or less
+ * objects or if one or more string has changed.
+ *
+ * Reordering the objects will only cause an update if orderMatters is set
+ * to true.
+ *
+ * If the values are {@link Name} instances, equality for Distinguished
+ * Names will be used for calculating attribute modifications.
+ * @param name The id of the attribute.
+ * @param values Attribute values.
+ * @param orderMatters If true, it will be changed even if data
+ * was just reordered.
+ * @throws IllegalArgumentException if value is a {@link Name} instance
+ * and one or several of the currently present attribute values is not
+ * {@link Name} instances or Strings representing valid Distinguished Names.
+ */
+ void setAttributeValues(String name, Object[] values, boolean orderMatters);
+
+ /**
+ * Add a value to the Attribute with the specified name. If the Attribute
+ * doesn't exist it will be created. This method makes sure that the there
+ * will be no duplicates of an added value - it the value exists it will not
+ * be added again.
+ *
+ * If the value is a {@link Name} instance, equality for Distinguished
+ * Names will be used for calculating attribute modifications.
+ *
+ * @param name the name of the Attribute to which the specified value should
+ * be added.
+ * @param value the Attribute value to add.
+ * @throws IllegalArgumentException if value is a {@link Name} instance
+ * and one or several of the currently present attribute values is not
+ * {@link Name} instances or Strings representing valid Distinguished Names.
+ */
+ void addAttributeValue(String name, Object value);
+
+ /**
+ * Add a value to the Attribute with the specified name. If the Attribute
+ * doesn't exist it will be created. The addIfDuplicateExists
+ * parameter controls the handling of duplicates. It false,
+ * this method makes sure that the there will be no duplicates of an added
+ * value - it the value exists it will not be added again.
+ *
+ * If the value is a {@link Name} instance, equality for Distinguished
+ * Names will be used for calculating attribute modifications.
+ *
+ * @param name the name of the Attribute to which the specified value should
+ * be added.
+ * @param value the Attribute value to add.
+ * @param addIfDuplicateExists true will add the value
+ * regardless of whether there is an identical value already, allowing for
+ * duplicate attribute values; false will not add the value if
+ * it already exists.
+ * @throws IllegalArgumentException if value is a {@link Name} instance
+ * and one or several of the currently present attribute values is not
+ * {@link Name} instances or Strings representing valid Distinguished Names.
+ */
+ void addAttributeValue(String name, Object value,
+ boolean addIfDuplicateExists);
+
+ /**
+ * Remove a value from the Attribute with the specified name. If the
+ * Attribute doesn't exist, do nothing.
+ *
+ * If the value is a {@link Name} instance, equality for Distinguished
+ * Names will be used for calculating attribute modifications.
+ *
+ * @param name the name of the Attribute from which the specified value
+ * should be removed.
+ * @param value the value to remove.
+ * @throws IllegalArgumentException if value is a {@link Name} instance
+ * and one or several of the currently present attribute values is not
+ * {@link Name} instances or Strings representing valid Distinguished Names.
+ */
+ void removeAttributeValue(String name, Object value);
+
+ /**
+ * Get all values of a String attribute.
+ *
+ * @param name name of the attribute.
+ * @return a (possibly empty) array containing all registered values of the
+ * attribute as Strings if the attribute is defined or null
+ * otherwise.
+ * @throws IllegalArgumentException if any of the attribute values is not a
+ * String.
+ */
+ String[] getStringAttributes(String name);
+
+ /**
+ * Get all values of an Object attribute.
+ *
+ * @param name name of the attribute.
+ * @return a (possibly empty) array containing all registered values of the
+ * attribute if the attribute is defined or null otherwise.
+ * @since 1.3
+ */
+ Object[] getObjectAttributes(String name);
+
+ /**
+ * Get all String values of the attribute as a SortedSet.
+ *
+ * @param name name of the attribute.
+ * @return a SortedSet containing all values of the attribute,
+ * or null if the attribute does not exist.
+ * @throws IllegalArgumentException if one of the found attribute values cannot be cast to a String.
+ */
+ SortedSet getAttributeSortedStringSet(String name);
+
+ /**
+ * Returns the DN relative to the base path.
+ * NB: as of version 2.0 the returned name will be an LdapName instance.
+ *
+ * @return The distinguished name of the current context.
+ *
+ * @see com.fr.third.springframework.ldap.core.DirContextAdapter#getNameInNamespace()
+ */
+ Name getDn();
+
+
+ /**
+ * Get all the Attributes.
+ *
+ * @return all the Attributes.
+ * @since 1.3
+ */
+ Attributes getAttributes();
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/CannotAcquireLockException.java b/fine-spring/src/com/fr/third/springframework/dao/CannotAcquireLockException.java
new file mode 100644
index 000000000..5bca784b1
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/CannotAcquireLockException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception thrown on failure to aquire a lock during an update,
+ * for example during a "select for update" statement.
+ *
+ * @author Rod Johnson
+ */
+@SuppressWarnings("serial")
+public class CannotAcquireLockException extends PessimisticLockingFailureException {
+
+ /**
+ * Constructor for CannotAcquireLockException.
+ * @param msg the detail message
+ */
+ public CannotAcquireLockException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for CannotAcquireLockException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public CannotAcquireLockException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/CannotSerializeTransactionException.java b/fine-spring/src/com/fr/third/springframework/dao/CannotSerializeTransactionException.java
new file mode 100644
index 000000000..b87940c8f
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/CannotSerializeTransactionException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception thrown on failure to complete a transaction in serialized mode
+ * due to update conflicts.
+ *
+ * @author Rod Johnson
+ */
+@SuppressWarnings("serial")
+public class CannotSerializeTransactionException extends PessimisticLockingFailureException {
+
+ /**
+ * Constructor for CannotSerializeTransactionException.
+ * @param msg the detail message
+ */
+ public CannotSerializeTransactionException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for CannotSerializeTransactionException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public CannotSerializeTransactionException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/CleanupFailureDataAccessException.java b/fine-spring/src/com/fr/third/springframework/dao/CleanupFailureDataAccessException.java
new file mode 100644
index 000000000..3ec16417d
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/CleanupFailureDataAccessException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception thrown when we couldn't cleanup after a data access operation,
+ * but the actual operation went OK.
+ *
+ *
For example, this exception or a subclass might be thrown if a JDBC
+ * Connection couldn't be closed after it had been used successfully.
+ *
+ *
Note that data access code might perform resources cleanup in a
+ * finally block and therefore log cleanup failure rather than rethrow it,
+ * to keep the original data access exception, if any.
+ *
+ * @author Rod Johnson
+ */
+@SuppressWarnings("serial")
+public class CleanupFailureDataAccessException extends NonTransientDataAccessException {
+
+ /**
+ * Constructor for CleanupFailureDataAccessException.
+ * @param msg the detail message
+ * @param cause the root cause from the underlying data access API,
+ * such as JDBC
+ */
+ public CleanupFailureDataAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/ConcurrencyFailureException.java b/fine-spring/src/com/fr/third/springframework/dao/ConcurrencyFailureException.java
new file mode 100644
index 000000000..fb15b5274
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/ConcurrencyFailureException.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception thrown on concurrency failure.
+ *
+ *
This exception should be subclassed to indicate the type of failure:
+ * optimistic locking, failure to acquire lock, etc.
+ *
+ * @author Thomas Risberg
+ * @since 1.1
+ * @see OptimisticLockingFailureException
+ * @see PessimisticLockingFailureException
+ * @see CannotAcquireLockException
+ * @see DeadlockLoserDataAccessException
+ */
+@SuppressWarnings("serial")
+public class ConcurrencyFailureException extends TransientDataAccessException {
+
+ /**
+ * Constructor for ConcurrencyFailureException.
+ * @param msg the detail message
+ */
+ public ConcurrencyFailureException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for ConcurrencyFailureException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public ConcurrencyFailureException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/DataAccessException.java b/fine-spring/src/com/fr/third/springframework/dao/DataAccessException.java
new file mode 100644
index 000000000..183599e19
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/DataAccessException.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+import com.fr.third.springframework.core.NestedRuntimeException;
+
+/**
+ * Root of the hierarchy of data access exceptions discussed in
+ * Expert One-On-One J2EE Design and Development.
+ * Please see Chapter 9 of this book for detailed discussion of the
+ * motivation for this package.
+ *
+ *
This exception hierarchy aims to let user code find and handle the
+ * kind of error encountered without knowing the details of the particular
+ * data access API in use (e.g. JDBC). Thus it is possible to react to an
+ * optimistic locking failure without knowing that JDBC is being used.
+ *
+ *
As this class is a runtime exception, there is no need for user code
+ * to catch it or subclasses if any error is to be considered fatal
+ * (the usual case).
+ *
+ * @author Rod Johnson
+ */
+@SuppressWarnings("serial")
+public abstract class DataAccessException extends NestedRuntimeException {
+
+ /**
+ * Constructor for DataAccessException.
+ * @param msg the detail message
+ */
+ public DataAccessException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for DataAccessException.
+ * @param msg the detail message
+ * @param cause the root cause (usually from using a underlying
+ * data access API such as JDBC)
+ */
+ public DataAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/DataAccessResourceFailureException.java b/fine-spring/src/com/fr/third/springframework/dao/DataAccessResourceFailureException.java
new file mode 100644
index 000000000..b832c2f2f
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/DataAccessResourceFailureException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Data access exception thrown when a resource fails completely:
+ * for example, if we can't connect to a database using JDBC.
+ *
+ * @author Rod Johnson
+ * @author Thomas Risberg
+ */
+@SuppressWarnings("serial")
+public class DataAccessResourceFailureException extends NonTransientDataAccessResourceException {
+
+ /**
+ * Constructor for DataAccessResourceFailureException.
+ * @param msg the detail message
+ */
+ public DataAccessResourceFailureException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for DataAccessResourceFailureException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public DataAccessResourceFailureException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/DataIntegrityViolationException.java b/fine-spring/src/com/fr/third/springframework/dao/DataIntegrityViolationException.java
new file mode 100644
index 000000000..96978ee1a
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/DataIntegrityViolationException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception thrown when an attempt to insert or update data
+ * results in violation of an integrity constraint. Note that this
+ * is not purely a relational concept; unique primary keys are
+ * required by most database types.
+ *
+ * @author Rod Johnson
+ */
+@SuppressWarnings("serial")
+public class DataIntegrityViolationException extends NonTransientDataAccessException {
+
+ /**
+ * Constructor for DataIntegrityViolationException.
+ * @param msg the detail message
+ */
+ public DataIntegrityViolationException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for DataIntegrityViolationException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public DataIntegrityViolationException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/DataRetrievalFailureException.java b/fine-spring/src/com/fr/third/springframework/dao/DataRetrievalFailureException.java
new file mode 100644
index 000000000..a7b233ab7
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/DataRetrievalFailureException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception thrown if certain expected data could not be retrieved, e.g.
+ * when looking up specific data via a known identifier. This exception
+ * will be thrown either by O/R mapping tools or by DAO implementations.
+ *
+ * @author Juergen Hoeller
+ * @since 13.10.2003
+ */
+@SuppressWarnings("serial")
+public class DataRetrievalFailureException extends NonTransientDataAccessException {
+
+ /**
+ * Constructor for DataRetrievalFailureException.
+ * @param msg the detail message
+ */
+ public DataRetrievalFailureException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for DataRetrievalFailureException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public DataRetrievalFailureException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/DeadlockLoserDataAccessException.java b/fine-spring/src/com/fr/third/springframework/dao/DeadlockLoserDataAccessException.java
new file mode 100644
index 000000000..e6361fcd3
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/DeadlockLoserDataAccessException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Generic exception thrown when the current process was
+ * a deadlock loser, and its transaction rolled back.
+ *
+ * @author Rod Johnson
+ */
+@SuppressWarnings("serial")
+public class DeadlockLoserDataAccessException extends PessimisticLockingFailureException {
+
+ /**
+ * Constructor for DeadlockLoserDataAccessException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public DeadlockLoserDataAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/DuplicateKeyException.java b/fine-spring/src/com/fr/third/springframework/dao/DuplicateKeyException.java
new file mode 100644
index 000000000..9b4270fcd
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/DuplicateKeyException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception thrown when an attempt to insert or update data
+ * results in violation of an primary key or unique constraint.
+ * Note that this is not necessarily a purely relational concept;
+ * unique primary keys are required by most database types.
+ *
+ * @author Thomas Risberg
+ */
+@SuppressWarnings("serial")
+public class DuplicateKeyException extends DataIntegrityViolationException {
+
+ /**
+ * Constructor for DuplicateKeyException.
+ * @param msg the detail message
+ */
+ public DuplicateKeyException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for DuplicateKeyException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public DuplicateKeyException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/EmptyResultDataAccessException.java b/fine-spring/src/com/fr/third/springframework/dao/EmptyResultDataAccessException.java
new file mode 100644
index 000000000..1837cfaf3
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/EmptyResultDataAccessException.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Data access exception thrown when a result was expected to have at least
+ * one row (or element) but zero rows (or elements) were actually returned.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see IncorrectResultSizeDataAccessException
+ */
+@SuppressWarnings("serial")
+public class EmptyResultDataAccessException extends IncorrectResultSizeDataAccessException {
+
+ /**
+ * Constructor for EmptyResultDataAccessException.
+ * @param expectedSize the expected result size
+ */
+ public EmptyResultDataAccessException(int expectedSize) {
+ super(expectedSize, 0);
+ }
+
+ /**
+ * Constructor for EmptyResultDataAccessException.
+ * @param msg the detail message
+ * @param expectedSize the expected result size
+ */
+ public EmptyResultDataAccessException(String msg, int expectedSize) {
+ super(msg, expectedSize, 0);
+ }
+
+ /**
+ * Constructor for EmptyResultDataAccessException.
+ * @param msg the detail message
+ * @param expectedSize the expected result size
+ * @param ex the wrapped exception
+ */
+ public EmptyResultDataAccessException(String msg, int expectedSize, Throwable ex) {
+ super(msg, expectedSize, 0, ex);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/IncorrectResultSizeDataAccessException.java b/fine-spring/src/com/fr/third/springframework/dao/IncorrectResultSizeDataAccessException.java
new file mode 100644
index 000000000..ef47fa1f4
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/IncorrectResultSizeDataAccessException.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Data access exception thrown when a result was not of the expected size,
+ * for example when expecting a single row but getting 0 or more than 1 rows.
+ *
+ * @author Juergen Hoeller
+ * @author Chris Beams
+ * @since 1.0.2
+ * @see EmptyResultDataAccessException
+ */
+@SuppressWarnings("serial")
+public class IncorrectResultSizeDataAccessException extends DataRetrievalFailureException {
+
+ private int expectedSize;
+
+ private int actualSize;
+
+
+ /**
+ * Constructor for IncorrectResultSizeDataAccessException.
+ * @param expectedSize the expected result size
+ */
+ public IncorrectResultSizeDataAccessException(int expectedSize) {
+ super("Incorrect result size: expected " + expectedSize);
+ this.expectedSize = expectedSize;
+ this.actualSize = -1;
+ }
+
+ /**
+ * Constructor for IncorrectResultSizeDataAccessException.
+ * @param expectedSize the expected result size
+ * @param actualSize the actual result size (or -1 if unknown)
+ */
+ public IncorrectResultSizeDataAccessException(int expectedSize, int actualSize) {
+ super("Incorrect result size: expected " + expectedSize + ", actual " + actualSize);
+ this.expectedSize = expectedSize;
+ this.actualSize = actualSize;
+ }
+
+ /**
+ * Constructor for IncorrectResultSizeDataAccessException.
+ * @param msg the detail message
+ * @param expectedSize the expected result size
+ */
+ public IncorrectResultSizeDataAccessException(String msg, int expectedSize) {
+ super(msg);
+ this.expectedSize = expectedSize;
+ this.actualSize = -1;
+ }
+
+ /**
+ * Constructor for IncorrectResultSizeDataAccessException.
+ * @param msg the detail message
+ * @param expectedSize the expected result size
+ * @param ex the wrapped exception
+ */
+ public IncorrectResultSizeDataAccessException(String msg, int expectedSize, Throwable ex) {
+ super(msg, ex);
+ this.expectedSize = expectedSize;
+ this.actualSize = -1;
+ }
+
+ /**
+ * Constructor for IncorrectResultSizeDataAccessException.
+ * @param msg the detail message
+ * @param expectedSize the expected result size
+ * @param actualSize the actual result size (or -1 if unknown)
+ */
+ public IncorrectResultSizeDataAccessException(String msg, int expectedSize, int actualSize) {
+ super(msg);
+ this.expectedSize = expectedSize;
+ this.actualSize = actualSize;
+ }
+
+ /**
+ * Constructor for IncorrectResultSizeDataAccessException.
+ * @param msg the detail message
+ * @param expectedSize the expected result size
+ * @param actualSize the actual result size (or -1 if unknown)
+ * @param ex the wrapped exception
+ */
+ public IncorrectResultSizeDataAccessException(String msg, int expectedSize, int actualSize, Throwable ex) {
+ super(msg, ex);
+ this.expectedSize = expectedSize;
+ this.actualSize = actualSize;
+ }
+
+
+ /**
+ * Return the expected result size.
+ */
+ public int getExpectedSize() {
+ return this.expectedSize;
+ }
+
+ /**
+ * Return the actual result size (or -1 if unknown).
+ */
+ public int getActualSize() {
+ return this.actualSize;
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/IncorrectUpdateSemanticsDataAccessException.java b/fine-spring/src/com/fr/third/springframework/dao/IncorrectUpdateSemanticsDataAccessException.java
new file mode 100644
index 000000000..21714916c
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/IncorrectUpdateSemanticsDataAccessException.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Data access exception thrown when something unintended appears to have
+ * happened with an update, but the transaction hasn't already been rolled back.
+ * Thrown, for example, when we wanted to update 1 row in an RDBMS but actually
+ * updated 3.
+ *
+ * @author Rod Johnson
+ */
+@SuppressWarnings("serial")
+public class IncorrectUpdateSemanticsDataAccessException extends InvalidDataAccessResourceUsageException {
+
+ /**
+ * Constructor for IncorrectUpdateSemanticsDataAccessException.
+ * @param msg the detail message
+ */
+ public IncorrectUpdateSemanticsDataAccessException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for IncorrectUpdateSemanticsDataAccessException.
+ * @param msg the detail message
+ * @param cause the root cause from the underlying API, such as JDBC
+ */
+ public IncorrectUpdateSemanticsDataAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ /**
+ * Return whether data was updated.
+ * If this method returns false, there's nothing to roll back.
+ *
The default implementation always returns true.
+ * This can be overridden in subclasses.
+ */
+ public boolean wasDataUpdated() {
+ return true;
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/InvalidDataAccessApiUsageException.java b/fine-spring/src/com/fr/third/springframework/dao/InvalidDataAccessApiUsageException.java
new file mode 100644
index 000000000..8d254e8f3
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/InvalidDataAccessApiUsageException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception thrown on incorrect usage of the API, such as failing to
+ * "compile" a query object that needed compilation before execution.
+ *
+ *
This represents a problem in our Java data access framework,
+ * not the underlying data access infrastructure.
+ *
+ * @author Rod Johnson
+ */
+@SuppressWarnings("serial")
+public class InvalidDataAccessApiUsageException extends NonTransientDataAccessException {
+
+ /**
+ * Constructor for InvalidDataAccessApiUsageException.
+ * @param msg the detail message
+ */
+ public InvalidDataAccessApiUsageException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for InvalidDataAccessApiUsageException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public InvalidDataAccessApiUsageException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/InvalidDataAccessResourceUsageException.java b/fine-spring/src/com/fr/third/springframework/dao/InvalidDataAccessResourceUsageException.java
new file mode 100644
index 000000000..d34ac12bb
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/InvalidDataAccessResourceUsageException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Root for exceptions thrown when we use a data access resource incorrectly.
+ * Thrown for example on specifying bad SQL when using a RDBMS.
+ * Resource-specific subclasses are supplied by concrete data access packages.
+ *
+ * @author Rod Johnson
+ */
+@SuppressWarnings("serial")
+public class InvalidDataAccessResourceUsageException extends NonTransientDataAccessException {
+
+ /**
+ * Constructor for InvalidDataAccessResourceUsageException.
+ * @param msg the detail message
+ */
+ public InvalidDataAccessResourceUsageException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for InvalidDataAccessResourceUsageException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public InvalidDataAccessResourceUsageException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/NonTransientDataAccessException.java b/fine-spring/src/com/fr/third/springframework/dao/NonTransientDataAccessException.java
new file mode 100644
index 000000000..68c56a4a2
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/NonTransientDataAccessException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Root of the hierarchy of data access exceptions that are considered non-transient -
+ * where a retry of the same operation would fail unless the cause of the Exception
+ * is corrected.
+ *
+ * @author Thomas Risberg
+ * @since 2.5
+ * @see java.sql.SQLNonTransientException
+ */
+@SuppressWarnings("serial")
+public abstract class NonTransientDataAccessException extends DataAccessException {
+
+ /**
+ * Constructor for NonTransientDataAccessException.
+ * @param msg the detail message
+ */
+ public NonTransientDataAccessException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for NonTransientDataAccessException.
+ * @param msg the detail message
+ * @param cause the root cause (usually from using a underlying
+ * data access API such as JDBC)
+ */
+ public NonTransientDataAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/NonTransientDataAccessResourceException.java b/fine-spring/src/com/fr/third/springframework/dao/NonTransientDataAccessResourceException.java
new file mode 100644
index 000000000..32f8f23d8
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/NonTransientDataAccessResourceException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Data access exception thrown when a resource fails completely and the failure is permanent.
+ *
+ * @author Thomas Risberg
+ * @since 2.5
+ * @see java.sql.SQLNonTransientConnectionException
+ */
+@SuppressWarnings("serial")
+public class NonTransientDataAccessResourceException extends NonTransientDataAccessException {
+
+ /**
+ * Constructor for NonTransientDataAccessResourceException.
+ * @param msg the detail message
+ */
+ public NonTransientDataAccessResourceException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for NonTransientDataAccessResourceException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public NonTransientDataAccessResourceException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/OptimisticLockingFailureException.java b/fine-spring/src/com/fr/third/springframework/dao/OptimisticLockingFailureException.java
new file mode 100644
index 000000000..656942938
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/OptimisticLockingFailureException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception thrown on an optimistic locking violation.
+ *
+ *
This exception will be thrown either by O/R mapping tools
+ * or by custom DAO implementations. Optimistic locking failure
+ * is typically not detected by the database itself.
+ *
+ * @author Rod Johnson
+ * @see PessimisticLockingFailureException
+ */
+@SuppressWarnings("serial")
+public class OptimisticLockingFailureException extends ConcurrencyFailureException {
+
+ /**
+ * Constructor for OptimisticLockingFailureException.
+ * @param msg the detail message
+ */
+ public OptimisticLockingFailureException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for OptimisticLockingFailureException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public OptimisticLockingFailureException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/PermissionDeniedDataAccessException.java b/fine-spring/src/com/fr/third/springframework/dao/PermissionDeniedDataAccessException.java
new file mode 100644
index 000000000..500ec4247
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/PermissionDeniedDataAccessException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception thrown when the underlying resource denied a permission
+ * to access a specific element, such as a specific database table.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+@SuppressWarnings("serial")
+public class PermissionDeniedDataAccessException extends NonTransientDataAccessException {
+
+ /**
+ * Constructor for PermissionDeniedDataAccessException.
+ * @param msg the detail message
+ * @param cause the root cause from the underlying data access API,
+ * such as JDBC
+ */
+ public PermissionDeniedDataAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/PessimisticLockingFailureException.java b/fine-spring/src/com/fr/third/springframework/dao/PessimisticLockingFailureException.java
new file mode 100644
index 000000000..c511ad268
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/PessimisticLockingFailureException.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception thrown on a pessimistic locking violation.
+ * Thrown by Spring's SQLException translation mechanism
+ * if a corresponding database error is encountered.
+ *
+ *
Serves as superclass for more specific exceptions, like
+ * CannotAcquireLockException and DeadlockLoserDataAccessException.
+ *
+ * @author Thomas Risberg
+ * @since 1.2
+ * @see CannotAcquireLockException
+ * @see DeadlockLoserDataAccessException
+ * @see OptimisticLockingFailureException
+ */
+@SuppressWarnings("serial")
+public class PessimisticLockingFailureException extends ConcurrencyFailureException {
+
+ /**
+ * Constructor for PessimisticLockingFailureException.
+ * @param msg the detail message
+ */
+ public PessimisticLockingFailureException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for PessimisticLockingFailureException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public PessimisticLockingFailureException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/QueryTimeoutException.java b/fine-spring/src/com/fr/third/springframework/dao/QueryTimeoutException.java
new file mode 100644
index 000000000..14fd55558
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/QueryTimeoutException.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception to be thrown on a query timeout. This could have different causes depending on
+ * the database API in use but most likely thrown after the database interrupts or stops
+ * the processing of a query before it has completed.
+ *
+ *
This exception can be thrown by user code trapping the native database exception or
+ * by exception translation.
+ *
+ * @author Thomas Risberg
+ * @since 3.1
+ */
+@SuppressWarnings("serial")
+public class QueryTimeoutException extends TransientDataAccessException {
+
+ /**
+ * Constructor for QueryTimeoutException.
+ * @param msg the detail message
+ */
+ public QueryTimeoutException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for QueryTimeoutException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public QueryTimeoutException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/RecoverableDataAccessException.java b/fine-spring/src/com/fr/third/springframework/dao/RecoverableDataAccessException.java
new file mode 100644
index 000000000..641c80112
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/RecoverableDataAccessException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Data access exception thrown when a previously failed operation might be able
+ * to succeed if the application performs some recovery steps and retries the entire
+ * transaction or in the case of a distributed transaction, the transaction branch.
+ * At a minimum, the recovery operation must include closing the current connection
+ * and getting a new connection.
+ *
+ * @author Thomas Risberg
+ * @since 2.5
+ * @see java.sql.SQLRecoverableException
+ */
+@SuppressWarnings("serial")
+public class RecoverableDataAccessException extends DataAccessException {
+
+ /**
+ * Constructor for RecoverableDataAccessException.
+ * @param msg the detail message
+ */
+ public RecoverableDataAccessException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for RecoverableDataAccessException.
+ * @param msg the detail message
+ * @param cause the root cause (usually from using a underlying
+ * data access API such as JDBC)
+ */
+ public RecoverableDataAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/TransientDataAccessException.java b/fine-spring/src/com/fr/third/springframework/dao/TransientDataAccessException.java
new file mode 100644
index 000000000..d5665f37b
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/TransientDataAccessException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Root of the hierarchy of data access exceptions that are considered transient -
+ * where a previously failed operation might be able to succeed when the operation
+ * is retried without any intervention by application-level functionality.
+ *
+ * @author Thomas Risberg
+ * @since 2.5
+ * @see java.sql.SQLTransientException
+ */
+@SuppressWarnings("serial")
+public abstract class TransientDataAccessException extends DataAccessException {
+
+ /**
+ * Constructor for TransientDataAccessException.
+ * @param msg the detail message
+ */
+ public TransientDataAccessException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for TransientDataAccessException.
+ * @param msg the detail message
+ * @param cause the root cause (usually from using a underlying
+ * data access API such as JDBC)
+ */
+ public TransientDataAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/TransientDataAccessResourceException.java b/fine-spring/src/com/fr/third/springframework/dao/TransientDataAccessResourceException.java
new file mode 100644
index 000000000..2f0b47cc8
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/TransientDataAccessResourceException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Data access exception thrown when a resource fails temporarily
+ * and the operation can be retried.
+ *
+ * @author Thomas Risberg
+ * @since 2.5
+ * @see java.sql.SQLTransientConnectionException
+ */
+@SuppressWarnings("serial")
+public class TransientDataAccessResourceException extends TransientDataAccessException {
+
+ /**
+ * Constructor for TransientDataAccessResourceException.
+ * @param msg the detail message
+ */
+ public TransientDataAccessResourceException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for TransientDataAccessResourceException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public TransientDataAccessResourceException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/TypeMismatchDataAccessException.java b/fine-spring/src/com/fr/third/springframework/dao/TypeMismatchDataAccessException.java
new file mode 100644
index 000000000..4cbfdca20
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/TypeMismatchDataAccessException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Exception thrown on mismatch between Java type and database type:
+ * for example on an attempt to set an object of the wrong type
+ * in an RDBMS column.
+ *
+ * @author Rod Johnson
+ */
+@SuppressWarnings("serial")
+public class TypeMismatchDataAccessException extends InvalidDataAccessResourceUsageException {
+
+ /**
+ * Constructor for TypeMismatchDataAccessException.
+ * @param msg the detail message
+ */
+ public TypeMismatchDataAccessException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for TypeMismatchDataAccessException.
+ * @param msg the detail message
+ * @param cause the root cause from the data access API in use
+ */
+ public TypeMismatchDataAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/UncategorizedDataAccessException.java b/fine-spring/src/com/fr/third/springframework/dao/UncategorizedDataAccessException.java
new file mode 100644
index 000000000..40c2f69c1
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/UncategorizedDataAccessException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao;
+
+/**
+ * Normal superclass when we can't distinguish anything more specific
+ * than "something went wrong with the underlying resource": for example,
+ * a SQLException from JDBC we can't pinpoint more precisely.
+ *
+ * @author Rod Johnson
+ */
+@SuppressWarnings("serial")
+public abstract class UncategorizedDataAccessException extends NonTransientDataAccessException {
+
+ /**
+ * Constructor for UncategorizedDataAccessException.
+ * @param msg the detail message
+ * @param cause the exception thrown by underlying data access API
+ */
+ public UncategorizedDataAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/annotation/PersistenceExceptionTranslationAdvisor.java b/fine-spring/src/com/fr/third/springframework/dao/annotation/PersistenceExceptionTranslationAdvisor.java
new file mode 100644
index 000000000..c895b7652
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/annotation/PersistenceExceptionTranslationAdvisor.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao.annotation;
+
+import java.lang.annotation.Annotation;
+
+import org.aopalliance.aop.Advice;
+
+import com.fr.third.springframework.aop.Pointcut;
+import com.fr.third.springframework.aop.support.AbstractPointcutAdvisor;
+import com.fr.third.springframework.aop.support.annotation.AnnotationMatchingPointcut;
+import com.fr.third.springframework.beans.factory.ListableBeanFactory;
+import com.fr.third.springframework.dao.support.PersistenceExceptionTranslationInterceptor;
+import com.fr.third.springframework.dao.support.PersistenceExceptionTranslator;
+
+/**
+ * Spring AOP exception translation aspect for use at Repository or DAO layer level.
+ * Translates native persistence exceptions into Spring's DataAccessException hierarchy,
+ * based on a given PersistenceExceptionTranslator.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see com.fr.third.springframework.dao.DataAccessException
+ * @see com.fr.third.springframework.dao.support.PersistenceExceptionTranslator
+ */
+@SuppressWarnings("serial")
+public class PersistenceExceptionTranslationAdvisor extends AbstractPointcutAdvisor {
+
+ private final PersistenceExceptionTranslationInterceptor advice;
+
+ private final AnnotationMatchingPointcut pointcut;
+
+
+ /**
+ * Create a new PersistenceExceptionTranslationAdvisor.
+ * @param persistenceExceptionTranslator the PersistenceExceptionTranslator to use
+ * @param repositoryAnnotationType the annotation type to check for
+ */
+ public PersistenceExceptionTranslationAdvisor(
+ PersistenceExceptionTranslator persistenceExceptionTranslator,
+ Class extends Annotation> repositoryAnnotationType) {
+
+ this.advice = new PersistenceExceptionTranslationInterceptor(persistenceExceptionTranslator);
+ this.pointcut = new AnnotationMatchingPointcut(repositoryAnnotationType, true);
+ }
+
+ /**
+ * Create a new PersistenceExceptionTranslationAdvisor.
+ * @param beanFactory the ListableBeanFactory to obtaining all
+ * PersistenceExceptionTranslators from
+ * @param repositoryAnnotationType the annotation type to check for
+ */
+ PersistenceExceptionTranslationAdvisor(
+ ListableBeanFactory beanFactory, Class extends Annotation> repositoryAnnotationType) {
+
+ this.advice = new PersistenceExceptionTranslationInterceptor(beanFactory);
+ this.pointcut = new AnnotationMatchingPointcut(repositoryAnnotationType, true);
+ }
+
+
+ @Override
+ public Advice getAdvice() {
+ return this.advice;
+ }
+
+ @Override
+ public Pointcut getPointcut() {
+ return this.pointcut;
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessor.java b/fine-spring/src/com/fr/third/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessor.java
new file mode 100644
index 000000000..a0d65337a
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessor.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao.annotation;
+
+import java.lang.annotation.Annotation;
+
+import com.fr.third.springframework.aop.framework.AbstractAdvisingBeanPostProcessor;
+import com.fr.third.springframework.beans.factory.BeanFactory;
+import com.fr.third.springframework.beans.factory.BeanFactoryAware;
+import com.fr.third.springframework.beans.factory.ListableBeanFactory;
+import com.fr.third.springframework.stereotype.Repository;
+import com.fr.third.springframework.util.Assert;
+
+/**
+ * Bean post-processor that automatically applies persistence exception translation to any
+ * bean marked with Spring's @{@link com.fr.third.springframework.stereotype.Repository Repository}
+ * annotation, adding a corresponding {@link PersistenceExceptionTranslationAdvisor} to
+ * the exposed proxy (either an existing AOP proxy or a newly generated proxy that
+ * implements all of the target's interfaces).
+ *
+ *
Translates native resource exceptions to Spring's
+ * {@link com.fr.third.springframework.dao.DataAccessException DataAccessException} hierarchy.
+ * Autodetects beans that implement the
+ * {@link com.fr.third.springframework.dao.support.PersistenceExceptionTranslator
+ * PersistenceExceptionTranslator} interface, which are subsequently asked to translate
+ * candidate exceptions.
+ *
+
+ *
All of Spring's applicable resource factories (e.g.
+ * {@link com.fr.third.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean})
+ * implement the {@code PersistenceExceptionTranslator} interface out of the box.
+ * As a consequence, all that is usually needed to enable automatic exception
+ * translation is marking all affected beans (such as Repositories or DAOs)
+ * with the {@code @Repository} annotation, along with defining this post-processor
+ * as a bean in the application context.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see PersistenceExceptionTranslationAdvisor
+ * @see com.fr.third.springframework.stereotype.Repository
+ * @see com.fr.third.springframework.dao.DataAccessException
+ * @see com.fr.third.springframework.dao.support.PersistenceExceptionTranslator
+ */
+@SuppressWarnings("serial")
+public class PersistenceExceptionTranslationPostProcessor extends AbstractAdvisingBeanPostProcessor
+ implements BeanFactoryAware {
+
+ private Class extends Annotation> repositoryAnnotationType = Repository.class;
+
+
+ /**
+ * Set the 'repository' annotation type.
+ * The default repository annotation type is the {@link Repository} annotation.
+ *
This setter property exists so that developers can provide their own
+ * (non-Spring-specific) annotation type to indicate that a class has a
+ * repository role.
+ * @param repositoryAnnotationType the desired annotation type
+ */
+ public void setRepositoryAnnotationType(Class extends Annotation> repositoryAnnotationType) {
+ Assert.notNull(repositoryAnnotationType, "'repositoryAnnotationType' must not be null");
+ this.repositoryAnnotationType = repositoryAnnotationType;
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) {
+ if (!(beanFactory instanceof ListableBeanFactory)) {
+ throw new IllegalArgumentException(
+ "Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory");
+ }
+ this.advisor = new PersistenceExceptionTranslationAdvisor(
+ (ListableBeanFactory) beanFactory, this.repositoryAnnotationType);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/annotation/package-info.java b/fine-spring/src/com/fr/third/springframework/dao/annotation/package-info.java
new file mode 100644
index 000000000..b3f9edecf
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/annotation/package-info.java
@@ -0,0 +1,9 @@
+
+/**
+ *
+ * Annotation support for DAOs. Contains a bean post-processor for translating
+ * persistence exceptions based on a repository stereotype annotation.
+ *
+ */
+package com.fr.third.springframework.dao.annotation;
+
diff --git a/fine-spring/src/com/fr/third/springframework/dao/package-info.java b/fine-spring/src/com/fr/third/springframework/dao/package-info.java
new file mode 100644
index 000000000..13b7cbe50
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/package-info.java
@@ -0,0 +1,20 @@
+
+/**
+ *
+ * Exception hierarchy enabling sophisticated error handling independent
+ * of the data access approach in use. For example, when DAOs and data
+ * access frameworks use the exceptions in this package (and custom
+ * subclasses), calling code can detect and handle common problems such
+ * as deadlocks without being tied to a particular data access strategy,
+ * such as JDBC.
+ *
+ *
All these exceptions are unchecked, meaning that calling code can
+ * leave them uncaught and treat all data access exceptions as fatal.
+ *
+ *
The classes in this package are discussed in Chapter 9 of
+ * Expert One-On-One J2EE Design and Development
+ * by Rod Johnson (Wrox, 2002).
+ *
+ */
+package com.fr.third.springframework.dao;
+
diff --git a/fine-spring/src/com/fr/third/springframework/dao/support/ChainedPersistenceExceptionTranslator.java b/fine-spring/src/com/fr/third/springframework/dao/support/ChainedPersistenceExceptionTranslator.java
new file mode 100644
index 000000000..52ae95015
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/support/ChainedPersistenceExceptionTranslator.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao.support;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fr.third.springframework.dao.DataAccessException;
+import com.fr.third.springframework.util.Assert;
+
+/**
+ * Implementation of {@link PersistenceExceptionTranslator} that supports chaining,
+ * allowing the addition of PersistenceExceptionTranslator instances in order.
+ * Returns {@code non-null} on the first (if any) match.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+public class ChainedPersistenceExceptionTranslator implements PersistenceExceptionTranslator {
+
+ /** List of PersistenceExceptionTranslators */
+ private final List delegates = new ArrayList(4);
+
+
+ /**
+ * Add a PersistenceExceptionTranslator to the chained delegate list.
+ */
+ public final void addDelegate(PersistenceExceptionTranslator pet) {
+ Assert.notNull(pet, "PersistenceExceptionTranslator must not be null");
+ this.delegates.add(pet);
+ }
+
+ /**
+ * Return all registered PersistenceExceptionTranslator delegates (as array).
+ */
+ public final PersistenceExceptionTranslator[] getDelegates() {
+ return this.delegates.toArray(new PersistenceExceptionTranslator[this.delegates.size()]);
+ }
+
+
+ @Override
+ public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
+ for (PersistenceExceptionTranslator pet : this.delegates) {
+ DataAccessException translatedDex = pet.translateExceptionIfPossible(ex);
+ if (translatedDex != null) {
+ return translatedDex;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/support/DaoSupport.java b/fine-spring/src/com/fr/third/springframework/dao/support/DaoSupport.java
new file mode 100644
index 000000000..bc7e0b40c
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/support/DaoSupport.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao.support;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.fr.third.springframework.beans.factory.BeanInitializationException;
+import com.fr.third.springframework.beans.factory.InitializingBean;
+
+/**
+ * Generic base class for DAOs, defining template methods for DAO initialization.
+ *
+ *
Extended by Spring's specific DAO support classes, such as:
+ * JdbcDaoSupport, JdoDaoSupport, etc.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2.2
+ * @see com.fr.third.springframework.jdbc.core.support.JdbcDaoSupport
+ */
+public abstract class DaoSupport implements InitializingBean {
+
+ /** Logger available to subclasses */
+ protected final Log logger = LogFactory.getLog(getClass());
+
+
+ @Override
+ public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
+ // Let abstract subclasses check their configuration.
+ checkDaoConfig();
+
+ // Let concrete implementations initialize themselves.
+ try {
+ initDao();
+ }
+ catch (Exception ex) {
+ throw new BeanInitializationException("Initialization of DAO failed", ex);
+ }
+ }
+
+ /**
+ * Abstract subclasses must override this to check their configuration.
+ *
Implementors should be marked as {@code final} if concrete subclasses
+ * are not supposed to override this template method themselves.
+ * @throws IllegalArgumentException in case of illegal configuration
+ */
+ protected abstract void checkDaoConfig() throws IllegalArgumentException;
+
+ /**
+ * Concrete subclasses can override this for custom initialization behavior.
+ * Gets called after population of this instance's bean properties.
+ * @throws Exception if DAO initialization fails
+ * (will be rethrown as a BeanInitializationException)
+ * @see com.fr.third.springframework.beans.factory.BeanInitializationException
+ */
+ protected void initDao() throws Exception {
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/support/DataAccessUtils.java b/fine-spring/src/com/fr/third/springframework/dao/support/DataAccessUtils.java
new file mode 100644
index 000000000..9d7cdf4c7
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/support/DataAccessUtils.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao.support;
+
+import java.util.Collection;
+
+import com.fr.third.springframework.dao.DataAccessException;
+import com.fr.third.springframework.dao.EmptyResultDataAccessException;
+import com.fr.third.springframework.dao.IncorrectResultSizeDataAccessException;
+import com.fr.third.springframework.dao.TypeMismatchDataAccessException;
+import com.fr.third.springframework.util.Assert;
+import com.fr.third.springframework.util.CollectionUtils;
+import com.fr.third.springframework.util.NumberUtils;
+
+/**
+ * Miscellaneous utility methods for DAO implementations.
+ * Useful with any data access technology.
+ *
+ * @author Juergen Hoeller
+ * @since 1.0.2
+ */
+public abstract class DataAccessUtils {
+
+ /**
+ * Return a single result object from the given Collection.
+ *
Returns {@code null} if 0 result objects found;
+ * throws an exception if more than 1 element found.
+ * @param results the result Collection (can be {@code null})
+ * @return the single result object, or {@code null} if none
+ * @throws IncorrectResultSizeDataAccessException if more than one
+ * element has been found in the given Collection
+ */
+ public static T singleResult(Collection results) throws IncorrectResultSizeDataAccessException {
+ int size = (results != null ? results.size() : 0);
+ if (size == 0) {
+ return null;
+ }
+ if (results.size() > 1) {
+ throw new IncorrectResultSizeDataAccessException(1, size);
+ }
+ return results.iterator().next();
+ }
+
+ /**
+ * Return a single result object from the given Collection.
+ *
Throws an exception if 0 or more than 1 element found.
+ * @param results the result Collection (can be {@code null})
+ * @return the single result object
+ * @throws IncorrectResultSizeDataAccessException if more than one
+ * element has been found in the given Collection
+ * @throws EmptyResultDataAccessException if no element at all
+ * has been found in the given Collection
+ */
+ public static T requiredSingleResult(Collection results) throws IncorrectResultSizeDataAccessException {
+ int size = (results != null ? results.size() : 0);
+ if (size == 0) {
+ throw new EmptyResultDataAccessException(1);
+ }
+ if (results.size() > 1) {
+ throw new IncorrectResultSizeDataAccessException(1, size);
+ }
+ return results.iterator().next();
+ }
+
+ /**
+ * Return a unique result object from the given Collection.
+ *
Returns {@code null} if 0 result objects found;
+ * throws an exception if more than 1 instance found.
+ * @param results the result Collection (can be {@code null})
+ * @return the unique result object, or {@code null} if none
+ * @throws IncorrectResultSizeDataAccessException if more than one
+ * result object has been found in the given Collection
+ * @see com.fr.third.springframework.util.CollectionUtils#hasUniqueObject
+ */
+ public static T uniqueResult(Collection results) throws IncorrectResultSizeDataAccessException {
+ int size = (results != null ? results.size() : 0);
+ if (size == 0) {
+ return null;
+ }
+ if (!CollectionUtils.hasUniqueObject(results)) {
+ throw new IncorrectResultSizeDataAccessException(1, size);
+ }
+ return results.iterator().next();
+ }
+
+ /**
+ * Return a unique result object from the given Collection.
+ *
Throws an exception if 0 or more than 1 instance found.
+ * @param results the result Collection (can be {@code null})
+ * @return the unique result object
+ * @throws IncorrectResultSizeDataAccessException if more than one
+ * result object has been found in the given Collection
+ * @throws EmptyResultDataAccessException if no result object at all
+ * has been found in the given Collection
+ * @see com.fr.third.springframework.util.CollectionUtils#hasUniqueObject
+ */
+ public static T requiredUniqueResult(Collection results) throws IncorrectResultSizeDataAccessException {
+ int size = (results != null ? results.size() : 0);
+ if (size == 0) {
+ throw new EmptyResultDataAccessException(1);
+ }
+ if (!CollectionUtils.hasUniqueObject(results)) {
+ throw new IncorrectResultSizeDataAccessException(1, size);
+ }
+ return results.iterator().next();
+ }
+
+ /**
+ * Return a unique result object from the given Collection.
+ * Throws an exception if 0 or more than 1 result objects found,
+ * of if the unique result object is not convertable to the
+ * specified required type.
+ * @param results the result Collection (can be {@code null})
+ * @return the unique result object
+ * @throws IncorrectResultSizeDataAccessException if more than one
+ * result object has been found in the given Collection
+ * @throws EmptyResultDataAccessException if no result object
+ * at all has been found in the given Collection
+ * @throws TypeMismatchDataAccessException if the unique object does
+ * not match the specified required type
+ */
+ @SuppressWarnings("unchecked")
+ public static T objectResult(Collection> results, Class requiredType)
+ throws IncorrectResultSizeDataAccessException, TypeMismatchDataAccessException {
+
+ Object result = requiredUniqueResult(results);
+ if (requiredType != null && !requiredType.isInstance(result)) {
+ if (String.class.equals(requiredType)) {
+ result = result.toString();
+ }
+ else if (Number.class.isAssignableFrom(requiredType) && Number.class.isInstance(result)) {
+ try {
+ result = NumberUtils.convertNumberToTargetClass(((Number) result), (Class extends Number>) requiredType);
+ }
+ catch (IllegalArgumentException ex) {
+ throw new TypeMismatchDataAccessException(ex.getMessage());
+ }
+ }
+ else {
+ throw new TypeMismatchDataAccessException(
+ "Result object is of type [" + result.getClass().getName() +
+ "] and could not be converted to required type [" + requiredType.getName() + "]");
+ }
+ }
+ return (T) result;
+ }
+
+ /**
+ * Return a unique int result from the given Collection.
+ * Throws an exception if 0 or more than 1 result objects found,
+ * of if the unique result object is not convertable to an int.
+ * @param results the result Collection (can be {@code null})
+ * @return the unique int result
+ * @throws IncorrectResultSizeDataAccessException if more than one
+ * result object has been found in the given Collection
+ * @throws EmptyResultDataAccessException if no result object
+ * at all has been found in the given Collection
+ * @throws TypeMismatchDataAccessException if the unique object
+ * in the collection is not convertable to an int
+ */
+ public static int intResult(Collection> results)
+ throws IncorrectResultSizeDataAccessException, TypeMismatchDataAccessException {
+
+ return objectResult(results, Number.class).intValue();
+ }
+
+ /**
+ * Return a unique long result from the given Collection.
+ * Throws an exception if 0 or more than 1 result objects found,
+ * of if the unique result object is not convertable to a long.
+ * @param results the result Collection (can be {@code null})
+ * @return the unique long result
+ * @throws IncorrectResultSizeDataAccessException if more than one
+ * result object has been found in the given Collection
+ * @throws EmptyResultDataAccessException if no result object
+ * at all has been found in the given Collection
+ * @throws TypeMismatchDataAccessException if the unique object
+ * in the collection is not convertable to a long
+ */
+ public static long longResult(Collection> results)
+ throws IncorrectResultSizeDataAccessException, TypeMismatchDataAccessException {
+
+ return objectResult(results, Number.class).longValue();
+ }
+
+
+ /**
+ * Return a translated exception if this is appropriate,
+ * otherwise return the input exception.
+ * @param rawException exception we may wish to translate
+ * @param pet PersistenceExceptionTranslator to use to perform the translation
+ * @return a translated exception if translation is possible, or
+ * the raw exception if it is not
+ */
+ public static RuntimeException translateIfNecessary(
+ RuntimeException rawException, PersistenceExceptionTranslator pet) {
+
+ Assert.notNull(pet, "PersistenceExceptionTranslator must not be null");
+ DataAccessException dex = pet.translateExceptionIfPossible(rawException);
+ return (dex != null ? dex : rawException);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java b/fine-spring/src/com/fr/third/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java
new file mode 100644
index 000000000..21bbba2ec
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao.support;
+
+import java.util.Map;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import com.fr.third.springframework.beans.BeansException;
+import com.fr.third.springframework.beans.factory.BeanFactory;
+import com.fr.third.springframework.beans.factory.BeanFactoryAware;
+import com.fr.third.springframework.beans.factory.BeanFactoryUtils;
+import com.fr.third.springframework.beans.factory.InitializingBean;
+import com.fr.third.springframework.beans.factory.ListableBeanFactory;
+import com.fr.third.springframework.util.Assert;
+import com.fr.third.springframework.util.ReflectionUtils;
+
+/**
+ * AOP Alliance MethodInterceptor that provides persistence exception translation
+ * based on a given PersistenceExceptionTranslator.
+ *
+ *
Delegates to the given {@link PersistenceExceptionTranslator} to translate
+ * a RuntimeException thrown into Spring's DataAccessException hierarchy
+ * (if appropriate). If the RuntimeException in question is declared on the
+ * target method, it is always propagated as-is (with no translation applied).
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see PersistenceExceptionTranslator
+ */
+public class PersistenceExceptionTranslationInterceptor
+ implements MethodInterceptor, BeanFactoryAware, InitializingBean {
+
+ private volatile PersistenceExceptionTranslator persistenceExceptionTranslator;
+
+ private boolean alwaysTranslate = false;
+
+ private ListableBeanFactory beanFactory;
+
+
+ /**
+ * Create a new PersistenceExceptionTranslationInterceptor.
+ * Needs to be configured with a PersistenceExceptionTranslator afterwards.
+ * @see #setPersistenceExceptionTranslator
+ */
+ public PersistenceExceptionTranslationInterceptor() {
+ }
+
+ /**
+ * Create a new PersistenceExceptionTranslationInterceptor
+ * for the given PersistenceExceptionTranslator.
+ * @param pet the PersistenceExceptionTranslator to use
+ */
+ public PersistenceExceptionTranslationInterceptor(PersistenceExceptionTranslator pet) {
+ Assert.notNull(pet, "PersistenceExceptionTranslator must not be null");
+ this.persistenceExceptionTranslator = pet;
+ }
+
+ /**
+ * Create a new PersistenceExceptionTranslationInterceptor, autodetecting
+ * PersistenceExceptionTranslators in the given BeanFactory.
+ * @param beanFactory the ListableBeanFactory to obtaining all
+ * PersistenceExceptionTranslators from
+ */
+ public PersistenceExceptionTranslationInterceptor(ListableBeanFactory beanFactory) {
+ Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
+ this.beanFactory = beanFactory;
+ }
+
+
+ /**
+ * Specify the PersistenceExceptionTranslator to use.
+ *
Default is to autodetect all PersistenceExceptionTranslators
+ * in the containing BeanFactory, using them in a chain.
+ * @see #detectPersistenceExceptionTranslators
+ */
+ public void setPersistenceExceptionTranslator(PersistenceExceptionTranslator pet) {
+ this.persistenceExceptionTranslator = pet;
+ }
+
+ /**
+ * Specify whether to always translate the exception ("true"), or whether throw the
+ * raw exception when declared, i.e. when the originating method signature's exception
+ * declarations allow for the raw exception to be thrown ("false").
+ *
Default is "false". Switch this flag to "true" in order to always translate
+ * applicable exceptions, independent from the originating method signature.
+ *
Note that the originating method does not have to declare the specific exception.
+ * Any base class will do as well, even {@code throws Exception}: As long as the
+ * originating method does explicitly declare compatible exceptions, the raw exception
+ * will be rethrown. If you would like to avoid throwing raw exceptions in any case,
+ * switch this flag to "true".
+ */
+ public void setAlwaysTranslate(boolean alwaysTranslate) {
+ this.alwaysTranslate = alwaysTranslate;
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ if (this.persistenceExceptionTranslator == null) {
+ // No explicit exception translator specified - perform autodetection.
+ if (!(beanFactory instanceof ListableBeanFactory)) {
+ throw new IllegalArgumentException(
+ "Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory");
+ }
+ this.beanFactory = (ListableBeanFactory) beanFactory;
+ }
+ }
+
+ @Override
+ public void afterPropertiesSet() {
+ if (this.persistenceExceptionTranslator == null && this.beanFactory == null) {
+ throw new IllegalArgumentException("Property 'persistenceExceptionTranslator' is required");
+ }
+ }
+
+
+ @Override
+ public Object invoke(MethodInvocation mi) throws Throwable {
+ try {
+ return mi.proceed();
+ }
+ catch (RuntimeException ex) {
+ // Let it throw raw if the type of the exception is on the throws clause of the method.
+ if (!this.alwaysTranslate && ReflectionUtils.declaresException(mi.getMethod(), ex.getClass())) {
+ throw ex;
+ }
+ else {
+ if (this.persistenceExceptionTranslator == null) {
+ this.persistenceExceptionTranslator = detectPersistenceExceptionTranslators(this.beanFactory);
+ }
+ throw DataAccessUtils.translateIfNecessary(ex, this.persistenceExceptionTranslator);
+ }
+ }
+ }
+
+ /**
+ * Detect all PersistenceExceptionTranslators in the given BeanFactory.
+ * @param beanFactory the ListableBeanFactory to obtaining all
+ * PersistenceExceptionTranslators from
+ * @return a chained PersistenceExceptionTranslator, combining all
+ * PersistenceExceptionTranslators found in the factory
+ * @see ChainedPersistenceExceptionTranslator
+ */
+ protected PersistenceExceptionTranslator detectPersistenceExceptionTranslators(ListableBeanFactory beanFactory) {
+ // Find all translators, being careful not to activate FactoryBeans.
+ Map pets = BeanFactoryUtils.beansOfTypeIncludingAncestors(
+ beanFactory, PersistenceExceptionTranslator.class, false, false);
+ ChainedPersistenceExceptionTranslator cpet = new ChainedPersistenceExceptionTranslator();
+ for (PersistenceExceptionTranslator pet : pets.values()) {
+ cpet.addDelegate(pet);
+ }
+ return cpet;
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/support/PersistenceExceptionTranslator.java b/fine-spring/src/com/fr/third/springframework/dao/support/PersistenceExceptionTranslator.java
new file mode 100644
index 000000000..a7e3efec2
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/support/PersistenceExceptionTranslator.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.dao.support;
+
+import com.fr.third.springframework.dao.DataAccessException;
+
+/**
+ * Interface implemented by Spring integrations with data access technologies
+ * that throw runtime exceptions, such as JPA, TopLink, JDO and Hibernate.
+ *
+ *
This allows consistent usage of combined exception translation functionality,
+ * without forcing a single translator to understand every single possible type
+ * of exception.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+public interface PersistenceExceptionTranslator {
+
+ /**
+ * Translate the given runtime exception thrown by a persistence framework to a
+ * corresponding exception from Spring's generic DataAccessException hierarchy,
+ * if possible.
+ *
Do not translate exceptions that are not understand by this translator:
+ * for example, if coming from another persistence framework, or resulting
+ * from user code and unrelated to persistence.
+ *
Of particular importance is the correct translation to
+ * DataIntegrityViolationException, for example on constraint violation.
+ * Implementations may use Spring JDBC's sophisticated exception translation
+ * to provide further information in the event of SQLException as a root cause.
+ * @param ex a RuntimeException thrown
+ * @return the corresponding DataAccessException (or {@code null} if the
+ * exception could not be translated, as in this case it may result from
+ * user code rather than an actual persistence problem)
+ * @see com.fr.third.springframework.dao.DataIntegrityViolationException
+ * @see com.fr.third.springframework.jdbc.support.SQLExceptionTranslator
+ */
+ DataAccessException translateExceptionIfPossible(RuntimeException ex);
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/dao/support/package-info.java b/fine-spring/src/com/fr/third/springframework/dao/support/package-info.java
new file mode 100644
index 000000000..5940dd4a9
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/dao/support/package-info.java
@@ -0,0 +1,9 @@
+
+/**
+ *
+ * Support classes for DAO implementations,
+ * providing miscellaneous utility methods.
+ *
+ */
+package com.fr.third.springframework.dao.support;
+
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/CannotCreateRecordException.java b/fine-spring/src/com/fr/third/springframework/jca/cci/CannotCreateRecordException.java
new file mode 100644
index 000000000..287fb9679
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/CannotCreateRecordException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci;
+
+import javax.resource.ResourceException;
+
+import com.fr.third.springframework.dao.DataAccessResourceFailureException;
+
+/**
+ * Exception thrown when the creating of a CCI Record failed
+ * for connector-internal reasons.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ */
+@SuppressWarnings("serial")
+public class CannotCreateRecordException extends DataAccessResourceFailureException {
+
+ /**
+ * Constructor for CannotCreateRecordException.
+ * @param msg message
+ * @param ex ResourceException root cause
+ */
+ public CannotCreateRecordException(String msg, ResourceException ex) {
+ super(msg, ex);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/CannotGetCciConnectionException.java b/fine-spring/src/com/fr/third/springframework/jca/cci/CannotGetCciConnectionException.java
new file mode 100644
index 000000000..96fcd2468
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/CannotGetCciConnectionException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci;
+
+import javax.resource.ResourceException;
+
+import com.fr.third.springframework.dao.DataAccessResourceFailureException;
+
+/**
+ * Fatal exception thrown when we can't connect to an EIS using CCI.
+ *
+ * @author Thierry Templier
+ * @author Juergen Hoeller
+ * @since 1.2
+ */
+@SuppressWarnings("serial")
+public class CannotGetCciConnectionException extends DataAccessResourceFailureException {
+
+ /**
+ * Constructor for CannotGetCciConnectionException.
+ * @param msg message
+ * @param ex ResourceException root cause
+ */
+ public CannotGetCciConnectionException(String msg, ResourceException ex) {
+ super(msg, ex);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/CciOperationNotSupportedException.java b/fine-spring/src/com/fr/third/springframework/jca/cci/CciOperationNotSupportedException.java
new file mode 100644
index 000000000..84a660093
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/CciOperationNotSupportedException.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci;
+
+import javax.resource.ResourceException;
+
+import com.fr.third.springframework.dao.InvalidDataAccessResourceUsageException;
+
+/**
+ * Exception thrown when the connector doesn't support a specific CCI operation.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ */
+@SuppressWarnings("serial")
+public class CciOperationNotSupportedException extends InvalidDataAccessResourceUsageException {
+
+ /**
+ * Constructor for CciOperationNotSupportedException.
+ * @param msg message
+ * @param ex ResourceException root cause
+ */
+ public CciOperationNotSupportedException(String msg, ResourceException ex) {
+ super(msg, ex);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/InvalidResultSetAccessException.java b/fine-spring/src/com/fr/third/springframework/jca/cci/InvalidResultSetAccessException.java
new file mode 100644
index 000000000..044c7c8f5
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/InvalidResultSetAccessException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci;
+
+import java.sql.SQLException;
+
+import com.fr.third.springframework.dao.InvalidDataAccessResourceUsageException;
+
+/**
+ * Exception thrown when a ResultSet has been accessed in an invalid fashion.
+ * Such exceptions always have a {@code java.sql.SQLException} root cause.
+ *
+ *
This typically happens when an invalid ResultSet column index or name
+ * has been specified.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see javax.resource.cci.ResultSet
+ */
+@SuppressWarnings("serial")
+public class InvalidResultSetAccessException extends InvalidDataAccessResourceUsageException {
+
+ /**
+ * Constructor for InvalidResultSetAccessException.
+ * @param msg message
+ * @param ex the root cause
+ */
+ public InvalidResultSetAccessException(String msg, SQLException ex) {
+ super(ex.getMessage(), ex);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/RecordTypeNotSupportedException.java b/fine-spring/src/com/fr/third/springframework/jca/cci/RecordTypeNotSupportedException.java
new file mode 100644
index 000000000..eb90de2dd
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/RecordTypeNotSupportedException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci;
+
+import javax.resource.ResourceException;
+
+import com.fr.third.springframework.dao.InvalidDataAccessResourceUsageException;
+
+/**
+ * Exception thrown when the creating of a CCI Record failed because
+ * the connector doesn't support the desired CCI Record type.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ */
+@SuppressWarnings("serial")
+public class RecordTypeNotSupportedException extends InvalidDataAccessResourceUsageException {
+
+ /**
+ * Constructor for RecordTypeNotSupportedException.
+ * @param msg message
+ * @param ex ResourceException root cause
+ */
+ public RecordTypeNotSupportedException(String msg, ResourceException ex) {
+ super(msg, ex);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/connection/CciLocalTransactionManager.java b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/CciLocalTransactionManager.java
new file mode 100644
index 000000000..77bd729e6
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/CciLocalTransactionManager.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.connection;
+
+import javax.resource.NotSupportedException;
+import javax.resource.ResourceException;
+import javax.resource.cci.Connection;
+import javax.resource.cci.ConnectionFactory;
+import javax.resource.spi.LocalTransactionException;
+
+import com.fr.third.springframework.beans.factory.InitializingBean;
+import com.fr.third.springframework.transaction.CannotCreateTransactionException;
+import com.fr.third.springframework.transaction.TransactionDefinition;
+import com.fr.third.springframework.transaction.TransactionException;
+import com.fr.third.springframework.transaction.TransactionSystemException;
+import com.fr.third.springframework.transaction.support.AbstractPlatformTransactionManager;
+import com.fr.third.springframework.transaction.support.DefaultTransactionStatus;
+import com.fr.third.springframework.transaction.support.ResourceTransactionManager;
+import com.fr.third.springframework.transaction.support.TransactionSynchronizationManager;
+
+/**
+ * {@link com.fr.third.springframework.transaction.PlatformTransactionManager} implementation
+ * that manages local transactions for a single CCI ConnectionFactory.
+ * Binds a CCI Connection from the specified ConnectionFactory to the thread,
+ * potentially allowing for one thread-bound Connection per ConnectionFactory.
+ *
+ *
Application code is required to retrieve the CCI Connection via
+ * {@link ConnectionFactoryUtils#getConnection(ConnectionFactory)} instead of a standard
+ * J2EE-style {@link ConnectionFactory#getConnection()} call. Spring classes such as
+ * {@link com.fr.third.springframework.jca.cci.core.CciTemplate} use this strategy implicitly.
+ * If not used in combination with this transaction manager, the
+ * {@link ConnectionFactoryUtils} lookup strategy behaves exactly like the native
+ * DataSource lookup; it can thus be used in a portable fashion.
+ *
+ *
Alternatively, you can allow application code to work with the standard
+ * J2EE lookup pattern {@link ConnectionFactory#getConnection()}, for example
+ * for legacy code that is not aware of Spring at all. In that case, define a
+ * {@link TransactionAwareConnectionFactoryProxy} for your target ConnectionFactory,
+ * which will automatically participate in Spring-managed transactions.
+ *
+ * @author Thierry Templier
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see ConnectionFactoryUtils#getConnection(javax.resource.cci.ConnectionFactory)
+ * @see ConnectionFactoryUtils#releaseConnection
+ * @see TransactionAwareConnectionFactoryProxy
+ * @see com.fr.third.springframework.jca.cci.core.CciTemplate
+ */
+@SuppressWarnings("serial")
+public class CciLocalTransactionManager extends AbstractPlatformTransactionManager
+ implements ResourceTransactionManager, InitializingBean {
+
+ private ConnectionFactory connectionFactory;
+
+
+ /**
+ * Create a new CciLocalTransactionManager instance.
+ * A ConnectionFactory has to be set to be able to use it.
+ * @see #setConnectionFactory
+ */
+ public CciLocalTransactionManager() {
+ }
+
+ /**
+ * Create a new CciLocalTransactionManager instance.
+ * @param connectionFactory CCI ConnectionFactory to manage local transactions for
+ */
+ public CciLocalTransactionManager(ConnectionFactory connectionFactory) {
+ setConnectionFactory(connectionFactory);
+ afterPropertiesSet();
+ }
+
+
+ /**
+ * Set the CCI ConnectionFactory that this instance should manage local
+ * transactions for.
+ */
+ public void setConnectionFactory(ConnectionFactory cf) {
+ if (cf instanceof TransactionAwareConnectionFactoryProxy) {
+ // If we got a TransactionAwareConnectionFactoryProxy, we need to perform transactions
+ // for its underlying target ConnectionFactory, else JMS access code won't see
+ // properly exposed transactions (i.e. transactions for the target ConnectionFactory).
+ this.connectionFactory = ((TransactionAwareConnectionFactoryProxy) cf).getTargetConnectionFactory();
+ }
+ else {
+ this.connectionFactory = cf;
+ }
+ }
+
+ /**
+ * Return the CCI ConnectionFactory that this instance manages local
+ * transactions for.
+ */
+ public ConnectionFactory getConnectionFactory() {
+ return this.connectionFactory;
+ }
+
+ @Override
+ public void afterPropertiesSet() {
+ if (getConnectionFactory() == null) {
+ throw new IllegalArgumentException("Property 'connectionFactory' is required");
+ }
+ }
+
+
+ @Override
+ public Object getResourceFactory() {
+ return getConnectionFactory();
+ }
+
+ @Override
+ protected Object doGetTransaction() {
+ CciLocalTransactionObject txObject = new CciLocalTransactionObject();
+ ConnectionHolder conHolder =
+ (ConnectionHolder) TransactionSynchronizationManager.getResource(getConnectionFactory());
+ txObject.setConnectionHolder(conHolder);
+ return txObject;
+ }
+
+ @Override
+ protected boolean isExistingTransaction(Object transaction) {
+ CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction;
+ // Consider a pre-bound connection as transaction.
+ return (txObject.getConnectionHolder() != null);
+ }
+
+ @Override
+ protected void doBegin(Object transaction, TransactionDefinition definition) {
+ CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction;
+ Connection con = null;
+
+ try {
+ con = getConnectionFactory().getConnection();
+ if (logger.isDebugEnabled()) {
+ logger.debug("Acquired Connection [" + con + "] for local CCI transaction");
+ }
+
+ txObject.setConnectionHolder(new ConnectionHolder(con));
+ txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
+
+ con.getLocalTransaction().begin();
+ int timeout = determineTimeout(definition);
+ if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
+ txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
+ }
+ TransactionSynchronizationManager.bindResource(getConnectionFactory(), txObject.getConnectionHolder());
+ }
+ catch (NotSupportedException ex) {
+ ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
+ throw new CannotCreateTransactionException("CCI Connection does not support local transactions", ex);
+ }
+ catch (LocalTransactionException ex) {
+ ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
+ throw new CannotCreateTransactionException("Could not begin local CCI transaction", ex);
+ }
+ catch (Throwable ex) {
+ ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
+ throw new TransactionSystemException("Unexpected failure on begin of CCI local transaction", ex);
+ }
+ }
+
+ @Override
+ protected Object doSuspend(Object transaction) {
+ CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction;
+ txObject.setConnectionHolder(null);
+ return TransactionSynchronizationManager.unbindResource(getConnectionFactory());
+ }
+
+ @Override
+ protected void doResume(Object transaction, Object suspendedResources) {
+ ConnectionHolder conHolder = (ConnectionHolder) suspendedResources;
+ TransactionSynchronizationManager.bindResource(getConnectionFactory(), conHolder);
+ }
+
+ protected boolean isRollbackOnly(Object transaction) throws TransactionException {
+ CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction;
+ return txObject.getConnectionHolder().isRollbackOnly();
+ }
+
+ @Override
+ protected void doCommit(DefaultTransactionStatus status) {
+ CciLocalTransactionObject txObject = (CciLocalTransactionObject) status.getTransaction();
+ Connection con = txObject.getConnectionHolder().getConnection();
+ if (status.isDebug()) {
+ logger.debug("Committing CCI local transaction on Connection [" + con + "]");
+ }
+ try {
+ con.getLocalTransaction().commit();
+ }
+ catch (LocalTransactionException ex) {
+ throw new TransactionSystemException("Could not commit CCI local transaction", ex);
+ }
+ catch (ResourceException ex) {
+ throw new TransactionSystemException("Unexpected failure on commit of CCI local transaction", ex);
+ }
+ }
+
+ @Override
+ protected void doRollback(DefaultTransactionStatus status) {
+ CciLocalTransactionObject txObject = (CciLocalTransactionObject) status.getTransaction();
+ Connection con = txObject.getConnectionHolder().getConnection();
+ if (status.isDebug()) {
+ logger.debug("Rolling back CCI local transaction on Connection [" + con + "]");
+ }
+ try {
+ con.getLocalTransaction().rollback();
+ }
+ catch (LocalTransactionException ex) {
+ throw new TransactionSystemException("Could not roll back CCI local transaction", ex);
+ }
+ catch (ResourceException ex) {
+ throw new TransactionSystemException("Unexpected failure on rollback of CCI local transaction", ex);
+ }
+ }
+
+ @Override
+ protected void doSetRollbackOnly(DefaultTransactionStatus status) {
+ CciLocalTransactionObject txObject = (CciLocalTransactionObject) status.getTransaction();
+ if (status.isDebug()) {
+ logger.debug("Setting CCI local transaction [" + txObject.getConnectionHolder().getConnection() +
+ "] rollback-only");
+ }
+ txObject.getConnectionHolder().setRollbackOnly();
+ }
+
+ @Override
+ protected void doCleanupAfterCompletion(Object transaction) {
+ CciLocalTransactionObject txObject = (CciLocalTransactionObject) transaction;
+
+ // Remove the connection holder from the thread.
+ TransactionSynchronizationManager.unbindResource(getConnectionFactory());
+ txObject.getConnectionHolder().clear();
+
+ Connection con = txObject.getConnectionHolder().getConnection();
+ if (logger.isDebugEnabled()) {
+ logger.debug("Releasing CCI Connection [" + con + "] after transaction");
+ }
+ ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
+ }
+
+
+ /**
+ * CCI local transaction object, representing a ConnectionHolder.
+ * Used as transaction object by CciLocalTransactionManager.
+ * @see ConnectionHolder
+ */
+ private static class CciLocalTransactionObject {
+
+ private ConnectionHolder connectionHolder;
+
+ public void setConnectionHolder(ConnectionHolder connectionHolder) {
+ this.connectionHolder = connectionHolder;
+ }
+
+ public ConnectionHolder getConnectionHolder() {
+ return this.connectionHolder;
+ }
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/connection/ConnectionFactoryUtils.java b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/ConnectionFactoryUtils.java
new file mode 100644
index 000000000..f3c746f5c
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/ConnectionFactoryUtils.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.connection;
+
+import javax.resource.ResourceException;
+import javax.resource.cci.Connection;
+import javax.resource.cci.ConnectionFactory;
+import javax.resource.cci.ConnectionSpec;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.fr.third.springframework.jca.cci.CannotGetCciConnectionException;
+import com.fr.third.springframework.transaction.support.ResourceHolderSynchronization;
+import com.fr.third.springframework.transaction.support.TransactionSynchronizationManager;
+import com.fr.third.springframework.util.Assert;
+
+/**
+ * Helper class that provides static methods for obtaining CCI Connections
+ * from a {@link javax.resource.cci.ConnectionFactory}. Includes special
+ * support for Spring-managed transactional Connections, e.g. managed
+ * by {@link CciLocalTransactionManager} or
+ * {@link com.fr.third.springframework.transaction.jta.JtaTransactionManager}.
+ *
+ *
Used internally by {@link com.fr.third.springframework.jca.cci.core.CciTemplate},
+ * Spring's CCI operation objects and the {@link CciLocalTransactionManager}.
+ * Can also be used directly in application code.
+ *
+ * @author Thierry Templier
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see #getConnection
+ * @see #releaseConnection
+ * @see CciLocalTransactionManager
+ * @see com.fr.third.springframework.transaction.jta.JtaTransactionManager
+ * @see com.fr.third.springframework.transaction.support.TransactionSynchronizationManager
+ */
+public abstract class ConnectionFactoryUtils {
+
+ private static final Log logger = LogFactory.getLog(ConnectionFactoryUtils.class);
+
+
+ /**
+ * Obtain a Connection from the given ConnectionFactory. Translates ResourceExceptions
+ * into the Spring hierarchy of unchecked generic data access exceptions, simplifying
+ * calling code and making any exception that is thrown more meaningful.
+ *
Is aware of a corresponding Connection bound to the current thread, for example
+ * when using {@link CciLocalTransactionManager}. Will bind a Connection to the thread
+ * if transaction synchronization is active (e.g. if in a JTA transaction).
+ * @param cf the ConnectionFactory to obtain Connection from
+ * @return a CCI Connection from the given ConnectionFactory
+ * @throws com.fr.third.springframework.jca.cci.CannotGetCciConnectionException
+ * if the attempt to get a Connection failed
+ * @see #releaseConnection
+ */
+ public static Connection getConnection(ConnectionFactory cf) throws CannotGetCciConnectionException {
+ return getConnection(cf, null);
+ }
+
+ /**
+ * Obtain a Connection from the given ConnectionFactory. Translates ResourceExceptions
+ * into the Spring hierarchy of unchecked generic data access exceptions, simplifying
+ * calling code and making any exception that is thrown more meaningful.
+ *
Is aware of a corresponding Connection bound to the current thread, for example
+ * when using {@link CciLocalTransactionManager}. Will bind a Connection to the thread
+ * if transaction synchronization is active (e.g. if in a JTA transaction).
+ * @param cf the ConnectionFactory to obtain Connection from
+ * @param spec the ConnectionSpec for the desired Connection (may be {@code null}).
+ * Note: If this is specified, a new Connection will be obtained for every call,
+ * without participating in a shared transactional Connection.
+ * @return a CCI Connection from the given ConnectionFactory
+ * @throws com.fr.third.springframework.jca.cci.CannotGetCciConnectionException
+ * if the attempt to get a Connection failed
+ * @see #releaseConnection
+ */
+ public static Connection getConnection(ConnectionFactory cf, ConnectionSpec spec)
+ throws CannotGetCciConnectionException {
+ try {
+ if (spec != null) {
+ Assert.notNull(cf, "No ConnectionFactory specified");
+ return cf.getConnection(spec);
+ }
+ else {
+ return doGetConnection(cf);
+ }
+ }
+ catch (ResourceException ex) {
+ throw new CannotGetCciConnectionException("Could not get CCI Connection", ex);
+ }
+ }
+
+ /**
+ * Actually obtain a CCI Connection from the given ConnectionFactory.
+ * Same as {@link #getConnection}, but throwing the original ResourceException.
+ *
Is aware of a corresponding Connection bound to the current thread, for example
+ * when using {@link CciLocalTransactionManager}. Will bind a Connection to the thread
+ * if transaction synchronization is active (e.g. if in a JTA transaction).
+ *
Directly accessed by {@link TransactionAwareConnectionFactoryProxy}.
+ * @param cf the ConnectionFactory to obtain Connection from
+ * @return a CCI Connection from the given ConnectionFactory
+ * @throws ResourceException if thrown by CCI API methods
+ * @see #doReleaseConnection
+ */
+ public static Connection doGetConnection(ConnectionFactory cf) throws ResourceException {
+ Assert.notNull(cf, "No ConnectionFactory specified");
+
+ ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(cf);
+ if (conHolder != null) {
+ return conHolder.getConnection();
+ }
+
+ logger.debug("Opening CCI Connection");
+ Connection con = cf.getConnection();
+
+ if (TransactionSynchronizationManager.isSynchronizationActive()) {
+ logger.debug("Registering transaction synchronization for CCI Connection");
+ conHolder = new ConnectionHolder(con);
+ conHolder.setSynchronizedWithTransaction(true);
+ TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(conHolder, cf));
+ TransactionSynchronizationManager.bindResource(cf, conHolder);
+ }
+
+ return con;
+ }
+
+ /**
+ * Determine whether the given JCA CCI Connection is transactional, that is,
+ * bound to the current thread by Spring's transaction facilities.
+ * @param con the Connection to check
+ * @param cf the ConnectionFactory that the Connection was obtained from
+ * (may be {@code null})
+ * @return whether the Connection is transactional
+ */
+ public static boolean isConnectionTransactional(Connection con, ConnectionFactory cf) {
+ if (cf == null) {
+ return false;
+ }
+ ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(cf);
+ return (conHolder != null && conHolder.getConnection() == con);
+ }
+
+ /**
+ * Close the given Connection, obtained from the given ConnectionFactory,
+ * if it is not managed externally (that is, not bound to the thread).
+ * @param con the Connection to close if necessary
+ * (if this is {@code null}, the call will be ignored)
+ * @param cf the ConnectionFactory that the Connection was obtained from
+ * (can be {@code null})
+ * @see #getConnection
+ */
+ public static void releaseConnection(Connection con, ConnectionFactory cf) {
+ try {
+ doReleaseConnection(con, cf);
+ }
+ catch (ResourceException ex) {
+ logger.debug("Could not close CCI Connection", ex);
+ }
+ catch (Throwable ex) {
+ // We don't trust the CCI driver: It might throw RuntimeException or Error.
+ logger.debug("Unexpected exception on closing CCI Connection", ex);
+ }
+ }
+
+ /**
+ * Actually close the given Connection, obtained from the given ConnectionFactory.
+ * Same as {@link #releaseConnection}, but throwing the original ResourceException.
+ *
Directly accessed by {@link TransactionAwareConnectionFactoryProxy}.
+ * @param con the Connection to close if necessary
+ * (if this is {@code null}, the call will be ignored)
+ * @param cf the ConnectionFactory that the Connection was obtained from
+ * (can be {@code null})
+ * @throws ResourceException if thrown by JCA CCI methods
+ * @see #doGetConnection
+ */
+ public static void doReleaseConnection(Connection con, ConnectionFactory cf) throws ResourceException {
+ if (con == null || isConnectionTransactional(con, cf)) {
+ return;
+ }
+ con.close();
+ }
+
+
+ /**
+ * Callback for resource cleanup at the end of a non-native CCI transaction
+ * (e.g. when participating in a JTA transaction).
+ */
+ private static class ConnectionSynchronization
+ extends ResourceHolderSynchronization {
+
+ public ConnectionSynchronization(ConnectionHolder connectionHolder, ConnectionFactory connectionFactory) {
+ super(connectionHolder, connectionFactory);
+ }
+
+ @Override
+ protected void releaseResource(ConnectionHolder resourceHolder, ConnectionFactory resourceKey) {
+ releaseConnection(resourceHolder.getConnection(), resourceKey);
+ }
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/connection/ConnectionHolder.java b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/ConnectionHolder.java
new file mode 100644
index 000000000..f6d801c0a
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/ConnectionHolder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.connection;
+
+import javax.resource.cci.Connection;
+
+import com.fr.third.springframework.transaction.support.ResourceHolderSupport;
+
+/**
+ * Connection holder, wrapping a CCI Connection.
+ *
+ *
CciLocalTransactionManager binds instances of this class
+ * to the thread, for a given ConnectionFactory.
+ *
+ *
Note: This is an SPI class, not intended to be used by applications.
+ *
+ * @author Thierry Templier
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see CciLocalTransactionManager
+ * @see ConnectionFactoryUtils
+ */
+public class ConnectionHolder extends ResourceHolderSupport {
+
+ private final Connection connection;
+
+ public ConnectionHolder(Connection connection) {
+ this.connection = connection;
+ }
+
+ public Connection getConnection() {
+ return this.connection;
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/connection/ConnectionSpecConnectionFactoryAdapter.java b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/ConnectionSpecConnectionFactoryAdapter.java
new file mode 100644
index 000000000..788492954
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/ConnectionSpecConnectionFactoryAdapter.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.connection;
+
+import javax.resource.ResourceException;
+import javax.resource.cci.Connection;
+import javax.resource.cci.ConnectionSpec;
+
+import com.fr.third.springframework.core.NamedThreadLocal;
+
+/**
+ * An adapter for a target CCI {@link javax.resource.cci.ConnectionFactory},
+ * applying the given ConnectionSpec to every standard {@code getConnection()}
+ * call, that is, implicitly invoking {@code getConnection(ConnectionSpec)}
+ * on the target. All other methods simply delegate to the corresponding methods
+ * of the target ConnectionFactory.
+ *
+ *
Can be used to proxy a target JNDI ConnectionFactory that does not have a
+ * ConnectionSpec configured. Client code can work with the ConnectionFactory
+ * without passing in a ConnectionSpec on every {@code getConnection()} call.
+ *
+ *
In the following example, client code can simply transparently work with
+ * the preconfigured "myConnectionFactory", implicitly accessing
+ * "myTargetConnectionFactory" with the specified user credentials.
+ *
+ *
If the "connectionSpec" is empty, this proxy will simply delegate to the
+ * standard {@code getConnection()} method of the target ConnectionFactory.
+ * This can be used to keep a UserCredentialsConnectionFactoryAdapter bean definition
+ * just for the option of implicitly passing in a ConnectionSpec if the
+ * particular target ConnectionFactory requires it.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see #getConnection
+ */
+@SuppressWarnings("serial")
+public class ConnectionSpecConnectionFactoryAdapter extends DelegatingConnectionFactory {
+
+ private ConnectionSpec connectionSpec;
+
+ private final ThreadLocal threadBoundSpec =
+ new NamedThreadLocal("Current CCI ConnectionSpec");
+
+
+ /**
+ * Set the ConnectionSpec that this adapter should use for retrieving Connections.
+ * Default is none.
+ */
+ public void setConnectionSpec(ConnectionSpec connectionSpec) {
+ this.connectionSpec = connectionSpec;
+ }
+
+ /**
+ * Set a ConnectionSpec for this proxy and the current thread.
+ * The given ConnectionSpec will be applied to all subsequent
+ * {@code getConnection()} calls on this ConnectionFactory proxy.
+ *
This will override any statically specified "connectionSpec" property.
+ * @param spec the ConnectionSpec to apply
+ * @see #removeConnectionSpecFromCurrentThread
+ */
+ public void setConnectionSpecForCurrentThread(ConnectionSpec spec) {
+ this.threadBoundSpec.set(spec);
+ }
+
+ /**
+ * Remove any ConnectionSpec for this proxy from the current thread.
+ * A statically specified ConnectionSpec applies again afterwards.
+ * @see #setConnectionSpecForCurrentThread
+ */
+ public void removeConnectionSpecFromCurrentThread() {
+ this.threadBoundSpec.remove();
+ }
+
+
+ /**
+ * Determine whether there is currently a thread-bound ConnectionSpec,
+ * using it if available, falling back to the statically specified
+ * "connectionSpec" property else.
+ * @see #doGetConnection
+ */
+ @Override
+ public final Connection getConnection() throws ResourceException {
+ ConnectionSpec threadSpec = this.threadBoundSpec.get();
+ if (threadSpec != null) {
+ return doGetConnection(threadSpec);
+ }
+ else {
+ return doGetConnection(this.connectionSpec);
+ }
+ }
+
+ /**
+ * This implementation delegates to the {@code getConnection(ConnectionSpec)}
+ * method of the target ConnectionFactory, passing in the specified user credentials.
+ * If the specified username is empty, it will simply delegate to the standard
+ * {@code getConnection()} method of the target ConnectionFactory.
+ * @param spec the ConnectionSpec to apply
+ * @return the Connection
+ * @see javax.resource.cci.ConnectionFactory#getConnection(javax.resource.cci.ConnectionSpec)
+ * @see javax.resource.cci.ConnectionFactory#getConnection()
+ */
+ protected Connection doGetConnection(ConnectionSpec spec) throws ResourceException {
+ if (getTargetConnectionFactory() == null) {
+ throw new IllegalStateException("targetConnectionFactory is required");
+ }
+ if (spec != null) {
+ return getTargetConnectionFactory().getConnection(spec);
+ }
+ else {
+ return getTargetConnectionFactory().getConnection();
+ }
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/connection/DelegatingConnectionFactory.java b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/DelegatingConnectionFactory.java
new file mode 100644
index 000000000..8e97db872
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/DelegatingConnectionFactory.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.connection;
+
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.resource.ResourceException;
+import javax.resource.cci.Connection;
+import javax.resource.cci.ConnectionFactory;
+import javax.resource.cci.ConnectionSpec;
+import javax.resource.cci.RecordFactory;
+import javax.resource.cci.ResourceAdapterMetaData;
+
+import com.fr.third.springframework.beans.factory.InitializingBean;
+
+/**
+ * CCI {@link ConnectionFactory} implementation that delegates all calls
+ * to a given target {@link ConnectionFactory}.
+ *
+ *
This class is meant to be subclassed, with subclasses overriding only
+ * those methods (such as {@link #getConnection()}) that should not simply
+ * delegate to the target {@link ConnectionFactory}.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see #getConnection
+ */
+@SuppressWarnings("serial")
+public class DelegatingConnectionFactory implements ConnectionFactory, InitializingBean {
+
+ private ConnectionFactory targetConnectionFactory;
+
+
+ /**
+ * Set the target ConnectionFactory that this ConnectionFactory should delegate to.
+ */
+ public void setTargetConnectionFactory(ConnectionFactory targetConnectionFactory) {
+ this.targetConnectionFactory = targetConnectionFactory;
+ }
+
+ /**
+ * Return the target ConnectionFactory that this ConnectionFactory should delegate to.
+ */
+ public ConnectionFactory getTargetConnectionFactory() {
+ return this.targetConnectionFactory;
+ }
+
+
+ @Override
+ public void afterPropertiesSet() {
+ if (getTargetConnectionFactory() == null) {
+ throw new IllegalArgumentException("Property 'targetConnectionFactory' is required");
+ }
+ }
+
+
+ @Override
+ public Connection getConnection() throws ResourceException {
+ return getTargetConnectionFactory().getConnection();
+ }
+
+ @Override
+ public Connection getConnection(ConnectionSpec connectionSpec) throws ResourceException {
+ return getTargetConnectionFactory().getConnection(connectionSpec);
+ }
+
+ @Override
+ public RecordFactory getRecordFactory() throws ResourceException {
+ return getTargetConnectionFactory().getRecordFactory();
+ }
+
+ @Override
+ public ResourceAdapterMetaData getMetaData() throws ResourceException {
+ return getTargetConnectionFactory().getMetaData();
+ }
+
+ @Override
+ public Reference getReference() throws NamingException {
+ return getTargetConnectionFactory().getReference();
+ }
+
+ @Override
+ public void setReference(Reference reference) {
+ getTargetConnectionFactory().setReference(reference);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/connection/NotSupportedRecordFactory.java b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/NotSupportedRecordFactory.java
new file mode 100644
index 000000000..6c74809fe
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/NotSupportedRecordFactory.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.connection;
+
+import javax.resource.NotSupportedException;
+import javax.resource.ResourceException;
+import javax.resource.cci.IndexedRecord;
+import javax.resource.cci.MappedRecord;
+import javax.resource.cci.RecordFactory;
+
+/**
+ * Implementation of the CCI RecordFactory interface that always throws
+ * NotSupportedException.
+ *
+ *
Useful as a placeholder for a RecordFactory argument (for example as
+ * defined by the RecordCreator callback), in particular when the connector's
+ * {@code ConnectionFactory.getRecordFactory()} implementation happens to
+ * throw NotSupportedException early rather than throwing the exception from
+ * RecordFactory's methods.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2.4
+ * @see com.fr.third.springframework.jca.cci.core.RecordCreator#createRecord(javax.resource.cci.RecordFactory)
+ * @see com.fr.third.springframework.jca.cci.core.CciTemplate#getRecordFactory(javax.resource.cci.ConnectionFactory)
+ * @see javax.resource.cci.ConnectionFactory#getRecordFactory()
+ * @see javax.resource.NotSupportedException
+ */
+public class NotSupportedRecordFactory implements RecordFactory {
+
+ @Override
+ public MappedRecord createMappedRecord(String name) throws ResourceException {
+ throw new NotSupportedException("The RecordFactory facility is not supported by the connector");
+ }
+
+ @Override
+ public IndexedRecord createIndexedRecord(String name) throws ResourceException {
+ throw new NotSupportedException("The RecordFactory facility is not supported by the connector");
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/connection/SingleConnectionFactory.java b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/SingleConnectionFactory.java
new file mode 100644
index 000000000..b64633883
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/SingleConnectionFactory.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.connection;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import javax.resource.NotSupportedException;
+import javax.resource.ResourceException;
+import javax.resource.cci.Connection;
+import javax.resource.cci.ConnectionFactory;
+import javax.resource.cci.ConnectionSpec;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.fr.third.springframework.beans.factory.DisposableBean;
+import com.fr.third.springframework.util.Assert;
+
+/**
+ * A CCI ConnectionFactory adapter that returns the same Connection on all
+ * {@code getConnection} calls, and ignores calls to
+ * {@code Connection.close()}.
+ *
+ *
Useful for testing and standalone environments, to keep using the same
+ * Connection for multiple CciTemplate calls, without having a pooling
+ * ConnectionFactory, also spanning any number of transactions.
+ *
+ *
You can either pass in a CCI Connection directly, or let this
+ * factory lazily create a Connection via a given target ConnectionFactory.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see #getConnection()
+ * @see javax.resource.cci.Connection#close()
+ * @see com.fr.third.springframework.jca.cci.core.CciTemplate
+ */
+@SuppressWarnings("serial")
+public class SingleConnectionFactory extends DelegatingConnectionFactory implements DisposableBean {
+
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ /** Wrapped Connection */
+ private Connection target;
+
+ /** Proxy Connection */
+ private Connection connection;
+
+ /** Synchronization monitor for the shared Connection */
+ private final Object connectionMonitor = new Object();
+
+
+ /**
+ * Create a new SingleConnectionFactory for bean-style usage.
+ * @see #setTargetConnectionFactory
+ */
+ public SingleConnectionFactory() {
+ }
+
+ /**
+ * Create a new SingleConnectionFactory that always returns the
+ * given Connection.
+ * @param target the single Connection
+ */
+ public SingleConnectionFactory(Connection target) {
+ Assert.notNull(target, "Target Connection must not be null");
+ this.target = target;
+ this.connection = getCloseSuppressingConnectionProxy(target);
+ }
+
+ /**
+ * Create a new SingleConnectionFactory that always returns a single
+ * Connection which it will lazily create via the given target
+ * ConnectionFactory.
+ * @param targetConnectionFactory the target ConnectionFactory
+ */
+ public SingleConnectionFactory(ConnectionFactory targetConnectionFactory) {
+ Assert.notNull(targetConnectionFactory, "Target ConnectionFactory must not be null");
+ setTargetConnectionFactory(targetConnectionFactory);
+ }
+
+
+ /**
+ * Make sure a Connection or ConnectionFactory has been set.
+ */
+ @Override
+ public void afterPropertiesSet() {
+ if (this.connection == null && getTargetConnectionFactory() == null) {
+ throw new IllegalArgumentException("Connection or 'targetConnectionFactory' is required");
+ }
+ }
+
+
+ @Override
+ public Connection getConnection() throws ResourceException {
+ synchronized (this.connectionMonitor) {
+ if (this.connection == null) {
+ initConnection();
+ }
+ return this.connection;
+ }
+ }
+
+ @Override
+ public Connection getConnection(ConnectionSpec connectionSpec) throws ResourceException {
+ throw new NotSupportedException(
+ "SingleConnectionFactory does not support custom ConnectionSpec");
+ }
+
+ /**
+ * Close the underlying Connection.
+ * The provider of this ConnectionFactory needs to care for proper shutdown.
+ *
As this bean implements DisposableBean, a bean factory will
+ * automatically invoke this on destruction of its cached singletons.
+ */
+ @Override
+ public void destroy() {
+ resetConnection();
+ }
+
+
+ /**
+ * Initialize the single underlying Connection.
+ *
Closes and reinitializes the Connection if an underlying
+ * Connection is present already.
+ * @throws javax.resource.ResourceException if thrown by CCI API methods
+ */
+ public void initConnection() throws ResourceException {
+ if (getTargetConnectionFactory() == null) {
+ throw new IllegalStateException(
+ "'targetConnectionFactory' is required for lazily initializing a Connection");
+ }
+ synchronized (this.connectionMonitor) {
+ if (this.target != null) {
+ closeConnection(this.target);
+ }
+ this.target = doCreateConnection();
+ prepareConnection(this.target);
+ if (logger.isInfoEnabled()) {
+ logger.info("Established shared CCI Connection: " + this.target);
+ }
+ this.connection = getCloseSuppressingConnectionProxy(this.target);
+ }
+ }
+
+ /**
+ * Reset the underlying shared Connection, to be reinitialized on next access.
+ */
+ public void resetConnection() {
+ synchronized (this.connectionMonitor) {
+ if (this.target != null) {
+ closeConnection(this.target);
+ }
+ this.target = null;
+ this.connection = null;
+ }
+ }
+
+ /**
+ * Create a CCI Connection via this template's ConnectionFactory.
+ * @return the new CCI Connection
+ * @throws javax.resource.ResourceException if thrown by CCI API methods
+ */
+ protected Connection doCreateConnection() throws ResourceException {
+ return getTargetConnectionFactory().getConnection();
+ }
+
+ /**
+ * Prepare the given Connection before it is exposed.
+ *
The default implementation is empty. Can be overridden in subclasses.
+ * @param con the Connection to prepare
+ */
+ protected void prepareConnection(Connection con) throws ResourceException {
+ }
+
+ /**
+ * Close the given Connection.
+ * @param con the Connection to close
+ */
+ protected void closeConnection(Connection con) {
+ try {
+ con.close();
+ }
+ catch (Throwable ex) {
+ logger.warn("Could not close shared CCI Connection", ex);
+ }
+ }
+
+ /**
+ * Wrap the given Connection with a proxy that delegates every method call to it
+ * but suppresses close calls. This is useful for allowing application code to
+ * handle a special framework Connection just like an ordinary Connection from a
+ * CCI ConnectionFactory.
+ * @param target the original Connection to wrap
+ * @return the wrapped Connection
+ */
+ protected Connection getCloseSuppressingConnectionProxy(Connection target) {
+ return (Connection) Proxy.newProxyInstance(
+ Connection.class.getClassLoader(),
+ new Class>[] {Connection.class},
+ new CloseSuppressingInvocationHandler(target));
+ }
+
+
+ /**
+ * Invocation handler that suppresses close calls on CCI Connections.
+ */
+ private static class CloseSuppressingInvocationHandler implements InvocationHandler {
+
+ private final Connection target;
+
+ private CloseSuppressingInvocationHandler(Connection target) {
+ this.target = target;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (method.getName().equals("equals")) {
+ // Only consider equal when proxies are identical.
+ return (proxy == args[0]);
+ }
+ else if (method.getName().equals("hashCode")) {
+ // Use hashCode of Connection proxy.
+ return System.identityHashCode(proxy);
+ }
+ else if (method.getName().equals("close")) {
+ // Handle close method: don't pass the call on.
+ return null;
+ }
+ try {
+ return method.invoke(this.target, args);
+ }
+ catch (InvocationTargetException ex) {
+ throw ex.getTargetException();
+ }
+ }
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/connection/TransactionAwareConnectionFactoryProxy.java b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/TransactionAwareConnectionFactoryProxy.java
new file mode 100644
index 000000000..7af3d5a94
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/TransactionAwareConnectionFactoryProxy.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.connection;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import javax.resource.ResourceException;
+import javax.resource.cci.Connection;
+import javax.resource.cci.ConnectionFactory;
+
+/**
+ * Proxy for a target CCI {@link javax.resource.cci.ConnectionFactory}, adding
+ * awareness of Spring-managed transactions. Similar to a transactional JNDI
+ * ConnectionFactory as provided by a J2EE server.
+ *
+ *
Data access code that should remain unaware of Spring's data access support
+ * can work with this proxy to seamlessly participate in Spring-managed transactions.
+ * Note that the transaction manager, for example the {@link CciLocalTransactionManager},
+ * still needs to work with underlying ConnectionFactory, not with this proxy.
+ *
+ *
Make sure that TransactionAwareConnectionFactoryProxy is the outermost
+ * ConnectionFactory of a chain of ConnectionFactory proxies/adapters.
+ * TransactionAwareConnectionFactoryProxy can delegate either directly to the
+ * target connection pool or to some intermediate proxy/adapter like
+ * {@link ConnectionSpecConnectionFactoryAdapter}.
+ *
+ *
Delegates to {@link ConnectionFactoryUtils} for automatically participating in
+ * thread-bound transactions, for example managed by {@link CciLocalTransactionManager}.
+ * {@code getConnection} calls and {@code close} calls on returned Connections
+ * will behave properly within a transaction, i.e. always operate on the transactional
+ * Connection. If not within a transaction, normal ConnectionFactory behavior applies.
+ *
+ *
This proxy allows data access code to work with the plain JCA CCI API and still
+ * participate in Spring-managed transactions, similar to CCI code in a J2EE/JTA
+ * environment. However, if possible, use Spring's ConnectionFactoryUtils, CciTemplate or
+ * CCI operation objects to get transaction participation even without a proxy for
+ * the target ConnectionFactory, avoiding the need to define such a proxy in the first place.
+ *
+ *
NOTE: This ConnectionFactory proxy needs to return wrapped Connections
+ * in order to handle close calls properly. Therefore, the returned Connections cannot
+ * be cast to a native CCI Connection type or to a connection pool implementation type.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see javax.resource.cci.ConnectionFactory#getConnection
+ * @see javax.resource.cci.Connection#close
+ * @see ConnectionFactoryUtils#doGetConnection
+ * @see ConnectionFactoryUtils#doReleaseConnection
+ */
+@SuppressWarnings("serial")
+public class TransactionAwareConnectionFactoryProxy extends DelegatingConnectionFactory {
+
+ /**
+ * Create a new TransactionAwareConnectionFactoryProxy.
+ * @see #setTargetConnectionFactory
+ */
+ public TransactionAwareConnectionFactoryProxy() {
+ }
+
+ /**
+ * Create a new TransactionAwareConnectionFactoryProxy.
+ * @param targetConnectionFactory the target ConnectionFactory
+ */
+ public TransactionAwareConnectionFactoryProxy(ConnectionFactory targetConnectionFactory) {
+ setTargetConnectionFactory(targetConnectionFactory);
+ afterPropertiesSet();
+ }
+
+
+ /**
+ * Delegate to ConnectionFactoryUtils for automatically participating in Spring-managed
+ * transactions. Throws the original ResourceException, if any.
+ * @return a transactional Connection if any, a new one else
+ * @see com.fr.third.springframework.jca.cci.connection.ConnectionFactoryUtils#doGetConnection
+ */
+ @Override
+ public Connection getConnection() throws ResourceException {
+ Connection con = ConnectionFactoryUtils.doGetConnection(getTargetConnectionFactory());
+ return getTransactionAwareConnectionProxy(con, getTargetConnectionFactory());
+ }
+
+ /**
+ * Wrap the given Connection with a proxy that delegates every method call to it
+ * but delegates {@code close} calls to ConnectionFactoryUtils.
+ * @param target the original Connection to wrap
+ * @param cf ConnectionFactory that the Connection came from
+ * @return the wrapped Connection
+ * @see javax.resource.cci.Connection#close()
+ * @see ConnectionFactoryUtils#doReleaseConnection
+ */
+ protected Connection getTransactionAwareConnectionProxy(Connection target, ConnectionFactory cf) {
+ return (Connection) Proxy.newProxyInstance(
+ Connection.class.getClassLoader(),
+ new Class>[] {Connection.class},
+ new TransactionAwareInvocationHandler(target, cf));
+ }
+
+
+ /**
+ * Invocation handler that delegates close calls on CCI Connections
+ * to ConnectionFactoryUtils for being aware of thread-bound transactions.
+ */
+ private static class TransactionAwareInvocationHandler implements InvocationHandler {
+
+ private final Connection target;
+
+ private final ConnectionFactory connectionFactory;
+
+ public TransactionAwareInvocationHandler(Connection target, ConnectionFactory cf) {
+ this.target = target;
+ this.connectionFactory = cf;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ // Invocation on Connection interface coming in...
+
+ if (method.getName().equals("equals")) {
+ // Only consider equal when proxies are identical.
+ return (proxy == args[0]);
+ }
+ else if (method.getName().equals("hashCode")) {
+ // Use hashCode of Connection proxy.
+ return System.identityHashCode(proxy);
+ }
+ else if (method.getName().equals("getLocalTransaction")) {
+ if (ConnectionFactoryUtils.isConnectionTransactional(this.target, this.connectionFactory)) {
+ throw new javax.resource.spi.IllegalStateException(
+ "Local transaction handling not allowed within a managed transaction");
+ }
+ }
+ else if (method.getName().equals("close")) {
+ // Handle close method: only close if not within a transaction.
+ ConnectionFactoryUtils.doReleaseConnection(this.target, this.connectionFactory);
+ return null;
+ }
+
+ // Invoke method on target Connection.
+ try {
+ return method.invoke(this.target, args);
+ }
+ catch (InvocationTargetException ex) {
+ throw ex.getTargetException();
+ }
+ }
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/connection/package-info.java b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/package-info.java
new file mode 100644
index 000000000..9eaeaad94
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/connection/package-info.java
@@ -0,0 +1,10 @@
+
+/**
+ *
+ * Provides a utility class for easy ConnectionFactory access,
+ * a PlatformTransactionManager for local CCI transactions,
+ * and various simple ConnectionFactory proxies/adapters.
+ *
+ */
+package com.fr.third.springframework.jca.cci.connection;
+
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/core/CciOperations.java b/fine-spring/src/com/fr/third/springframework/jca/cci/core/CciOperations.java
new file mode 100644
index 000000000..e5da4b379
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/core/CciOperations.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.core;
+
+import javax.resource.cci.InteractionSpec;
+import javax.resource.cci.Record;
+
+import com.fr.third.springframework.dao.DataAccessException;
+
+/**
+ * Interface that specifies a basic set of CCI operations on an EIS.
+ * Implemented by CciTemplate. Not often used, but a useful option
+ * to enhance testability, as it can easily be mocked or stubbed.
+ *
+ *
Alternatively, the standard CCI infrastructure can be mocked.
+ * However, mocking this interface constitutes significantly less work.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see CciTemplate
+ */
+public interface CciOperations {
+
+ /**
+ * Execute a request on an EIS with CCI, implemented as callback action
+ * working on a CCI Connection. This allows for implementing arbitrary
+ * data access operations, within Spring's managed CCI environment:
+ * that is, participating in Spring-managed transactions and converting
+ * JCA ResourceExceptions into Spring's DataAccessException hierarchy.
+ *
The callback action can return a result object, for example a
+ * domain object or a collection of domain objects.
+ * @param action the callback object that specifies the action
+ * @return the result object returned by the action, if any
+ * @throws DataAccessException if there is any problem
+ */
+ T execute(ConnectionCallback action) throws DataAccessException;
+
+ /**
+ * Execute a request on an EIS with CCI, implemented as callback action
+ * working on a CCI Interaction. This allows for implementing arbitrary
+ * data access operations on a single Interaction, within Spring's managed
+ * CCI environment: that is, participating in Spring-managed transactions
+ * and converting JCA ResourceExceptions into Spring's DataAccessException
+ * hierarchy.
+ *
The callback action can return a result object, for example a
+ * domain object or a collection of domain objects.
+ * @param action the callback object that specifies the action
+ * @return the result object returned by the action, if any
+ * @throws DataAccessException if there is any problem
+ */
+ T execute(InteractionCallback action) throws DataAccessException;
+
+ /**
+ * Execute the specified interaction on an EIS with CCI.
+ * @param spec the CCI InteractionSpec instance that defines
+ * the interaction (connector-specific)
+ * @param inputRecord the input record
+ * @return the output record
+ * @throws DataAccessException if there is any problem
+ */
+ Record execute(InteractionSpec spec, Record inputRecord) throws DataAccessException;
+
+ /**
+ * Execute the specified interaction on an EIS with CCI.
+ * @param spec the CCI InteractionSpec instance that defines
+ * the interaction (connector-specific)
+ * @param inputRecord the input record
+ * @param outputRecord the output record
+ * @throws DataAccessException if there is any problem
+ */
+ void execute(InteractionSpec spec, Record inputRecord, Record outputRecord) throws DataAccessException;
+
+ /**
+ * Execute the specified interaction on an EIS with CCI.
+ * @param spec the CCI InteractionSpec instance that defines
+ * the interaction (connector-specific)
+ * @param inputCreator object that creates the input record to use
+ * @return the output record
+ * @throws DataAccessException if there is any problem
+ */
+ Record execute(InteractionSpec spec, RecordCreator inputCreator) throws DataAccessException;
+
+ /**
+ * Execute the specified interaction on an EIS with CCI.
+ * @param spec the CCI InteractionSpec instance that defines
+ * the interaction (connector-specific)
+ * @param inputRecord the input record
+ * @param outputExtractor object to convert the output record to a result object
+ * @return the output data extracted with the RecordExtractor object
+ * @throws DataAccessException if there is any problem
+ */
+ T execute(InteractionSpec spec, Record inputRecord, RecordExtractor outputExtractor)
+ throws DataAccessException;
+
+ /**
+ * Execute the specified interaction on an EIS with CCI.
+ * @param spec the CCI InteractionSpec instance that defines
+ * the interaction (connector-specific)
+ * @param inputCreator object that creates the input record to use
+ * @param outputExtractor object to convert the output record to a result object
+ * @return the output data extracted with the RecordExtractor object
+ * @throws DataAccessException if there is any problem
+ */
+ T execute(InteractionSpec spec, RecordCreator inputCreator, RecordExtractor outputExtractor)
+ throws DataAccessException;
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/core/CciTemplate.java b/fine-spring/src/com/fr/third/springframework/jca/cci/core/CciTemplate.java
new file mode 100644
index 000000000..09cec7357
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/core/CciTemplate.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.core;
+
+import java.sql.SQLException;
+import javax.resource.NotSupportedException;
+import javax.resource.ResourceException;
+import javax.resource.cci.Connection;
+import javax.resource.cci.ConnectionFactory;
+import javax.resource.cci.ConnectionSpec;
+import javax.resource.cci.IndexedRecord;
+import javax.resource.cci.Interaction;
+import javax.resource.cci.InteractionSpec;
+import javax.resource.cci.MappedRecord;
+import javax.resource.cci.Record;
+import javax.resource.cci.RecordFactory;
+import javax.resource.cci.ResultSet;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.fr.third.springframework.dao.DataAccessException;
+import com.fr.third.springframework.dao.DataAccessResourceFailureException;
+import com.fr.third.springframework.jca.cci.CannotCreateRecordException;
+import com.fr.third.springframework.jca.cci.CciOperationNotSupportedException;
+import com.fr.third.springframework.jca.cci.InvalidResultSetAccessException;
+import com.fr.third.springframework.jca.cci.RecordTypeNotSupportedException;
+import com.fr.third.springframework.jca.cci.connection.ConnectionFactoryUtils;
+import com.fr.third.springframework.jca.cci.connection.NotSupportedRecordFactory;
+import com.fr.third.springframework.util.Assert;
+
+/**
+ * This is the central class in the CCI core package.
+ * It simplifies the use of CCI and helps to avoid common errors.
+ * It executes core CCI workflow, leaving application code to provide parameters
+ * to CCI and extract results. This class executes EIS queries or updates,
+ * catching ResourceExceptions and translating them to the generic exception
+ * hierarchy defined in the {@code com.fr.third.springframework.dao} package.
+ *
+ *
Code using this class can pass in and receive {@link javax.resource.cci.Record}
+ * instances, or alternatively implement callback interfaces for creating input
+ * Records and extracting result objects from output Records (or CCI ResultSets).
+ *
+ *
Can be used within a service implementation via direct instantiation
+ * with a ConnectionFactory reference, or get prepared in an application context
+ * and given to services as bean reference. Note: The ConnectionFactory should
+ * always be configured as a bean in the application context, in the first case
+ * given to the service directly, in the second case to the prepared template.
+ *
+ * @author Thierry Templier
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see RecordCreator
+ * @see RecordExtractor
+ */
+public class CciTemplate implements CciOperations {
+
+ private final Log logger = LogFactory.getLog(getClass());
+
+ private ConnectionFactory connectionFactory;
+
+ private ConnectionSpec connectionSpec;
+
+ private RecordCreator outputRecordCreator;
+
+
+ /**
+ * Construct a new CciTemplate for bean usage.
+ *
Note: The ConnectionFactory has to be set before using the instance.
+ * @see #setConnectionFactory
+ */
+ public CciTemplate() {
+ }
+
+ /**
+ * Construct a new CciTemplate, given a ConnectionFactory to obtain Connections from.
+ * Note: This will trigger eager initialization of the exception translator.
+ * @param connectionFactory JCA ConnectionFactory to obtain Connections from
+ */
+ public CciTemplate(ConnectionFactory connectionFactory) {
+ setConnectionFactory(connectionFactory);
+ afterPropertiesSet();
+ }
+
+ /**
+ * Construct a new CciTemplate, given a ConnectionFactory to obtain Connections from.
+ * Note: This will trigger eager initialization of the exception translator.
+ * @param connectionFactory JCA ConnectionFactory to obtain Connections from
+ * @param connectionSpec the CCI ConnectionSpec to obtain Connections for
+ * (may be {@code null})
+ */
+ public CciTemplate(ConnectionFactory connectionFactory, ConnectionSpec connectionSpec) {
+ setConnectionFactory(connectionFactory);
+ setConnectionSpec(connectionSpec);
+ afterPropertiesSet();
+ }
+
+
+ /**
+ * Set the CCI ConnectionFactory to obtain Connections from.
+ */
+ public void setConnectionFactory(ConnectionFactory connectionFactory) {
+ this.connectionFactory = connectionFactory;
+ }
+
+ /**
+ * Return the CCI ConnectionFactory used by this template.
+ */
+ public ConnectionFactory getConnectionFactory() {
+ return this.connectionFactory;
+ }
+
+ /**
+ * Set the CCI ConnectionSpec that this template instance is
+ * supposed to obtain Connections for.
+ */
+ public void setConnectionSpec(ConnectionSpec connectionSpec) {
+ this.connectionSpec = connectionSpec;
+ }
+
+ /**
+ * Return the CCI ConnectionSpec used by this template, if any.
+ */
+ public ConnectionSpec getConnectionSpec() {
+ return this.connectionSpec;
+ }
+
+ /**
+ * Set a RecordCreator that should be used for creating default output Records.
+ *
Default is none: When no explicit output Record gets passed into an
+ * {@code execute} method, CCI's {@code Interaction.execute} variant
+ * that returns an output Record will be called.
+ *
Specify a RecordCreator here if you always need to call CCI's
+ * {@code Interaction.execute} variant with a passed-in output Record.
+ * Unless there is an explicitly specified output Record, CciTemplate will
+ * then invoke this RecordCreator to create a default output Record instance.
+ * @see javax.resource.cci.Interaction#execute(javax.resource.cci.InteractionSpec, Record)
+ * @see javax.resource.cci.Interaction#execute(javax.resource.cci.InteractionSpec, Record, Record)
+ */
+ public void setOutputRecordCreator(RecordCreator creator) {
+ this.outputRecordCreator = creator;
+ }
+
+ /**
+ * Return a RecordCreator that should be used for creating default output Records.
+ */
+ public RecordCreator getOutputRecordCreator() {
+ return this.outputRecordCreator;
+ }
+
+ public void afterPropertiesSet() {
+ if (getConnectionFactory() == null) {
+ throw new IllegalArgumentException("Property 'connectionFactory' is required");
+ }
+ }
+
+
+ /**
+ * Create a template derived from this template instance,
+ * inheriting the ConnectionFactory and other settings but
+ * overriding the ConnectionSpec used for obtaining Connections.
+ * @param connectionSpec the CCI ConnectionSpec that the derived template
+ * instance is supposed to obtain Connections for
+ * @return the derived template instance
+ * @see #setConnectionSpec
+ */
+ public CciTemplate getDerivedTemplate(ConnectionSpec connectionSpec) {
+ CciTemplate derived = new CciTemplate();
+ derived.setConnectionFactory(getConnectionFactory());
+ derived.setConnectionSpec(connectionSpec);
+ derived.setOutputRecordCreator(getOutputRecordCreator());
+ return derived;
+ }
+
+
+ @Override
+ public T execute(ConnectionCallback action) throws DataAccessException {
+ Assert.notNull(action, "Callback object must not be null");
+ Connection con = ConnectionFactoryUtils.getConnection(getConnectionFactory(), getConnectionSpec());
+ try {
+ return action.doInConnection(con, getConnectionFactory());
+ }
+ catch (NotSupportedException ex) {
+ throw new CciOperationNotSupportedException("CCI operation not supported by connector", ex);
+ }
+ catch (ResourceException ex) {
+ throw new DataAccessResourceFailureException("CCI operation failed", ex);
+ }
+ catch (SQLException ex) {
+ throw new InvalidResultSetAccessException("Parsing of CCI ResultSet failed", ex);
+ }
+ finally {
+ ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
+ }
+ }
+
+ @Override
+ public T execute(final InteractionCallback action) throws DataAccessException {
+ Assert.notNull(action, "Callback object must not be null");
+ return execute(new ConnectionCallback() {
+ @Override
+ public T doInConnection(Connection connection, ConnectionFactory connectionFactory)
+ throws ResourceException, SQLException, DataAccessException {
+ Interaction interaction = connection.createInteraction();
+ try {
+ return action.doInInteraction(interaction, connectionFactory);
+ }
+ finally {
+ closeInteraction(interaction);
+ }
+ }
+ });
+ }
+
+ @Override
+ public Record execute(InteractionSpec spec, Record inputRecord) throws DataAccessException {
+ return doExecute(spec, inputRecord, null, new SimpleRecordExtractor());
+ }
+
+ @Override
+ public void execute(InteractionSpec spec, Record inputRecord, Record outputRecord) throws DataAccessException {
+ doExecute(spec, inputRecord, outputRecord, null);
+ }
+
+ @Override
+ public Record execute(InteractionSpec spec, RecordCreator inputCreator) throws DataAccessException {
+ return doExecute(spec, createRecord(inputCreator), null, new SimpleRecordExtractor());
+ }
+
+ @Override
+ public T execute(InteractionSpec spec, Record inputRecord, RecordExtractor outputExtractor)
+ throws DataAccessException {
+
+ return doExecute(spec, inputRecord, null, outputExtractor);
+ }
+
+ @Override
+ public T execute(InteractionSpec spec, RecordCreator inputCreator, RecordExtractor outputExtractor)
+ throws DataAccessException {
+
+ return doExecute(spec, createRecord(inputCreator), null, outputExtractor);
+ }
+
+ /**
+ * Execute the specified interaction on an EIS with CCI.
+ * All other interaction execution methods go through this.
+ * @param spec the CCI InteractionSpec instance that defines
+ * the interaction (connector-specific)
+ * @param inputRecord the input record
+ * @param outputRecord output record (can be {@code null})
+ * @param outputExtractor object to convert the output record to a result object
+ * @return the output data extracted with the RecordExtractor object
+ * @throws DataAccessException if there is any problem
+ */
+ protected T doExecute(
+ final InteractionSpec spec, final Record inputRecord, final Record outputRecord,
+ final RecordExtractor outputExtractor) throws DataAccessException {
+
+ return execute(new InteractionCallback() {
+ @Override
+ public T doInInteraction(Interaction interaction, ConnectionFactory connectionFactory)
+ throws ResourceException, SQLException, DataAccessException {
+ Record outputRecordToUse = outputRecord;
+ try {
+ if (outputRecord != null || getOutputRecordCreator() != null) {
+ // Use the CCI execute method with output record as parameter.
+ if (outputRecord == null) {
+ RecordFactory recordFactory = getRecordFactory(connectionFactory);
+ outputRecordToUse = getOutputRecordCreator().createRecord(recordFactory);
+ }
+ interaction.execute(spec, inputRecord, outputRecordToUse);
+ }
+ else {
+ outputRecordToUse = interaction.execute(spec, inputRecord);
+ }
+ return (outputExtractor != null ? outputExtractor.extractData(outputRecordToUse) : null);
+ }
+ finally {
+ if (outputRecordToUse instanceof ResultSet) {
+ closeResultSet((ResultSet) outputRecordToUse);
+ }
+ }
+ }
+ });
+ }
+
+
+ /**
+ * Create an indexed Record through the ConnectionFactory's RecordFactory.
+ * @param name the name of the record
+ * @return the Record
+ * @throws DataAccessException if creation of the Record failed
+ * @see #getRecordFactory(javax.resource.cci.ConnectionFactory)
+ * @see javax.resource.cci.RecordFactory#createIndexedRecord(String)
+ */
+ public IndexedRecord createIndexedRecord(String name) throws DataAccessException {
+ try {
+ RecordFactory recordFactory = getRecordFactory(getConnectionFactory());
+ return recordFactory.createIndexedRecord(name);
+ }
+ catch (NotSupportedException ex) {
+ throw new RecordTypeNotSupportedException("Creation of indexed Record not supported by connector", ex);
+ }
+ catch (ResourceException ex) {
+ throw new CannotCreateRecordException("Creation of indexed Record failed", ex);
+ }
+ }
+
+ /**
+ * Create a mapped Record from the ConnectionFactory's RecordFactory.
+ * @param name record name
+ * @return the Record
+ * @throws DataAccessException if creation of the Record failed
+ * @see #getRecordFactory(javax.resource.cci.ConnectionFactory)
+ * @see javax.resource.cci.RecordFactory#createMappedRecord(String)
+ */
+ public MappedRecord createMappedRecord(String name) throws DataAccessException {
+ try {
+ RecordFactory recordFactory = getRecordFactory(getConnectionFactory());
+ return recordFactory.createMappedRecord(name);
+ }
+ catch (NotSupportedException ex) {
+ throw new RecordTypeNotSupportedException("Creation of mapped Record not supported by connector", ex);
+ }
+ catch (ResourceException ex) {
+ throw new CannotCreateRecordException("Creation of mapped Record failed", ex);
+ }
+ }
+
+ /**
+ * Invoke the given RecordCreator, converting JCA ResourceExceptions
+ * to Spring's DataAccessException hierarchy.
+ * @param recordCreator the RecordCreator to invoke
+ * @return the created Record
+ * @throws DataAccessException if creation of the Record failed
+ * @see #getRecordFactory(javax.resource.cci.ConnectionFactory)
+ * @see RecordCreator#createRecord(javax.resource.cci.RecordFactory)
+ */
+ protected Record createRecord(RecordCreator recordCreator) throws DataAccessException {
+ try {
+ RecordFactory recordFactory = getRecordFactory(getConnectionFactory());
+ return recordCreator.createRecord(recordFactory);
+ }
+ catch (NotSupportedException ex) {
+ throw new RecordTypeNotSupportedException(
+ "Creation of the desired Record type not supported by connector", ex);
+ }
+ catch (ResourceException ex) {
+ throw new CannotCreateRecordException("Creation of the desired Record failed", ex);
+ }
+ }
+
+ /**
+ * Return a RecordFactory for the given ConnectionFactory.
+ *
Default implementation returns the connector's RecordFactory if
+ * available, falling back to a NotSupportedRecordFactory placeholder.
+ * This allows to invoke a RecordCreator callback with a non-null
+ * RecordFactory reference in any case.
+ * @param connectionFactory the CCI ConnectionFactory
+ * @return the CCI RecordFactory for the ConnectionFactory
+ * @throws ResourceException if thrown by CCI methods
+ * @see com.fr.third.springframework.jca.cci.connection.NotSupportedRecordFactory
+ */
+ protected RecordFactory getRecordFactory(ConnectionFactory connectionFactory) throws ResourceException {
+ try {
+ return connectionFactory.getRecordFactory();
+ }
+ catch (NotSupportedException ex) {
+ return new NotSupportedRecordFactory();
+ }
+ }
+
+
+ /**
+ * Close the given CCI Interaction and ignore any thrown exception.
+ * This is useful for typical finally blocks in manual CCI code.
+ * @param interaction the CCI Interaction to close
+ * @see javax.resource.cci.Interaction#close()
+ */
+ private void closeInteraction(Interaction interaction) {
+ if (interaction != null) {
+ try {
+ interaction.close();
+ }
+ catch (ResourceException ex) {
+ logger.trace("Could not close CCI Interaction", ex);
+ }
+ catch (Throwable ex) {
+ // We don't trust the CCI driver: It might throw RuntimeException or Error.
+ logger.trace("Unexpected exception on closing CCI Interaction", ex);
+ }
+ }
+ }
+
+ /**
+ * Close the given CCI ResultSet and ignore any thrown exception.
+ * This is useful for typical finally blocks in manual CCI code.
+ * @param resultSet the CCI ResultSet to close
+ * @see javax.resource.cci.ResultSet#close()
+ */
+ private void closeResultSet(ResultSet resultSet) {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ }
+ catch (SQLException ex) {
+ logger.trace("Could not close CCI ResultSet", ex);
+ }
+ catch (Throwable ex) {
+ // We don't trust the CCI driver: It might throw RuntimeException or Error.
+ logger.trace("Unexpected exception on closing CCI ResultSet", ex);
+ }
+ }
+ }
+
+
+ private static class SimpleRecordExtractor implements RecordExtractor {
+
+ @Override
+ public Record extractData(Record record) {
+ return record;
+ }
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/core/ConnectionCallback.java b/fine-spring/src/com/fr/third/springframework/jca/cci/core/ConnectionCallback.java
new file mode 100644
index 000000000..297a910d5
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/core/ConnectionCallback.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.core;
+
+import java.sql.SQLException;
+
+import javax.resource.ResourceException;
+import javax.resource.cci.Connection;
+import javax.resource.cci.ConnectionFactory;
+
+import com.fr.third.springframework.dao.DataAccessException;
+
+/**
+ * Generic callback interface for code that operates on a CCI Connection.
+ * Allows to execute any number of operations on a single Connection,
+ * using any type and number of Interaction.
+ *
+ *
This is particularly useful for delegating to existing data access code
+ * that expects a Connection to work on and throws ResourceException. For newly
+ * written code, it is strongly recommended to use CciTemplate's more specific
+ * {@code execute} variants.
+ *
+ * @author Thierry Templier
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see CciTemplate#execute(ConnectionCallback)
+ * @see CciTemplate#execute(javax.resource.cci.InteractionSpec, javax.resource.cci.Record)
+ * @see CciTemplate#execute(javax.resource.cci.InteractionSpec, RecordCreator, RecordExtractor)
+ */
+public interface ConnectionCallback {
+
+ /**
+ * Gets called by {@code CciTemplate.execute} with an active CCI Connection.
+ * Does not need to care about activating or closing the Connection, or handling
+ * transactions.
+ *
+ *
If called without a thread-bound CCI transaction (initiated by
+ * CciLocalTransactionManager), the code will simply get executed on the CCI
+ * Connection with its transactional semantics. If CciTemplate is configured
+ * to use a JTA-aware ConnectionFactory, the CCI Connection and thus the callback
+ * code will be transactional if a JTA transaction is active.
+ *
+ *
Allows for returning a result object created within the callback, i.e.
+ * a domain object or a collection of domain objects. Note that there's special
+ * support for single step actions: see the {@code CciTemplate.execute}
+ * variants. A thrown RuntimeException is treated as application exception:
+ * it gets propagated to the caller of the template.
+ *
+ * @param connection active CCI Connection
+ * @param connectionFactory the CCI ConnectionFactory that the Connection was
+ * created with (gives access to RecordFactory and ResourceAdapterMetaData)
+ * @return a result object, or {@code null} if none
+ * @throws ResourceException if thrown by a CCI method, to be auto-converted
+ * to a DataAccessException
+ * @throws SQLException if thrown by a ResultSet method, to be auto-converted
+ * to a DataAccessException
+ * @throws DataAccessException in case of custom exceptions
+ * @see javax.resource.cci.ConnectionFactory#getRecordFactory()
+ * @see javax.resource.cci.ConnectionFactory#getMetaData()
+ * @see CciTemplate#execute(javax.resource.cci.InteractionSpec, RecordCreator, RecordExtractor)
+ */
+ T doInConnection(Connection connection, ConnectionFactory connectionFactory)
+ throws ResourceException, SQLException, DataAccessException;
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/core/InteractionCallback.java b/fine-spring/src/com/fr/third/springframework/jca/cci/core/InteractionCallback.java
new file mode 100644
index 000000000..2fc332f61
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/core/InteractionCallback.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.core;
+
+import java.sql.SQLException;
+
+import javax.resource.ResourceException;
+import javax.resource.cci.ConnectionFactory;
+import javax.resource.cci.Interaction;
+
+import com.fr.third.springframework.dao.DataAccessException;
+
+/**
+ * Generic callback interface for code that operates on a CCI Interaction.
+ * Allows to execute any number of operations on a single Interaction, for
+ * example a single execute call or repeated execute calls with varying
+ * parameters.
+ *
+ *
This is particularly useful for delegating to existing data access code
+ * that expects an Interaction to work on and throws ResourceException. For newly
+ * written code, it is strongly recommended to use CciTemplate's more specific
+ * {@code execute} variants.
+ *
+ * @author Thierry Templier
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see CciTemplate#execute(InteractionCallback)
+ * @see CciTemplate#execute(javax.resource.cci.InteractionSpec, javax.resource.cci.Record)
+ * @see CciTemplate#execute(javax.resource.cci.InteractionSpec, RecordCreator, RecordExtractor)
+ */
+public interface InteractionCallback {
+
+ /**
+ * Gets called by {@code CciTemplate.execute} with an active CCI Interaction.
+ * Does not need to care about activating or closing the Interaction, or
+ * handling transactions.
+ *
+ *
If called without a thread-bound CCI transaction (initiated by
+ * CciLocalTransactionManager), the code will simply get executed on the CCI
+ * Interaction with its transactional semantics. If CciTemplate is configured
+ * to use a JTA-aware ConnectionFactory, the CCI Interaction and thus the callback
+ * code will be transactional if a JTA transaction is active.
+ *
+ *
Allows for returning a result object created within the callback, i.e.
+ * a domain object or a collection of domain objects. Note that there's special
+ * support for single step actions: see the {@code CciTemplate.execute}
+ * variants. A thrown RuntimeException is treated as application exception:
+ * it gets propagated to the caller of the template.
+ *
+ * @param interaction active CCI Interaction
+ * @param connectionFactory the CCI ConnectionFactory that the Connection was
+ * created with (gives access to RecordFactory and ResourceAdapterMetaData)
+ * @return a result object, or {@code null} if none
+ * @throws ResourceException if thrown by a CCI method, to be auto-converted
+ * to a DataAccessException
+ * @throws SQLException if thrown by a ResultSet method, to be auto-converted
+ * to a DataAccessException
+ * @throws DataAccessException in case of custom exceptions
+ * @see javax.resource.cci.ConnectionFactory#getRecordFactory()
+ * @see javax.resource.cci.ConnectionFactory#getMetaData()
+ * @see CciTemplate#execute(javax.resource.cci.InteractionSpec, RecordCreator, RecordExtractor)
+ */
+ T doInInteraction(Interaction interaction, ConnectionFactory connectionFactory)
+ throws ResourceException, SQLException, DataAccessException;
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/core/RecordCreator.java b/fine-spring/src/com/fr/third/springframework/jca/cci/core/RecordCreator.java
new file mode 100644
index 000000000..d3287e98c
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/core/RecordCreator.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.core;
+
+import javax.resource.ResourceException;
+import javax.resource.cci.Record;
+import javax.resource.cci.RecordFactory;
+
+import com.fr.third.springframework.dao.DataAccessException;
+
+/**
+ * Callback interface for creating a CCI Record instance,
+ * usually based on the passed-in CCI RecordFactory.
+ *
+ *
Used for input Record creation in CciTemplate. Alternatively,
+ * Record instances can be passed into CciTemplate's corresponding
+ * {@code execute} methods directly, either instantiated manually
+ * or created through CciTemplate's Record factory methods.
+ *
+ *
Also used for creating default output Records in CciTemplate.
+ * This is useful when the JCA connector needs an explicit output Record
+ * instance, but no output Records should be passed into CciTemplate's
+ * {@code execute} methods.
+ *
+ * @author Thierry Templier
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see CciTemplate#execute(javax.resource.cci.InteractionSpec, RecordCreator)
+ * @see CciTemplate#execute(javax.resource.cci.InteractionSpec, RecordCreator, RecordExtractor)
+ * @see CciTemplate#createIndexedRecord(String)
+ * @see CciTemplate#createMappedRecord(String)
+ * @see CciTemplate#setOutputRecordCreator(RecordCreator)
+ */
+public interface RecordCreator {
+
+ /**
+ * Create a CCI Record instance, usually based on the passed-in CCI RecordFactory.
+ *
For use as input creator with CciTemplate's {@code execute} methods,
+ * this method should create a populated Record instance. For use as
+ * output Record creator, it should return an empty Record instance.
+ * @param recordFactory the CCI RecordFactory (never {@code null}, but not guaranteed to be
+ * supported by the connector: its create methods might throw NotSupportedException)
+ * @return the Record instance
+ * @throws ResourceException if thrown by a CCI method, to be auto-converted
+ * to a DataAccessException
+ * @throws DataAccessException in case of custom exceptions
+ */
+ Record createRecord(RecordFactory recordFactory) throws ResourceException, DataAccessException;
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/core/RecordExtractor.java b/fine-spring/src/com/fr/third/springframework/jca/cci/core/RecordExtractor.java
new file mode 100644
index 000000000..2edcc5934
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/core/RecordExtractor.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.core;
+
+import java.sql.SQLException;
+
+import javax.resource.ResourceException;
+import javax.resource.cci.Record;
+
+import com.fr.third.springframework.dao.DataAccessException;
+
+/**
+ * Callback interface for extracting a result object from a CCI Record instance.
+ *
+ *
Used for output object creation in CciTemplate. Alternatively, output
+ * Records can also be returned to client code as-is. In case of a CCI ResultSet
+ * as execution result, you will almost always want to implement a RecordExtractor,
+ * to be able to read the ResultSet in a managed fashion, with the CCI Connection
+ * still open while reading the ResultSet.
+ *
+ *
Implementations of this interface perform the actual work of extracting
+ * results, but don't need to worry about exception handling. ResourceExceptions
+ * will be caught and handled correctly by the CciTemplate class.
+ *
+ * @author Thierry Templier
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see CciTemplate#execute(javax.resource.cci.InteractionSpec, Record, RecordExtractor)
+ * @see CciTemplate#execute(javax.resource.cci.InteractionSpec, RecordCreator, RecordExtractor)
+ * @see javax.resource.cci.ResultSet
+ */
+public interface RecordExtractor {
+
+ /**
+ * Process the data in the given Record, creating a corresponding result object.
+ * @param record the Record to extract data from
+ * (possibly a CCI ResultSet)
+ * @return an arbitrary result object, or {@code null} if none
+ * (the extractor will typically be stateful in the latter case)
+ * @throws ResourceException if thrown by a CCI method, to be auto-converted
+ * to a DataAccessException
+ * @throws SQLException if thrown by a ResultSet method, to be auto-converted
+ * to a DataAccessException
+ * @throws DataAccessException in case of custom exceptions
+ * @see javax.resource.cci.ResultSet
+ */
+ T extractData(Record record) throws ResourceException, SQLException, DataAccessException;
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/core/package-info.java b/fine-spring/src/com/fr/third/springframework/jca/cci/core/package-info.java
new file mode 100644
index 000000000..151c601d8
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/core/package-info.java
@@ -0,0 +1,9 @@
+
+/**
+ *
+ * Provides the core JCA CCI support, based on CciTemplate
+ * and its associated callback interfaces.
+ *
+ */
+package com.fr.third.springframework.jca.cci.core;
+
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/core/support/CciDaoSupport.java b/fine-spring/src/com/fr/third/springframework/jca/cci/core/support/CciDaoSupport.java
new file mode 100644
index 000000000..1d90705ac
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/core/support/CciDaoSupport.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.core.support;
+
+import javax.resource.cci.Connection;
+import javax.resource.cci.ConnectionFactory;
+import javax.resource.cci.ConnectionSpec;
+
+import com.fr.third.springframework.dao.support.DaoSupport;
+import com.fr.third.springframework.jca.cci.CannotGetCciConnectionException;
+import com.fr.third.springframework.jca.cci.connection.ConnectionFactoryUtils;
+import com.fr.third.springframework.jca.cci.core.CciTemplate;
+
+/**
+ * Convenient super class for CCI-based data access objects.
+ *
+ *
Requires a {@link javax.resource.cci.ConnectionFactory} to be set,
+ * providing a {@link com.fr.third.springframework.jca.cci.core.CciTemplate} based
+ * on it to subclasses through the {@link #getCciTemplate()} method.
+ *
+ *
This base class is mainly intended for CciTemplate usage but can
+ * also be used when working with a Connection directly or when using
+ * {@code com.fr.third.springframework.jca.cci.object} classes.
+ *
+ * @author Thierry Templier
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see #setConnectionFactory
+ * @see #getCciTemplate
+ * @see com.fr.third.springframework.jca.cci.core.CciTemplate
+ */
+public abstract class CciDaoSupport extends DaoSupport {
+
+ private CciTemplate cciTemplate;
+
+
+ /**
+ * Set the ConnectionFactory to be used by this DAO.
+ */
+ public final void setConnectionFactory(ConnectionFactory connectionFactory) {
+ if (this.cciTemplate == null || connectionFactory != this.cciTemplate.getConnectionFactory()) {
+ this.cciTemplate = createCciTemplate(connectionFactory);
+ }
+ }
+
+ /**
+ * Create a CciTemplate for the given ConnectionFactory.
+ * Only invoked if populating the DAO with a ConnectionFactory reference!
+ *
Can be overridden in subclasses to provide a CciTemplate instance
+ * with different configuration, or a custom CciTemplate subclass.
+ * @param connectionFactory the CCI ConnectionFactory to create a CciTemplate for
+ * @return the new CciTemplate instance
+ * @see #setConnectionFactory(javax.resource.cci.ConnectionFactory)
+ */
+ protected CciTemplate createCciTemplate(ConnectionFactory connectionFactory) {
+ return new CciTemplate(connectionFactory);
+ }
+
+ /**
+ * Return the ConnectionFactory used by this DAO.
+ */
+ public final ConnectionFactory getConnectionFactory() {
+ return this.cciTemplate.getConnectionFactory();
+ }
+
+ /**
+ * Set the CciTemplate for this DAO explicitly,
+ * as an alternative to specifying a ConnectionFactory.
+ */
+ public final void setCciTemplate(CciTemplate cciTemplate) {
+ this.cciTemplate = cciTemplate;
+ }
+
+ /**
+ * Return the CciTemplate for this DAO,
+ * pre-initialized with the ConnectionFactory or set explicitly.
+ */
+ public final CciTemplate getCciTemplate() {
+ return this.cciTemplate;
+ }
+
+ @Override
+ protected final void checkDaoConfig() {
+ if (this.cciTemplate == null) {
+ throw new IllegalArgumentException("'connectionFactory' or 'cciTemplate' is required");
+ }
+ }
+
+
+ /**
+ * Obtain a CciTemplate derived from the main template instance,
+ * inheriting the ConnectionFactory and other settings but
+ * overriding the ConnectionSpec used for obtaining Connections.
+ * @param connectionSpec the CCI ConnectionSpec that the returned
+ * template instance is supposed to obtain Connections for
+ * @return the derived template instance
+ * @see com.fr.third.springframework.jca.cci.core.CciTemplate#getDerivedTemplate(javax.resource.cci.ConnectionSpec)
+ */
+ protected final CciTemplate getCciTemplate(ConnectionSpec connectionSpec) {
+ return getCciTemplate().getDerivedTemplate(connectionSpec);
+ }
+
+ /**
+ * Get a CCI Connection, either from the current transaction or a new one.
+ * @return the CCI Connection
+ * @throws com.fr.third.springframework.jca.cci.CannotGetCciConnectionException
+ * if the attempt to get a Connection failed
+ * @see com.fr.third.springframework.jca.cci.connection.ConnectionFactoryUtils#getConnection(javax.resource.cci.ConnectionFactory)
+ */
+ protected final Connection getConnection() throws CannotGetCciConnectionException {
+ return ConnectionFactoryUtils.getConnection(getConnectionFactory());
+ }
+
+ /**
+ * Close the given CCI Connection, created via this bean's ConnectionFactory,
+ * if it isn't bound to the thread.
+ * @param con Connection to close
+ * @see com.fr.third.springframework.jca.cci.connection.ConnectionFactoryUtils#releaseConnection
+ */
+ protected final void releaseConnection(Connection con) {
+ ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory());
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/core/support/CommAreaRecord.java b/fine-spring/src/com/fr/third/springframework/jca/cci/core/support/CommAreaRecord.java
new file mode 100644
index 000000000..d481d8e06
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/core/support/CommAreaRecord.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.core.support;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.resource.cci.Record;
+import javax.resource.cci.Streamable;
+
+import com.fr.third.springframework.util.FileCopyUtils;
+
+/**
+ * CCI Record implementation for a COMMAREA, holding a byte array.
+ *
+ * @author Thierry Templier
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see com.fr.third.springframework.jca.cci.object.MappingCommAreaOperation
+ */
+@SuppressWarnings("serial")
+public class CommAreaRecord implements Record, Streamable {
+
+ private byte[] bytes;
+
+ private String recordName;
+
+ private String recordShortDescription;
+
+
+ /**
+ * Create a new CommAreaRecord.
+ * @see #read(java.io.InputStream)
+ */
+ public CommAreaRecord() {
+ }
+
+ /**
+ * Create a new CommAreaRecord.
+ * @param bytes the bytes to fill the record with
+ */
+ public CommAreaRecord(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+
+ @Override
+ public void setRecordName(String recordName) {
+ this.recordName=recordName;
+ }
+
+ @Override
+ public String getRecordName() {
+ return recordName;
+ }
+
+ @Override
+ public void setRecordShortDescription(String recordShortDescription) {
+ this.recordShortDescription=recordShortDescription;
+ }
+
+ @Override
+ public String getRecordShortDescription() {
+ return recordShortDescription;
+ }
+
+
+ @Override
+ public void read(InputStream in) throws IOException {
+ this.bytes = FileCopyUtils.copyToByteArray(in);
+ }
+
+ @Override
+ public void write(OutputStream out) throws IOException {
+ out.write(this.bytes);
+ out.flush();
+ }
+
+ public byte[] toByteArray() {
+ return this.bytes;
+ }
+
+
+ @Override
+ public Object clone() {
+ return new CommAreaRecord(this.bytes);
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/core/support/package-info.java b/fine-spring/src/com/fr/third/springframework/jca/cci/core/support/package-info.java
new file mode 100644
index 000000000..c42047655
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/core/support/package-info.java
@@ -0,0 +1,8 @@
+/**
+ *
+ * Classes supporting the {@code com.fr.third.springframework.jca.cci.core} package.
+ * Contains a DAO base class for CciTemplate usage.
+ *
+ */
+package com.fr.third.springframework.jca.cci.core.support;
+
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/object/EisOperation.java b/fine-spring/src/com/fr/third/springframework/jca/cci/object/EisOperation.java
new file mode 100644
index 000000000..a6d13deb2
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/object/EisOperation.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.object;
+
+import javax.resource.cci.ConnectionFactory;
+import javax.resource.cci.InteractionSpec;
+
+import com.fr.third.springframework.beans.factory.InitializingBean;
+import com.fr.third.springframework.jca.cci.core.CciTemplate;
+
+/**
+ * Base class for EIS operation objects that work with the CCI API.
+ * Encapsulates a CCI ConnectionFactory and a CCI InteractionSpec.
+ *
+ *
Works with a CciTemplate instance underneath. EIS operation objects
+ * are an alternative to working with a CciTemplate directly.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see #setConnectionFactory
+ * @see #setInteractionSpec
+ */
+public abstract class EisOperation implements InitializingBean {
+
+ private CciTemplate cciTemplate = new CciTemplate();
+
+ private InteractionSpec interactionSpec;
+
+
+ /**
+ * Set the CciTemplate to be used by this operation.
+ * Alternatively, specify a CCI ConnectionFactory.
+ * @see #setConnectionFactory
+ */
+ public void setCciTemplate(CciTemplate cciTemplate) {
+ if (cciTemplate == null) {
+ throw new IllegalArgumentException("cciTemplate must not be null");
+ }
+ this.cciTemplate = cciTemplate;
+ }
+
+ /**
+ * Return the CciTemplate used by this operation.
+ */
+ public CciTemplate getCciTemplate() {
+ return this.cciTemplate;
+ }
+
+ /**
+ * Set the CCI ConnectionFactory to be used by this operation.
+ */
+ public void setConnectionFactory(ConnectionFactory connectionFactory) {
+ this.cciTemplate.setConnectionFactory(connectionFactory);
+ }
+
+ /**
+ * Set the CCI InteractionSpec for this operation.
+ */
+ public void setInteractionSpec(InteractionSpec interactionSpec) {
+ this.interactionSpec = interactionSpec;
+ }
+
+ /**
+ * Return the CCI InteractionSpec for this operation.
+ */
+ public InteractionSpec getInteractionSpec() {
+ return this.interactionSpec;
+ }
+
+
+ @Override
+ public void afterPropertiesSet() {
+ this.cciTemplate.afterPropertiesSet();
+
+ if (this.interactionSpec == null) {
+ throw new IllegalArgumentException("interactionSpec is required");
+ }
+ }
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/object/MappingCommAreaOperation.java b/fine-spring/src/com/fr/third/springframework/jca/cci/object/MappingCommAreaOperation.java
new file mode 100644
index 000000000..b6d35d9fc
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/object/MappingCommAreaOperation.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.object;
+
+import java.io.IOException;
+
+import javax.resource.cci.ConnectionFactory;
+import javax.resource.cci.InteractionSpec;
+import javax.resource.cci.Record;
+import javax.resource.cci.RecordFactory;
+
+import com.fr.third.springframework.dao.DataAccessException;
+import com.fr.third.springframework.dao.DataRetrievalFailureException;
+import com.fr.third.springframework.jca.cci.core.support.CommAreaRecord;
+
+/**
+ * EIS operation object for access to COMMAREA records.
+ * Subclass of the generic MappingRecordOperation class.
+ *
+ * @author Thierry Templier
+ * @since 1.2
+ */
+public abstract class MappingCommAreaOperation extends MappingRecordOperation {
+
+ /**
+ * Create a new MappingCommAreaQuery.
+ * @see #setConnectionFactory
+ * @see #setInteractionSpec
+ */
+ public MappingCommAreaOperation() {
+ }
+
+ /**
+ * Create a new MappingCommAreaQuery.
+ * @param connectionFactory ConnectionFactory to use to obtain connections
+ * @param interactionSpec specification to configure the interaction
+ */
+ public MappingCommAreaOperation(ConnectionFactory connectionFactory, InteractionSpec interactionSpec) {
+ super(connectionFactory, interactionSpec);
+ }
+
+
+ @Override
+ protected final Record createInputRecord(RecordFactory recordFactory, Object inObject) {
+ try {
+ return new CommAreaRecord(objectToBytes(inObject));
+ }
+ catch (IOException ex) {
+ throw new DataRetrievalFailureException("I/O exception during bytes conversion", ex);
+ }
+ }
+
+ @Override
+ protected final Object extractOutputData(Record record) throws DataAccessException {
+ CommAreaRecord commAreaRecord = (CommAreaRecord) record;
+ try {
+ return bytesToObject(commAreaRecord.toByteArray());
+ }
+ catch (IOException ex) {
+ throw new DataRetrievalFailureException("I/O exception during bytes conversion", ex);
+ }
+ }
+
+
+ /**
+ * Method used to convert an object into COMMAREA bytes.
+ * @param inObject the input data
+ * @return the COMMAREA's bytes
+ * @throws IOException if thrown by I/O methods
+ * @throws DataAccessException if conversion failed
+ */
+ protected abstract byte[] objectToBytes(Object inObject) throws IOException, DataAccessException;
+
+ /**
+ * Method used to convert the COMMAREA's bytes to an object.
+ * @param bytes the COMMAREA's bytes
+ * @return the output data
+ * @throws IOException if thrown by I/O methods
+ * @throws DataAccessException if conversion failed
+ */
+ protected abstract Object bytesToObject(byte[] bytes) throws IOException, DataAccessException;
+
+}
diff --git a/fine-spring/src/com/fr/third/springframework/jca/cci/object/MappingRecordOperation.java b/fine-spring/src/com/fr/third/springframework/jca/cci/object/MappingRecordOperation.java
new file mode 100644
index 000000000..3e7951d0d
--- /dev/null
+++ b/fine-spring/src/com/fr/third/springframework/jca/cci/object/MappingRecordOperation.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fr.third.springframework.jca.cci.object;
+
+import java.sql.SQLException;
+
+import javax.resource.ResourceException;
+import javax.resource.cci.ConnectionFactory;
+import javax.resource.cci.InteractionSpec;
+import javax.resource.cci.Record;
+import javax.resource.cci.RecordFactory;
+
+import com.fr.third.springframework.dao.DataAccessException;
+import com.fr.third.springframework.jca.cci.core.RecordCreator;
+import com.fr.third.springframework.jca.cci.core.RecordExtractor;
+
+/**
+ * EIS operation object that expects mapped input and output objects,
+ * converting to and from CCI Records.
+ *
+ *
Concrete subclasses must implement the abstract
+ * {@code createInputRecord(RecordFactory, Object)} and
+ * {@code extractOutputData(Record)} methods, to create an input
+ * Record from an object and to convert an output Record into an object,
+ * respectively.
+ *
+ * @author Thierry Templier
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see #createInputRecord(javax.resource.cci.RecordFactory, Object)
+ * @see #extractOutputData(javax.resource.cci.Record)
+ */
+public abstract class MappingRecordOperation extends EisOperation {
+
+ /**
+ * Constructor that allows use as a JavaBean.
+ */
+ public MappingRecordOperation() {
+ }
+
+ /**
+ * Convenient constructor with ConnectionFactory and specifications
+ * (connection and interaction).
+ * @param connectionFactory ConnectionFactory to use to obtain connections
+ */
+ public MappingRecordOperation(ConnectionFactory connectionFactory, InteractionSpec interactionSpec) {
+ getCciTemplate().setConnectionFactory(connectionFactory);
+ setInteractionSpec(interactionSpec);
+ }
+
+ /**
+ * Set a RecordCreator that should be used for creating default output Records.
+ *
Default is none: CCI's {@code Interaction.execute} variant
+ * that returns an output Record will be called.
+ *
Specify a RecordCreator here if you always need to call CCI's
+ * {@code Interaction.execute} variant with a passed-in output Record.
+ * This RecordCreator will then be invoked to create a default output Record instance.
+ * @see javax.resource.cci.Interaction#execute(javax.resource.cci.InteractionSpec, Record)
+ * @see javax.resource.cci.Interaction#execute(javax.resource.cci.InteractionSpec, Record, Record)
+ * @see com.fr.third.springframework.jca.cci.core.CciTemplate#setOutputRecordCreator
+ */
+ public void setOutputRecordCreator(RecordCreator creator) {
+ getCciTemplate().setOutputRecordCreator(creator);
+ }
+
+ /**
+ * Execute the interaction encapsulated by this operation object.
+ * @param inputObject the input data, to be converted to a Record
+ * by the {@code createInputRecord} method
+ * @return the output data extracted with the {@code extractOutputData} method
+ * @throws DataAccessException if there is any problem
+ * @see #createInputRecord
+ * @see #extractOutputData
+ */
+ public Object execute(Object inputObject) throws DataAccessException {
+ return getCciTemplate().execute(
+ getInteractionSpec(), new RecordCreatorImpl(inputObject), new RecordExtractorImpl());
+ }
+
+
+ /**
+ * Subclasses must implement this method to generate an input Record
+ * from an input object passed into the {@code execute} method.
+ * @param inputObject the passed-in input object
+ * @return the CCI input Record
+ * @throws ResourceException if thrown by a CCI method, to be auto-converted
+ * to a DataAccessException
+ * @see #execute(Object)
+ */
+ protected abstract Record createInputRecord(RecordFactory recordFactory, Object inputObject)
+ throws ResourceException, DataAccessException;
+
+ /**
+ * Subclasses must implement this method to convert the Record returned
+ * by CCI execution into a result object for the {@code execute} method.
+ * @param outputRecord the Record returned by CCI execution
+ * @return the result object
+ * @throws ResourceException if thrown by a CCI method, to be auto-converted
+ * to a DataAccessException
+ * @see #execute(Object)
+ */
+ protected abstract Object extractOutputData(Record outputRecord)
+ throws ResourceException, SQLException, DataAccessException;
+
+
+ /**
+ * Implementation of RecordCreator that calls the enclosing
+ * class's {@code createInputRecord} method.
+ */
+ protected class RecordCreatorImpl implements RecordCreator {
+
+ private final Object inputObject;
+
+ public RecordCreatorImpl(Object inObject) {
+ this.inputObject = inObject;
+ }
+
+ @Override
+ public Record createRecord(RecordFactory recordFactory) throws ResourceException, DataAccessException {
+ return createInputRecord(recordFactory, this.inputObject);
+ }
+ }
+
+
+ /**
+ * Implementation of RecordExtractor that calls the enclosing
+ * class's {@code extractOutputData} method.
+ */
+ protected class RecordExtractorImpl implements RecordExtractor