| Title: |
| Notice: Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| . |
| http://www.apache.org/licenses/LICENSE-2.0 |
| . |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| |
| #Making your own Resource Provider |
| |
| The <code>TransactionControl</code> service is based around the use of <code>ResourceProvider</code> |
| instances. When combined the client receives a thread-safe resource instance that they can use. |
| |
| Resource Provider implementations already exist for [JDBC][1], [XA JDBC][2], [JPA][3] and [XA JPA][4] |
| |
| ##Create an interface |
| |
| If you need to create your own Resource Provider then you should define a sub-interface which declares the |
| type of the resource. This allows clients type-safe access to the resources, and makes it easier to identify |
| the right Resource Provider at runtime. |
| |
| public interface MyCustomResourceProvider extends |
| ResourceProvider<MyCustomResource> {} |
| |
| ## Create an implementation |
| |
| The implementation of a ResourceProvider should return a thread-safe delegating proxy. This may be a |
| dynamic proxy or a static one. The proxy delegates to the real resource, and is responsible for ensuring |
| that the thread consistently accesses the same physical resource throughout the scope. Note that even |
| when multiple proxies are created they should share the same physical resource throughout the life of the |
| context. |
| |
| ### Lifecycle - first access |
| |
| Whenever the resource is accessed then it must check to see whether it has already been accessed |
| within the current scope. If not then a resource should be selected and associated with the scope. This |
| physical resource must then be used for the rest of the scope. |
| |
| Note that if there is no active scope when the resource is accessed then the resource access should |
| fail with a TransactionException. |
| |
| /** |
| * A resource method |
| */ |
| @Override |
| public boolean doSomething() { |
| if(!txControl.activeScope()) { |
| throw new TransactionException("There is no scope currently active"); |
| } |
| |
| // Locate, or create and associate |
| MyCustomResource delegate = locateDelegate(txControl.getCurrentContext()); |
| |
| return delegate.doSomething(); |
| } |
| |
| ### Lifecycle - enlisting |
| |
| If a transaction is active then the resource should enlist with it when it is first used. If the resource is |
| usable with XA transactions then it should register an XAResource. |
| |
| private MyCustomResource locateDelegate(TransactionContext context) { |
| MyCustomResource resource = findExisting(context); |
| |
| if(resource != null) { |
| resource = getNewDelegate(context); |
| |
| if(context.activeTransaction()) { |
| if(context.supportsXA()) { |
| context.registerXAResource(resource.getXAResource()); |
| } else { |
| throw new TransactionException("The transaction does not support XA resources"); |
| } |
| } |
| // Other resource preparation |
| ... |
| } |
| |
| return resource; |
| } |
| |
| Local resources are similar, but they register a LocalResource, not an XAResource. |
| |
| private MyCustomResource locateDelegate(TransactionContext context) { |
| MyCustomResource resource = findExisting(context); |
| |
| if(resource != null) { |
| resource = getNewDelegate(context); |
| |
| if(context.activeTransaction()) { |
| if(context.supportsLocal()) { |
| context.registerLocalResource(resource.getLocalesource()); |
| } else { |
| throw new TransactionException("The transaction does not support Local resources"); |
| } |
| } |
| // Other resource preparation |
| ... |
| } |
| |
| return resource; |
| } |
| |
| ### Lifecycle - Tidying up |
| |
| In addition to registering with an active transaction the resource should also register for cleanup at the end |
| of the scope. This may involve closing the physical resource, or returning it to a pool. |
| |
| // Other resource preparation |
| |
| context.postCompletion(s -> resource.release()); |
| |
| ##Other things to look out for... |
| |
| 1. A client must have a friendly way to access the ResourceProvider. This may be directly via the OSGi Service |
| Registry or via a factory service of some kind. |
| |
| 1. A resource must remain valid throughout a scope, therefore clients should not be able to close or return the |
| resource by making calls on the thread-safe proxy. Typically calls to close should be a silent no-op (the |
| actual close will occur when the scope ends). |
| |
| 1. When a transaction is active clients must not be able to manually commit or rollback the resource. |
| Methods like <code>commit</code>, <code>rollback</code> and <code>setRollbackOnly</code> must |
| fail with a TransactionException indicating the incorrect usage. This is different from <code>close</code> |
| behaviour because unlike deferring a <code>close</code> ignoring a <code>rollback</code> results in a |
| different overall result from the client's perspective. |
| |
| 1. Resources must not rely on the Transaction Control service recognising duplicate enlistments. A resource |
| should be enlisted at most once. |
| |
| |
| [1]: localJDBC.html |
| [2]: xaJDBC.html |
| [3]: localJPA.html |
| [4]: xaJPA.html |