| java.lang.Object org.springframework.transaction.support.AbstractPlatformTransactionManager org.springframework.transaction.jta.JtaTransactionManager
All known Subclasses: org.springframework.transaction.jta.OC4JJtaTransactionManager, org.springframework.transaction.jta.WebLogicJtaTransactionManager,
JtaTransactionManager | public class JtaTransactionManager extends AbstractPlatformTransactionManager implements InitializingBean,Serializable(Code) | | org.springframework.transaction.PlatformTransactionManager implementation
for JTA, delegating to a backend JTA provider. This is typically used to delegate
to a J2EE server's transaction coordinator, but may also be configured with a
local JTA provider which is embedded within the application.
This transaction manager is appropriate for handling distributed transactions,
i.e. transactions that span multiple resources, and for controlling transactions on
application server resources (e.g. JDBC DataSources available in JNDI) in general.
For a single JDBC DataSource, DataSourceTransactionManager is perfectly sufficient,
and for accessing a single resource with Hibernate (including transactional cache),
HibernateTransactionManager is appropriate, for example.
Transaction synchronization is active by default, to allow data access support
classes to register resources that are opened within the transaction for closing at
transaction completion time. Spring's support classes for JDBC, Hibernate, JDO etc
all perform such registration, allowing for reuse of the same Hibernate Session etc
within the transaction. Standard JTA does not even guarantee that for Connections
from a transactional JDBC DataSource: Spring's synchronization solves those issues.
For typical JTA transactions (REQUIRED, SUPPORTS, MANDATORY, NEVER), a plain
JtaTransactionManager definition is all you need, completely portable across all
J2EE servers. This corresponds to the functionality of the JTA UserTransaction,
for which J2EE specifies a standard JNDI name ("java:comp/UserTransaction").
There is no need to configure a server-specific TransactionManager lookup for this
kind of JTA usage.
Note: Advanced JTA usage below. Dealing with these mechanisms is not
necessary for typical usage scenarios.
Transaction suspension (REQUIRES_NEW, NOT_SUPPORTED) is just available with
a JTA TransactionManager being registered, via the "transactionManagerName" or
"transactionManager" property. The location of this well-defined JTA object is
not specified by J2EE; it is specific to each J2EE server, often kept
in JNDI like the JTA UserTransaction. Some well-known JNDI locations are:
- "java:comp/UserTransaction" for Resin 2.x, Oracle OC4J (Orion),
JOnAS (JOTM), BEA WebLogic
- "java:comp/TransactionManager" for Resin 3.x
- "java:pm/TransactionManager" for Borland Enterprise Server and
Sun Application Server (Sun ONE 7 and later)
- "java:/TransactionManager" for JBoss Application Server
All of these cases are autodetected by JtaTransactionManager (since Spring 1.2),
provided that the "autodetectTransactionManager" flag is set to "true" (which it is
by default). Consequently, JtaTransactionManager will support transaction suspension
out-of-the-box on many J2EE servers.
A JNDI lookup can also be factored out into a corresponding
org.springframework.jndi.JndiObjectFactoryBean , passed into
JtaTransactionManager's "transactionManager" property. Such a bean definition
can then be reused by other objects, for example Spring's LocalSessionFactoryBean
for Hibernate.
For IBM WebSphere and standalone JOTM, static accessor methods are required to
obtain the JTA TransactionManager: Therefore, WebSphere and JOTM have their own
FactoryBean implementations, to be wired with the "transactionManager" property.
In case of
JotmFactoryBean , the same JTA object implements UserTransaction
too: Therefore, passing the object to the "userTransaction" property is sufficient.
It is also possible to specify a JTA TransactionManager only, either through
the corresponding constructor or through the "transactionManager" property.
In the latter case, the "userTransactionName" property needs to be set to null ,
to suppress the default "java:comp/UserTransaction" JNDI lookup and thus enforce
use of the given JTA TransactionManager even for begin, commit and rollback.
Note: Support for the JTA TransactionManager interface is not required by J2EE.
Almost all J2EE servers expose it, but do so as extension to J2EE. There might be some
issues with compatibility, despite the TransactionManager interface being part of JTA.
The only currently known problem is resuming a transaction on WebLogic, which by default
fails if the suspended transaction was marked rollback-only; for other usages, it works
properly. Use Spring's
WebLogicJtaTransactionManager to address this issue.
This standard JtaTransactionManager supports timeouts but not per-transaction
isolation levels. Custom subclasses may override
JtaTransactionManager.doJtaBegin for
specific JTA extensions in order to provide this functionality; Spring includes
corresponding
WebLogicJtaTransactionManager and
OC4JJtaTransactionManager classes, for BEA's WebLogic Server and Oracle's OC4J, respectively. Such adapters
for specific J2EE transaction coordinators may also expose transaction names for
monitoring (both of the above do); with standard JTA, transaction names will simply
be ignored.
This class is serializable. However, active synchronizations do not survive
serialization.
author: Juergen Hoeller since: 24.03.2003 See Also: JtaTransactionManager.setUserTransactionName See Also: JtaTransactionManager.setUserTransaction See Also: JtaTransactionManager.setTransactionManagerName See Also: JtaTransactionManager.setTransactionManager See Also: JtaTransactionManager.doJtaBegin See Also: JotmFactoryBean See Also: WebSphereTransactionManagerFactoryBean See Also: WebLogicJtaTransactionManager See Also: org.springframework.jndi.JndiObjectFactoryBean See Also: org.springframework.orm.hibernate.LocalSessionFactoryBean.setJtaTransactionManager |
Constructor Summary | |
public | JtaTransactionManager() Create a new JtaTransactionManager instance, to be configured as bean. | public | JtaTransactionManager(UserTransaction userTransaction) Create a new JtaTransactionManager instance. | public | JtaTransactionManager(UserTransaction userTransaction, TransactionManager transactionManager) Create a new JtaTransactionManager instance. | public | JtaTransactionManager(TransactionManager transactionManager) Create a new JtaTransactionManager instance. |
Method Summary | |
public void | afterPropertiesSet() Initialize the UserTransaction as well as the TransactionManager handle. | protected void | applyIsolationLevel(JtaTransactionObject txObject, int isolationLevel) Apply the given transaction isolation level. | protected void | applyTimeout(JtaTransactionObject txObject, int timeout) Apply the given transaction timeout. | protected UserTransaction | buildUserTransaction(TransactionManager transactionManager) Build a UserTransaction handle based on the given TransactionManager. | protected void | doBegin(Object transaction, TransactionDefinition definition) | protected void | doCommit(DefaultTransactionStatus status) | protected JtaTransactionObject | doGetJtaTransaction(UserTransaction ut) Get a JTA transaction object for the given current UserTransaction. | protected Object | doGetTransaction() This implementation returns a JtaTransactionObject instance for the
JTA UserTransaction.
The UserTransaction object will either be looked up freshly for the
current transaction, or the cached one looked up at startup will be used.
The latter is the default: Most application servers use a shared singleton
UserTransaction that can be cached. | protected void | doJtaBegin(JtaTransactionObject txObject, TransactionDefinition definition) Perform a JTA begin on the JTA UserTransaction or TransactionManager. | protected void | doJtaResume(JtaTransactionObject txObject, Object suspendedTransaction) Perform a JTA resume on the JTA TransactionManager. | protected Object | doJtaSuspend(JtaTransactionObject txObject) Perform a JTA suspend on the JTA TransactionManager. | protected void | doRegisterAfterCompletionWithJtaTransaction(JtaTransactionObject txObject, List synchronizations) Register a JTA synchronization on the JTA TransactionManager, for calling
afterCompletion on the given Spring TransactionSynchronizations. | protected void | doResume(Object transaction, Object suspendedResources) | protected void | doRollback(DefaultTransactionStatus status) | protected void | doSetRollbackOnly(DefaultTransactionStatus status) | protected Object | doSuspend(Object transaction) | protected TransactionManager | findTransactionManager(UserTransaction ut) Find the JTA TransactionManager through autodetection: checking whether the
UserTransaction object implements the TransactionManager, and checking the
fallback JNDI locations. | protected UserTransaction | findUserTransaction() Find the JTA UserTransaction through a default JNDI lookup:
"java:comp/UserTransaction". | public Properties | getJndiEnvironment() Return the JNDI environment to use for JNDI lookups. | public JndiTemplate | getJndiTemplate() Return the JndiTemplate used for JNDI lookups. | public TransactionManager | getTransactionManager() Return the JTA TransactionManager that this transaction manager uses. | public UserTransaction | getUserTransaction() Return the JTA UserTransaction that this transaction manager uses. | protected void | initUserTransactionAndTransactionManager() Initialize the UserTransaction as well as the TransactionManager handle. | protected boolean | isExistingTransaction(Object transaction) | protected TransactionManager | lookupTransactionManager(String transactionManagerName) Look up the JTA TransactionManager in JNDI via the configured name. | protected UserTransaction | lookupUserTransaction(String userTransactionName) Look up the JTA UserTransaction in JNDI via the configured name. | protected void | registerAfterCompletionWithExistingTransaction(Object transaction, List synchronizations) | protected TransactionManager | retrieveTransactionManager() Allows subclasses to retrieve the JTA TransactionManager in a vendor-specific manner. | protected UserTransaction | retrieveUserTransaction() Allows subclasses to retrieve the JTA UserTransaction in a vendor-specific manner. | public void | setAllowCustomIsolationLevels(boolean allowCustomIsolationLevels) Set whether to allow custom isolation levels to be specified.
Default is "false", throwing an exception if a non-default isolation level
is specified for a transaction. | public void | setAutodetectTransactionManager(boolean autodetectTransactionManager) Set whether to autodetect a JTA UserTransaction object that implements
the JTA TransactionManager interface too (i.e. | public void | setAutodetectUserTransaction(boolean autodetectUserTransaction) Set whether to autodetect the JTA UserTransaction at its default
JNDI location "java:comp/UserTransaction", as specified by J2EE.
Will proceed without UserTransaction if none found.
Default is "true", autodetecting the UserTransaction unless
it has been specified explicitly. | public void | setCacheUserTransaction(boolean cacheUserTransaction) Set whether to cache the JTA UserTransaction object fetched from JNDI.
Default is "true": UserTransaction lookup will only happen at startup,
reusing the same UserTransaction handle for all transactions of all threads.
This is the most efficient choice for all application servers that provide
a shared UserTransaction object (the typical case).
Turn this flag off to enforce a fresh lookup of the UserTransaction
for every transaction. | public void | setJndiEnvironment(Properties jndiEnvironment) Set the JNDI environment to use for JNDI lookups. | public void | setJndiTemplate(JndiTemplate jndiTemplate) Set the JndiTemplate to use for JNDI lookups. | public void | setTransactionManager(TransactionManager transactionManager) Set the JTA TransactionManager to use as direct reference. | public void | setTransactionManagerName(String transactionManagerName) Set the JNDI name of the JTA TransactionManager. | public void | setUserTransaction(UserTransaction userTransaction) Set the JTA UserTransaction to use as direct reference. | public void | setUserTransactionName(String userTransactionName) Set the JNDI name of the JTA UserTransaction. | protected boolean | shouldCommitOnGlobalRollbackOnly() This implementation returns "true": a JTA commit will properly handle
transactions that have been marked rollback-only at a global level. | protected boolean | useSavepointForNestedTransaction() This implementation returns false to cause a further invocation
of doBegin despite an already existing transaction. |
JtaTransactionManager | public JtaTransactionManager(UserTransaction userTransaction)(Code) | | Create a new JtaTransactionManager instance.
Parameters: userTransaction - the JTA UserTransaction to use as direct reference |
JtaTransactionManager | public JtaTransactionManager(UserTransaction userTransaction, TransactionManager transactionManager)(Code) | | Create a new JtaTransactionManager instance.
Parameters: userTransaction - the JTA UserTransaction to use as direct reference Parameters: transactionManager - the JTA TransactionManager to use as direct reference |
JtaTransactionManager | public JtaTransactionManager(TransactionManager transactionManager)(Code) | | Create a new JtaTransactionManager instance.
Parameters: transactionManager - the JTA TransactionManager to use as direct reference |
applyTimeout | protected void applyTimeout(JtaTransactionObject txObject, int timeout) throws SystemException(Code) | | Apply the given transaction timeout. The default implementation will call
UserTransaction.setTransactionTimeout for a non-default timeout value.
Parameters: txObject - the JtaTransactionObject containing the UserTransaction Parameters: timeout - timeout value taken from transaction definition throws: SystemException - if thrown by the JTA implementation See Also: JtaTransactionManager.doJtaBegin See Also: JtaTransactionObject.getUserTransaction See Also: javax.transaction.UserTransaction.setTransactionTimeout(int) |
buildUserTransaction | protected UserTransaction buildUserTransaction(TransactionManager transactionManager)(Code) | | Build a UserTransaction handle based on the given TransactionManager.
Parameters: transactionManager - the TransactionManager a corresponding UserTransaction handle |
doGetJtaTransaction | protected JtaTransactionObject doGetJtaTransaction(UserTransaction ut)(Code) | | Get a JTA transaction object for the given current UserTransaction.
Subclasses can override this to provide a JtaTransactionObject
subclass, for example holding some additional JTA handle needed.
Parameters: ut - the UserTransaction handle to use for the current transaction the JtaTransactionObject holding the UserTransaction |
doGetTransaction | protected Object doGetTransaction()(Code) | | This implementation returns a JtaTransactionObject instance for the
JTA UserTransaction.
The UserTransaction object will either be looked up freshly for the
current transaction, or the cached one looked up at startup will be used.
The latter is the default: Most application servers use a shared singleton
UserTransaction that can be cached. Turn off the "cacheUserTransaction"
flag to enforce a fresh lookup for every transaction.
See Also: JtaTransactionManager.setCacheUserTransaction |
doJtaResume | protected void doJtaResume(JtaTransactionObject txObject, Object suspendedTransaction) throws InvalidTransactionException, SystemException(Code) | | Perform a JTA resume on the JTA TransactionManager.
Can be overridden in subclasses, for specific JTA implementations.
Parameters: txObject - the JtaTransactionObject containing the UserTransaction Parameters: suspendedTransaction - the suspended JTA Transaction object throws: InvalidTransactionException - if thrown by JTA methods throws: SystemException - if thrown by JTA methods See Also: JtaTransactionManager.getTransactionManager() See Also: javax.transaction.TransactionManager.resume(javax.transaction.Transaction) |
doJtaSuspend | protected Object doJtaSuspend(JtaTransactionObject txObject) throws SystemException(Code) | | Perform a JTA suspend on the JTA TransactionManager.
Can be overridden in subclasses, for specific JTA implementations.
Parameters: txObject - the JtaTransactionObject containing the UserTransaction the suspended JTA Transaction object throws: SystemException - if thrown by JTA methods See Also: JtaTransactionManager.getTransactionManager() See Also: javax.transaction.TransactionManager.suspend |
doRegisterAfterCompletionWithJtaTransaction | protected void doRegisterAfterCompletionWithJtaTransaction(JtaTransactionObject txObject, List synchronizations) throws RollbackException, SystemException(Code) | | Register a JTA synchronization on the JTA TransactionManager, for calling
afterCompletion on the given Spring TransactionSynchronizations.
Can be overridden in subclasses, for specific JTA implementations.
Parameters: synchronizations - List of TransactionSynchronization objects throws: RollbackException - if thrown by JTA methods throws: SystemException - if thrown by JTA methods See Also: JtaTransactionManager.getTransactionManager() See Also: javax.transaction.Transaction.registerSynchronization See Also: JtaTransactionManager.invokeAfterCompletion(java.util.List,int) |
findTransactionManager | protected TransactionManager findTransactionManager(UserTransaction ut)(Code) | | Find the JTA TransactionManager through autodetection: checking whether the
UserTransaction object implements the TransactionManager, and checking the
fallback JNDI locations.
Parameters: ut - the JTA UserTransaction object the JTA TransactionManager reference, or null if not found See Also: JtaTransactionManager.FALLBACK_TRANSACTION_MANAGER_NAMES |
getJndiEnvironment | public Properties getJndiEnvironment()(Code) | | Return the JNDI environment to use for JNDI lookups.
|
getJndiTemplate | public JndiTemplate getJndiTemplate()(Code) | | Return the JndiTemplate used for JNDI lookups.
|
getTransactionManager | public TransactionManager getTransactionManager()(Code) | | Return the JTA TransactionManager that this transaction manager uses.
|
getUserTransaction | public UserTransaction getUserTransaction()(Code) | | Return the JTA UserTransaction that this transaction manager uses.
|
isExistingTransaction | protected boolean isExistingTransaction(Object transaction)(Code) | | |
registerAfterCompletionWithExistingTransaction | protected void registerAfterCompletionWithExistingTransaction(Object transaction, List synchronizations)(Code) | | |
setAllowCustomIsolationLevels | public void setAllowCustomIsolationLevels(boolean allowCustomIsolationLevels)(Code) | | Set whether to allow custom isolation levels to be specified.
Default is "false", throwing an exception if a non-default isolation level
is specified for a transaction. Turn this flag on if affected resource adapters
check the thread-bound transaction context and apply the specified isolation
levels individually (e.g. through a IsolationLevelDataSourceRouter).
See Also: org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter |
setAutodetectTransactionManager | public void setAutodetectTransactionManager(boolean autodetectTransactionManager)(Code) | | Set whether to autodetect a JTA UserTransaction object that implements
the JTA TransactionManager interface too (i.e. the JNDI location for the
TransactionManager is "java:comp/UserTransaction", same as for the UserTransaction).
Also checks the fallback JNDI locations "java:comp/TransactionManager" and
"java:/TransactionManager". Will proceed without TransactionManager if none found.
Default is "true", autodetecting the TransactionManager unless it has been
specified explicitly. Can be turned off to deliberately ignore an available
TransactionManager, for example when there are known issues with suspend/resume
and any attempt to use REQUIRES_NEW or NOT_SUPPORTED should fail fast.
See Also: JtaTransactionManager.FALLBACK_TRANSACTION_MANAGER_NAMES |
setAutodetectUserTransaction | public void setAutodetectUserTransaction(boolean autodetectUserTransaction)(Code) | | Set whether to autodetect the JTA UserTransaction at its default
JNDI location "java:comp/UserTransaction", as specified by J2EE.
Will proceed without UserTransaction if none found.
Default is "true", autodetecting the UserTransaction unless
it has been specified explicitly. Turn this flag off to allow for
JtaTransactionManager operating against the TransactionManager only,
despite a default UserTransaction being available.
See Also: JtaTransactionManager.DEFAULT_USER_TRANSACTION_NAME |
setCacheUserTransaction | public void setCacheUserTransaction(boolean cacheUserTransaction)(Code) | | Set whether to cache the JTA UserTransaction object fetched from JNDI.
Default is "true": UserTransaction lookup will only happen at startup,
reusing the same UserTransaction handle for all transactions of all threads.
This is the most efficient choice for all application servers that provide
a shared UserTransaction object (the typical case).
Turn this flag off to enforce a fresh lookup of the UserTransaction
for every transaction. This is only necessary for application servers
that return a new UserTransaction for every transaction, keeping state
tied to the UserTransaction object itself rather than the current thread.
See Also: JtaTransactionManager.setUserTransactionName |
setJndiTemplate | public void setJndiTemplate(JndiTemplate jndiTemplate)(Code) | | Set the JndiTemplate to use for JNDI lookups.
A default one is used if not set.
|
setTransactionManager | public void setTransactionManager(TransactionManager transactionManager)(Code) | | Set the JTA TransactionManager to use as direct reference.
A TransactionManager is necessary for suspending and resuming transactions,
as this not supported by the UserTransaction interface.
Note that the TransactionManager will be autodetected if the JTA
UserTransaction object implements the JTA TransactionManager interface too,
as well as autodetected at various well-known fallback JNDI locations.
See Also: JtaTransactionManager.setTransactionManagerName See Also: JtaTransactionManager.setAutodetectTransactionManager |
setTransactionManagerName | public void setTransactionManagerName(String transactionManagerName)(Code) | | Set the JNDI name of the JTA TransactionManager.
A TransactionManager is necessary for suspending and resuming transactions,
as this not supported by the UserTransaction interface.
Note that the TransactionManager will be autodetected if the JTA
UserTransaction object implements the JTA TransactionManager interface too,
as well as autodetected at various well-known fallback JNDI locations.
See Also: JtaTransactionManager.setTransactionManager See Also: JtaTransactionManager.setAutodetectTransactionManager |
shouldCommitOnGlobalRollbackOnly | protected boolean shouldCommitOnGlobalRollbackOnly()(Code) | | This implementation returns "true": a JTA commit will properly handle
transactions that have been marked rollback-only at a global level.
|
useSavepointForNestedTransaction | protected boolean useSavepointForNestedTransaction()(Code) | | This implementation returns false to cause a further invocation
of doBegin despite an already existing transaction.
JTA implementations might support nested transactions via further
UserTransaction.begin() invocations, but never support savepoints.
See Also: JtaTransactionManager.doBegin See Also: javax.transaction.UserTransaction.begin |
Fields inherited from org.springframework.transaction.support.AbstractPlatformTransactionManager | final public static int SYNCHRONIZATION_ALWAYS(Code)(Java Doc) final public static int SYNCHRONIZATION_NEVER(Code)(Java Doc) final public static int SYNCHRONIZATION_ON_ACTUAL_TRANSACTION(Code)(Java Doc) protected transient Log logger(Code)(Java Doc)
|
Methods inherited from org.springframework.transaction.support.AbstractPlatformTransactionManager | final public void commit(TransactionStatus status) throws TransactionException(Code)(Java Doc) protected int determineTimeout(TransactionDefinition definition)(Code)(Java Doc) abstract protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException(Code)(Java Doc) protected void doCleanupAfterCompletion(Object transaction)(Code)(Java Doc) abstract protected void doCommit(DefaultTransactionStatus status) throws TransactionException(Code)(Java Doc) abstract protected Object doGetTransaction() throws TransactionException(Code)(Java Doc) protected void doResume(Object transaction, Object suspendedResources) throws TransactionException(Code)(Java Doc) abstract protected void doRollback(DefaultTransactionStatus status) throws TransactionException(Code)(Java Doc) protected void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException(Code)(Java Doc) protected Object doSuspend(Object transaction) throws TransactionException(Code)(Java Doc) final public int getDefaultTimeout()(Code)(Java Doc) final public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException(Code)(Java Doc) final public int getTransactionSynchronization()(Code)(Java Doc) final protected void invokeAfterCompletion(List synchronizations, int completionStatus)(Code)(Java Doc) protected boolean isExistingTransaction(Object transaction) throws TransactionException(Code)(Java Doc) final public boolean isFailEarlyOnGlobalRollbackOnly()(Code)(Java Doc) final public boolean isGlobalRollbackOnParticipationFailure()(Code)(Java Doc) final public boolean isNestedTransactionAllowed()(Code)(Java Doc) final public boolean isRollbackOnCommitFailure()(Code)(Java Doc) protected DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, Object transaction, boolean newTransaction, boolean newSynchronization, boolean debug, Object suspendedResources)(Code)(Java Doc) protected void registerAfterCompletionWithExistingTransaction(Object transaction, List synchronizations) throws TransactionException(Code)(Java Doc) final protected void resume(Object transaction, SuspendedResourcesHolder resourcesHolder) throws TransactionException(Code)(Java Doc) final public void rollback(TransactionStatus status) throws TransactionException(Code)(Java Doc) final public void setDefaultTimeout(int defaultTimeout)(Code)(Java Doc) final public void setFailEarlyOnGlobalRollbackOnly(boolean failEarlyOnGlobalRollbackOnly)(Code)(Java Doc) final public void setGlobalRollbackOnParticipationFailure(boolean globalRollbackOnParticipationFailure)(Code)(Java Doc) final public void setNestedTransactionAllowed(boolean nestedTransactionAllowed)(Code)(Java Doc) final public void setRollbackOnCommitFailure(boolean rollbackOnCommitFailure)(Code)(Java Doc) final public void setTransactionSynchronization(int transactionSynchronization)(Code)(Java Doc) final public void setTransactionSynchronizationName(String constantName)(Code)(Java Doc) protected boolean shouldCommitOnGlobalRollbackOnly()(Code)(Java Doc) final protected SuspendedResourcesHolder suspend(Object transaction) throws TransactionException(Code)(Java Doc) final protected void triggerBeforeCommit(DefaultTransactionStatus status)(Code)(Java Doc) final protected void triggerBeforeCompletion(DefaultTransactionStatus status)(Code)(Java Doc) protected boolean useSavepointForNestedTransaction()(Code)(Java Doc)
|
|
|