blob: 00961708697cacd8996e56432d5511061794572d [file] [log] [blame]
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