Skip to content

Commit

Permalink
[ARIES-1475] Implement per-method transaction support.
Browse files Browse the repository at this point in the history
git-svn-id: https://svn.apache.org/repos/asf/aries/trunk@1724230 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
cschneider committed Jan 12, 2016
1 parent d1eab6d commit fff2e99
Show file tree
Hide file tree
Showing 13 changed files with 295 additions and 53 deletions.
Expand Up @@ -44,7 +44,7 @@ public class Generator implements PropertyWriter {
public static final String NS_JPA2 = "http://aries.apache.org/xmlns/jpa/v2.0.0";
public static final String NS_TX = "http://aries.apache.org/xmlns/transactions/v1.2.0";
public static final String NS_TX2 = "http://aries.apache.org/xmlns/transactions/v2.0.0";

private Context context;
private XMLStreamWriter writer;
private Set<String> namespaces;
Expand All @@ -65,7 +65,7 @@ public void generate() {
writer.writeCharacters("\n");
writeBlueprint();
writer.writeCharacters("\n");

if (namespaces.contains(NS_JPA2) && isJpaUsed()) {
writer.writeEmptyElement(NS_JPA2, "enable");
writer.writeCharacters("\n");
Expand All @@ -84,10 +84,10 @@ public void generate() {
writer.writeEndElement();
writer.writeCharacters("\n");
}

new OsgiServiceRefWriter(writer).write(context.getServiceRefs());
new OsgiServiceProviderWriter(writer).write(context.getBeans());

writer.writeEndElement();
writer.writeCharacters("\n");
writer.writeEndDocument();
Expand All @@ -109,14 +109,12 @@ private boolean isJpaUsed() {
}

private boolean isJtaUsed() {
boolean jtaUsed = false;
for (Bean bean : context.getBeans()) {
if (bean.transactionDef != null) {
jtaUsed = true;
if (!bean.transactionDefs.isEmpty()) {
return true;
}

}
return jtaUsed;
return false;
}

private void writeBlueprint() throws XMLStreamException {
Expand All @@ -128,7 +126,7 @@ private void writeBlueprint() throws XMLStreamException {
writer.writeNamespace(prefix, namespace);
}
}

private String getPrefixForNamesapace(String namespace) {
if (namespace.contains("jpa")) {
return "jpa";
Expand Down Expand Up @@ -156,15 +154,17 @@ public void writeBeanStart(Bean bean) throws XMLStreamException {
writer.writeAttribute("destroy-method", bean.destroyMethod);
}
writer.writeCharacters("\n");

if (namespaces.contains(NS_TX)) {
writeTransactional(bean.transactionDef);
for (TransactionalDef transactionalDef : bean.transactionDefs) {
writeTransactional(transactionalDef);
}
}
if (namespaces.contains(NS_JPA)) {
writePersistenceFields(bean.persistenceFields);
}
}

private void writeFactory(ProducedBean bean) throws XMLStreamException {
writer.writeAttribute("factory-ref", bean.factoryBean.id);
writer.writeAttribute("factory-method", bean.factoryMethod);
Expand All @@ -181,7 +181,7 @@ private void writeTransactional(TransactionalDef transactionDef)
}
}


private void writePersistenceFields(Field[] fields) throws XMLStreamException {
for (Field field : fields) {
writePersistenceField(field);
Expand Down
@@ -0,0 +1,65 @@
/**
* 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.
*/
package org.apache.aries.blueprint.plugin.model;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

/**
* Abstract factory for creating {@link TransactionalDef} for a given class.
*
* @param <A> the transactional annotation type.
*/
public abstract class AbstractTransactionalFactory<A extends Annotation>
{
/**
* Create {@link TransactionalDef} objects for the given class, inspecting
* class and methods for transaction annotations.
*
* @param clazz the class to inspect.
* @return a set of {@link TransactionalDef} objects.
*/
public Set<TransactionalDef> create(Class<?> clazz) {
Set<TransactionalDef> transactionalDefs = new HashSet<TransactionalDef>();
A transactional = clazz.getAnnotation(getTransactionalClass());
if (transactional != null) {
transactionalDefs.add(new TransactionalDef("*", getTransactionTypeName(transactional)));
}
for (Method method : clazz.getMethods()) {
transactional = method.getAnnotation(getTransactionalClass());
if (transactional != null) {
transactionalDefs.add(new TransactionalDef(method.getName(), getTransactionTypeName(transactional)));
}
}
return transactionalDefs;
}

/**
* @param transactional the transactional annotation.
* @return the blueprint-compatible name of the transaction type.
*/
public abstract String getTransactionTypeName(A transactional);

/**
* @return the annotation class to search for.
*/
public abstract Class<A> getTransactionalClass();
}
Expand Up @@ -22,7 +22,9 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

Expand All @@ -42,7 +44,7 @@ public class Bean extends BeanRef {
public String destroyMethod;
public SortedSet<Property> properties;
public Field[] persistenceFields;
public TransactionalDef transactionDef;
public Set<TransactionalDef> transactionDefs = new HashSet<TransactionalDef>();
public boolean isPrototype;

public Bean(Class<?> clazz) {
Expand All @@ -59,12 +61,12 @@ public Bean(Class<?> clazz) {
if (destroyMethod != null) {
this.destroyMethod = destroyMethod.getName();
}

// Transactional methods
transactionDefs.addAll(new JavaxTransactionFactory().create(clazz));
transactionDefs.addAll(new SpringTransactionFactory().create(clazz));
this.isPrototype = isPrototype(clazz);
this.persistenceFields = getPersistenceFields();
this.transactionDef = new JavaxTransactionFactory().create(clazz);
if (this.transactionDef == null) {
this.transactionDef = new SpringTransactionFactory().create(clazz);
}
properties = new TreeSet<Property>();
}

Expand Down
Expand Up @@ -23,18 +23,26 @@
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;

public class JavaxTransactionFactory {
import com.google.common.base.CaseFormat;

public class JavaxTransactionFactory extends AbstractTransactionalFactory<Transactional> {
private static HashMap<TxType, String> txTypeNames;

static {
txTypeNames = new HashMap<TxType, String>();
txTypeNames.put(TxType.REQUIRED, TransactionalDef.TYPE_REQUIRED);
txTypeNames.put(TxType.REQUIRES_NEW, TransactionalDef.TYPE_REQUIRES_NEW);
}

TransactionalDef create(Class<?> clazz) {
Transactional transactional = clazz.getAnnotation(Transactional.class);
return transactional != null ?
new TransactionalDef("*", txTypeNames.get(transactional.value())) : null;

@Override
public String getTransactionTypeName(Transactional transactional)
{
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, transactional.value().name());
}

@Override
public Class<Transactional> getTransactionalClass()
{
return Transactional.class;
}
}
Expand Up @@ -18,23 +18,25 @@
*/
package org.apache.aries.blueprint.plugin.model;

import java.util.HashMap;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public class SpringTransactionFactory {
private static HashMap<Propagation, String> txTypeNames;
import com.google.common.base.CaseFormat;

static {
txTypeNames = new HashMap<Propagation, String>();
txTypeNames.put(Propagation.REQUIRED, TransactionalDef.TYPE_REQUIRED);
txTypeNames.put(Propagation.REQUIRES_NEW, TransactionalDef.TYPE_REQUIRES_NEW);
public class SpringTransactionFactory extends AbstractTransactionalFactory<Transactional> {
@Override
public String getTransactionTypeName(Transactional transactional)
{
Propagation propagation = transactional.propagation();
if (propagation == Propagation.NESTED) {
throw new UnsupportedOperationException("Nested transactions not supported");
}
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, propagation.name());
}

TransactionalDef create(Class<?> clazz) {
Transactional transactional = clazz.getAnnotation(Transactional.class);
return transactional != null ?
new TransactionalDef("*", txTypeNames.get(transactional.propagation())) : null;
@Override
public Class<Transactional> getTransactionalClass()
{
return Transactional.class;
}
}
Expand Up @@ -18,13 +18,14 @@
*/
package org.apache.aries.blueprint.plugin.model;

import com.google.common.base.Objects;

public class TransactionalDef {
public static final String TYPE_REQUIRED = "Required";
public static final String TYPE_REQUIRES_NEW = "RequiresNew";
private String method;
private String type;

public TransactionalDef(String method, String type) {
this.method = method;
this.type = type;
Expand All @@ -33,8 +34,35 @@ public TransactionalDef(String method, String type) {
public String getMethod() {
return method;
}

public String getType() {
return type;
}

@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
TransactionalDef that = (TransactionalDef) o;
return Objects.equal(method, that.method) && Objects.equal(type, that.type);
}

@Override
public int hashCode()
{
return Objects.hashCode(method, type);
}

@Override
public String toString()
{
return Objects.toStringHelper(this).add("method", method).add("type", type).toString();
}
}
Expand Up @@ -36,15 +36,19 @@
import javax.xml.xpath.XPathFactory;

import org.apache.aries.blueprint.plugin.model.Context;
import org.apache.aries.blueprint.plugin.model.TransactionalDef;
import org.apache.aries.blueprint.plugin.test.MyBean1;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.xbean.finder.ClassFinder;
import org.junit.Assert;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.google.common.collect.Sets;

public class GeneratorTest {

private XPath xpath;
Expand Down Expand Up @@ -73,19 +77,31 @@ public void testGenerate() throws Exception {
Assert.assertEquals("destroy", xpath.evaluate("@destroy-method", bean1));
Assert.assertEquals("true", xpath.evaluate("@field-injection", bean1));
Assert.assertEquals("", xpath.evaluate("@scope", bean1));

// @Transactional
Assert.assertEquals("*", xpath.evaluate("transaction/@method", bean1));
Assert.assertEquals("Required", xpath.evaluate("transaction/@value", bean1));
NodeList txs = (NodeList) xpath.evaluate("transaction", bean1, XPathConstants.NODESET);
Set<TransactionalDef> defs = new HashSet<TransactionalDef>();
for (int i = 0; i < txs.getLength(); ++i) {
Node tx = txs.item(i);
defs.add(new TransactionalDef(xpath.evaluate("@method", tx), xpath.evaluate("@value", tx)));
}
Set<TransactionalDef> expectedDefs = Sets.newHashSet(new TransactionalDef("*", "RequiresNew"),
new TransactionalDef("txNotSupported", "NotSupported"),
new TransactionalDef("txMandatory", "Mandatory"),
new TransactionalDef("txNever", "Never"),
new TransactionalDef("txRequired", "Required"),
new TransactionalDef("txOverridenWithRequiresNew", "RequiresNew"),
new TransactionalDef("txSupports", "Supports"));
Assert.assertEquals(expectedDefs, defs);

// @PersistenceContext
Assert.assertEquals("person", xpath.evaluate("context/@unitname", bean1));
Assert.assertEquals("em", xpath.evaluate("context/@property", bean1));

// @PersistenceUnit
Assert.assertEquals("person", xpath.evaluate("unit/@unitname", bean1));
Assert.assertEquals("emf", xpath.evaluate("unit/@property", bean1));

// @Autowired
Assert.assertEquals("my1", xpath.evaluate("property[@name='bean2']/@ref", bean1));

Expand Down

0 comments on commit fff2e99

Please sign in to comment.