Spring REST: returning/accepting collections and HttpMessageConvertors

In prevoius post  I have described some problems what can arise when REST application having collection-based methods is migrated from WildFly to Spring. It was noticed what wrapper classes solve the problems of XML marshalling/unmarshalling in case of migration from WildFly to Spring but carry in another problem – change of JSON representation format. Let’s consider different ways to solve the problems.

Suppose we use “Dog” entity:

@XmlRootElement
public class Dog implements Serializable {
 
    private UUID id;
    private String name;

    public Dog() {
    }

    public Dog(UUID id) {
        this.id = id;
    }

    @XmlElement
    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }

    @XmlElement
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 97 * hash + Objects.hashCode(this.id);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(this instanceof Dog)) {
            return false;
        }
        final Dog other = (Dog) obj;
        if (!Objects.equals(this.id, other.id)) {
            return false;
        }
        return true;
   }
 
}

and wish to migrate the following method from WF to Spring Boot:

 @GET
 @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
 @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) 
 public Set<Dog> getDogs(Set<Dog> dogs) {
     return dogs;
 }

As you can see this method just responses the same set of Dogs back to caller. What is essential – it accepts and responses collection and is restricted to JSON and XML representation formats.

If we use Spring MVC as before, we can reproduce XML format using simplest wrapper:

@XmlRootElement
public class Dogs {

    private Set<Dog> collection = new HashSet<>();
    
    @XmlElement(name="collection")
    public Set<Dog> getCollection() {
        return collection;
    }

    public void setCollection(Set<Dog> collection) {
        this.collection = collection;
    }
}

but as it has been discussed in prevoius post this breaks out former JSON format.

The extensive way to solve this problem is to implement 4 different methods instead of single one – each one per unique combination “consumed” and “produced” format, e.g.

@GetMapping(
    consumes = {MediaType.APPLICATION_XML_VALUE},
    produces = {MediaType.APPLICATION_XML_VALUE})
@ResponseBody
public Dogs getDogsXmlXml(@RequestBody Dogs dogs) {
...
} 
 
@GetMapping(
    consumes = {MediaType.APPLICATION_JSON_VALUE},
    produces = {MediaType.APPLICATION_XML_VALUE})
@ResponseBody
public Dogs getDogsJsonXml(@RequestBody Set<Dog> dogs) {
...
}

@GetMapping(
    consumes = {MediaType.APPLICATION_XML_VALUE},
    produces = {MediaType.APPLICATION_JSON_VALUE})
@ResponseBody
public Set<Dog> getDogsXmlJson(@RequestBody Dogs dogs) {
...
}
 
@GetMapping(
    consumes = {MediaType.APPLICATION_JSON_VALUE},
    produces = {MediaType.APPLICATION_JSON_VALUE})
@ResponseBody
public Set<Dog> getDogsJsonJson(@RequestBody Set<Dog> dogs) {
...
}

This works – but looks suboptimal and needs so much efforts – do we really want to support all of this ?

Fortunately Spring provides the way to control JSON marshalling/unmarshalling using HttpMessageConverter. First of all let’s implement abstract wrapper class:

public abstract class AbstractWrapper<T> {

    private Set<T> collection = new HashSet<>();

    @XmlTransient
    public Set<T> getCollection() {
        return collection;
    }

    public void setCollection(Set<T> collection) {
        this.collection = collection;
    }
}

Corresponding dog’s wrapper is:

@XmlRootElement
public class Dogs extends AbstractWrapper<Dog> {
     @XmlElement(name="dog")
     @Override
     public Set<Dog> getCollection() {
         return super.getCollection();
     }
}

If we implement now the method

@GetMapping(
    consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE},
    produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@ResponseBody
public Dogs getDogs(@RequestBody Dogs dogs) {
    return dogs;
}

we can ensure what it accepts and returns expected XML:

curl -X GET --header 'Accept: application/xml' --header 'Content-Type: application/xml' -d '<dogs>
> <dog><id>8be0c745-cf76-491f-8e18-cc1d53069a90</id><name>Dog 1</name></dog>
> <dog><id>8be0c745-cf76-491f-8e18-cc1d53069a91</id><name>Dog 2</name></dog></dogs>' 'http://localhost:8080/myapp/rest/dog'
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><dogs><dog><id>8be0c745-cf76-491f-8e18-cc1d53069a91</id><name>Dog 2</name></dog><dog><id>8be0c745-cf76-491f-8e18-cc1d53069a90</id><name>Dog 1</name></dog></dogs>

Unfortunately (and as expected) the same request of JSON returns

{"collection":[{"id":"8be0c745-cf76-491f-8e18-cc1d53069a91","name":"Dog 2"},{"id":"8be0c745-cf76-491f-8e18-cc1d53069a90","name":"Dog 1"}]}

while our expectations would be

[{"id":"8be0c745-cf76-491f-8e18-cc1d53069a91","name":"Dog 2"},{"id":"8be0c745-cf76-491f-8e18-cc1d53069a90","name":"Dog 1"}]

Now let’s implement MessageConverter. Begin from abstract class which implements convertor of collections into wrapper and vice versa when content type is JSON.

public class AbstractConverter<T> implements HttpMessageConverter<AbstractWrapper<T>> {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    private final CollectionType collectionType; 
    private final Class<? extends AbstractWrapper<T>> wrapperType;

    public AbstractConverter(Class<? extends AbstractWrapper<T>> wrapperType, Class<? extends T> wrappedType) {
        this.wrapperType = wrapperType;
        this.collectionType = objectMapper.getTypeFactory()
            .constructCollectionType(Set.class, wrappedType);
    }

    @Override
    public boolean canRead(Class<?> type, MediaType mt) {
        if (mt.includes(MediaType.APPLICATION_JSON) && type == wrapperType) {
            return true;
        }
        return false;
    }

    @Override
    public boolean canWrite(Class<?> type, MediaType mt) {
        if (mt.includes(MediaType.APPLICATION_JSON) && type == wrapperType) {
            return true;
        }
        return false;
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return Arrays.asList(MediaType.APPLICATION_JSON);
    }

    @Override
    public AbstractWrapper<T> read(Class<? extends AbstractWrapper<T>> type, HttpInputMessage him) throws IOException, HttpMessageNotReadableException {
        InputStream body = him.getBody();
        Set<T> readValue = objectMapper.readValue(body, collectionType);
        try {
            Constructor<? extends AbstractWrapper<T>> constructor = wrapperType.getConstructor();
            AbstractWrapper<T> newInstance = constructor.newInstance();
            newInstance.setCollection(readValue);
            return newInstance;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    public void write(AbstractWrapper<T> t, MediaType mt, HttpOutputMessage hom) throws IOException, HttpMessageNotWritableException {
        objectMapper.writeValue(hom.getBody(), t.getCollection());
    }

}

Please remark what we need keep type-specific information to implement readValue method (remember – generics make nothing to give us type-specific knowledge at runtime – type info erasing !).

For particular Dogs wrapper class the converter (annotated as @Component to make it available for Spring instantiation) is

@Component
public class DogsHttpMessageConverter extends AbstractConverter<Dog> {

    public DogsHttpMessageConverter() {
        super(Dogs.class, Dog.class);
    }
}

Now we are getting the expected result:

curl -X GET --header 'Accept: application/json' --header 'Content-Type: application/xml' -d '<dogs>
<dog><id>8be0c745-cf76-491f-8e18-cc1d53069a90</id><name>Dog 1</name></dog>
<dog><id>8be0c745-cf76-491f-8e18-cc1d53069a91</id><name>Dog 2</name></dog></dogs>' 'http://localhost:8080/myapp/rest/dog'
[{"id":"8be0c745-cf76-491f-8e18-cc1d53069a91","name":"Dog 2"},{"id":"8be0c745-cf76-491f-8e18-cc1d53069a90","name":"Dog 1"}]
curl -X GET --header 'Accept: application/xml' --header 'Content-Type: application/json' -d '[{"id":"8be0c745-cf76-491f-8e18-cc1d53069a91","name":"Dog 2"},{"id":"8be0c745-cf76-491f-8e18-cc1d53069a90","name":"Dog 1"}]' 'http://localhost:8080/myapp/rest/dog'
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><dogs><dog><id>8be0c745-cf76-491f-8e18-cc1d53069a91</id><name>Dog 2</name></dog><dog><id>8be0c745-cf76-491f-8e18-cc1d53069a90</id><name>Dog 1</name></dog></dogs>
curl -X GET --header 'Accept: application/json' --header 'Content-Type: application/json' -d '[{"id":"8be0c745-cf76-491f-8e18-cc1d53069a91","name":"Dog 2"},{"id":"8be0c745-cf76-491f-8e18-cc1d53069a90","name":"Dog 1"}]' 'http://localhost:8080/myapp/rest/dog'
[{"id":"8be0c745-cf76-491f-8e18-cc1d53069a91","name":"Dog 2"},{"id":"8be0c745-cf76-491f-8e18-cc1d53069a90","name":"Dog 1"}]
curl -X GET --header 'Accept: application/xml' --header 'Content-Type: application/xml' -d '<dogs>
<dog><id>8be0c745-cf76-491f-8e18-cc1d53069a90</id><name>Dog 1</name></dog>
<dog><id>8be0c745-cf76-491f-8e18-cc1d53069a91</id><name>Dog 2</name></dog></dogs>' 'http://localhost:8080/myapp/rest/dog'
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><dogs><dog><id>8be0c745-cf76-491f-8e18-cc1d53069a91</id><name>Dog 2</name></dog><dog><id>8be0c745-cf76-491f-8e18-cc1d53069a90</id><name>Dog 1</name></dog></dogs>
Advertisements
Posted in Spring Boot | Tagged , , | Leave a comment

Spring Boot + Bitronix

Distributed transactions in Spring Boot can be implemented using another provider – Bitronix. To change XA provider from Atomikos into Bitronix in previous article example it’s enough to change jta starter and make changes in TransactionManager bean and Data Source implementations. Instead of Atomikos starter we should use

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-jta-bitronix</artifactId>
 </dependency>

Configuration class now looks like

@Configuration
@EnableTransactionManagement
public class DbConfig {

    @Bean(name = "bitronixTransactionManager")
    @DependsOn
    public BitronixTransactionManager bitronixTransactionManager() throws Throwable {
        BitronixTransactionManager bitronixTransactionManager = TransactionManagerServices.getTransactionManager();
        bitronixTransactionManager.setTransactionTimeout(10000);
        CustomJtaPlatform.setUserTransaction(bitronixTransactionManager);
        CustomJtaPlatform.setTransactionManager(bitronixTransactionManager);
        return bitronixTransactionManager;
    }

    @Bean(name = "transactionManager")
    @DependsOn({"bitronixTransactionManager"})
    public PlatformTransactionManager transactionManager(TransactionManager bitronixTransactionManager) throws Throwable {
        return new JtaTransactionManager(bitronixTransactionManager);
    }

    @Bean(name = "primaryMySqlDataSource")
    @Primary
    public DataSource primaryMySqlDataSource() {
        PoolingDataSource bitronixDataSourceBean = new PoolingDataSource();
        bitronixDataSourceBean.setMaxPoolSize(5);
        bitronixDataSourceBean.setUniqueName("primaryMySqlDataSourceResource");
        bitronixDataSourceBean.setClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
        Properties properties = new Properties();
        properties.put("user",  "dmitry");
        properties.put("password",  "***");
        properties.put("url", "jdbc:mysql://localhost/databaseOne");
        bitronixDataSourceBean.setDriverProperties(properties);
        return bitronixDataSourceBean;
    }

    @Bean(name = "primaryMySqlEntityManagerFactory")
    @Primary
    @DependsOn({"transactionManager", "primaryMySqlDataSource"})
    public LocalContainerEntityManagerFactoryBean primaryMySqlEntityManagerFactory(
    EntityManagerFactoryBuilder builder, 
    DataSource primaryMySqlDataSource) {
        return builder
            .dataSource(primaryMySqlDataSource)
            .persistenceUnit("primaryUnit")
            .packages(Cat.class)
            .build();
    }

    @Bean(name = "secondaryMySqlDataSource")
    public DataSource secondaryMySqlDataSource() {
        PoolingDataSource bitronixDataSourceBean = new PoolingDataSource();
        bitronixDataSourceBean.setMaxPoolSize(5);
        bitronixDataSourceBean.setUniqueName("secondaryMySqlDataSourceResource");
        bitronixDataSourceBean.setClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
        Properties properties = new Properties();
        properties.put("user",  "dmitry");
        properties.put("password",  "***");
        properties.put("url", "jdbc:mysql://localhost/databaseTwo");
        bitronixDataSourceBean.setDriverProperties(properties);
        return bitronixDataSourceBean;
    }

    @Bean(name = "secondaryMySqlEntityManagerFactory")
    @DependsOn({"transactionManager", "secondaryMySqlDataSource"})
    public LocalContainerEntityManagerFactoryBean secondaryMySqlEntityManagerFactory(
        EntityManagerFactoryBuilder builder, 
        @Qualifier("secondaryMySqlDataSource") DataSource secondaryMySqlDataSource) {
        return builder
            .dataSource(secondaryMySqlDataSource)
            .persistenceUnit("secondaryUnit")
            .packages(Dog.class)
            .build();
    }

}

 

Posted in Spring Boot | Tagged , , | Leave a comment

Spring Boot vs WildFly – distributed transactions

The next post related to migration from WildFly to Spring Boot concerns sligtly exotic thema of distributed transactions. Majority of application uses single datasource, but some of them can use several ones or combine different transaction-specific contexts in one transaction.

Begin from WildFly 12.0.0. Suppose I have two MySQL databases – databaseOne and databaseTwo. In first database I create the table “cat”, in second one – the table “dog”. Correspondent entities are:

@Entity
@Table(name="cat")
@XmlRootElement
public class Cat implements Serializable {
 
    @Id
    private UUID id;
    private String name;

    public Cat() {
    }

    public Cat(UUID id) {
        this.id = id;
    }

    @XmlElement
    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }

    @XmlElement
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 97 * hash + Objects.hashCode(this.id);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(this instanceof Cat)) {
            return false;
        }
        final Cat other = (Cat) obj;
        if (!Objects.equals(this.id, other.id)) {
            return false;
        }
        return true;
    }
 
}
@Entity
@Table(name="dog")
@XmlRootElement
public class Dog implements Serializable {
 
    @Id
    private UUID id;
    private String name;

    public Dog() {
    }

    public Dog(UUID id) {
        this.id = id;
    }

    @XmlElement
    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }

    @XmlElement
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
 
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 97 * hash + Objects.hashCode(this.id);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(this instanceof Dog)) {
            return false;
        }
        final Dog other = (Dog) obj;
        if (!Objects.equals(this.id, other.id)) {
            return false;
        }
        return true;
    }
 
}

I have configured two JNDI datasources (XA-based) in standalone.xml file of WildFly

<datasource jta="true" jndi-name="java:/jdbc/primary" pool-name="primary" use-ccm="true">
    <connection-url>jdbc:mysql://localhost/databaseOne...
    <driver>mysqlxa</driver>
...
<datasource jta="true" jndi-name="java:/jdbc/secondary" pool-name="secondary" use-ccm="true">
    <connection-url>jdbc:mysql://localhost/databaseTwo...
    <driver>mysqlxa</driver>
...
<drivers>
    <driver name="mysqlxa" module="com.mysql">
        <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
    </driver>
...

and added two persistence units to web application:

 <persistence-unit name="primaryUnit" transaction-type="JTA">
     <jta-data-source>jdbc/primary</jta-data-source>
     <class>org.test.model.Cat</class>
     <exclude-unlisted-classes>true</exclude-unlisted-classes>
 </persistence-unit>
 <persistence-unit name="secondaryUnit" transaction-type="JTA">
     <jta-data-source>jdbc/secondary</jta-data-source>
     <class>org.test.model.Dog</class>
     <exclude-unlisted-classes>true</exclude-unlisted-classes>
 </persistence-unit>

And finally tet’s try to make distributed transaction – insert one row into dog table and one row into cat table in one transaction.

...
@PersistenceContext(unitName = "primaryTest")
private EntityManager emPrimary;
@PersistenceContext(unitName = "secondaryTest")
private EntityManager emSecondary;

 @POST
 @Path("/{name}")
 public void create(@PathParam("name") String name) {
     Dog dog = new Dog(UUID.randomUUID());
     Cat cat = new Cat(UUID.randomUUID());
     dog.setName(name);
     cat.setName(name);
     emPrimary.persist(cat);
     emSecondary.persist(dog);
 }

As a result of execution of

curl -X POST 'http://localhost:8080/myapp/rest/dog/Peter'

we have got error. The reason is:

ARJUNA012140: Adding multiple last resources is disallowed.

Oops. OK. Let’s add the following setting into standalone.xml:

<system-properties> 
    <property name="com.arjuna.ats.arjuna.allowMultipleLastResources" value="true"/> 
</system-properties>

and repeate the try. Now everything is OK, and we’ve got

mysql>use databaseOne;
...
mysql> select name from cat;
+-------+
| name | 
+-------+ 
| Peter | 
+-------+ 
mysql>use databaseTwo;
...
mysql> select name from dog; 
+-------+ 
| name | 
+-------+ 
| Peter | 
+-------+

So, standard WildFly transaction manager settings don’t allow distributed transactions. We should change them or use non-provided transaction manager.

OK, now make the same in Spring Boot. As XA transaction manager I’m going to use Atomikos. In my maven project I have added the following dependency:

 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jta-atomikos</artifactId>
 </dependency>

The distributed transactions need JTA support. We are working with Hibernate 5+, and, hence, are to extend AbstractJtaPlatform abstract class. Also, we need to use JtaTransactionManager as the transaction manager in our application. Implement first CustomJtaPlatform:

public class CustomJtaPlatform extends AbstractJtaPlatform {

    private static TransactionManager transactionManager;
    private static UserTransaction transaction;
 
    public static void setTransactionManager(TransactionManager tm) {
        transactionManager = tm;
    }

    public static void setUserTransaction(UserTransaction ut) {
        transaction = ut;
    }
 
    @Override
    protected TransactionManager locateTransactionManager() {
        return transactionManager;
    }

    @Override
    protected UserTransaction locateUserTransaction() {
        return transaction;
    }

}

JtaTransactionManager requires the access to UserTransaction and TransactionManager interface realizations. Let’s implement singleton bean for UserTransaction in our configuration class:

@Configuration
@EnableTransactionManagement
public class DbConfig {

    @Bean(name = "userTransaction")
    public UserTransaction userTransaction() throws Throwable {
        UserTransactionImp userTransactionImp = new UserTransactionImp();
        userTransactionImp.setTransactionTimeout(30000);
        return userTransactionImp;
    }

Here we use Atomikos UserTransactionImp which should be used in Java SE implementation. Similarly we should create TransactionManager bean using Atomikos implementation:

 @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
 public TransactionManager atomikosTransactionManager() throws Throwable {
     UserTransactionManager userTransactionManager = new UserTransactionManager();
     userTransactionManager.setForceShutdown(false);
     return userTransactionManager;
 }

We use UserTransactionManager Atomikos class. It’s also Java SE – specific. Please consider what we have to poit out two methods – init() and close() – in @Bean annotation. This is required to start transaction manager working and gracefully shutdown it. Now the time is come JtaTransactionManager to specify:

 @Bean(name = "transactionManager")
 @DependsOn({"userTransaction", "atomikosTransactionManager"})
 public PlatformTransactionManager transactionManager(UserTransaction userTransaction, TransactionManager atomikosTransactionManager) throws Throwable {
 
     CustomJtaPlatform.setUserTransaction(userTransaction);
     CustomJtaPlatform.setTransactionManager(atomikosTransactionManager);
 
     return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
 }

Notice what there we also initialize CustomJtaPlatform settings.

Configure two different persistence units for our Spring Boot application:

 <persistence-unit name="primaryUnit" transaction-type="JTA">
     <properties>
         <property name="hibernate.transaction.jta.platform" value="org.integrationtest.config.CustomJtaPlatform" />
     </properties>
     </persistence-unit>
 <persistence-unit name="secondaryUnit" transaction-type="JTA">
     <properties>
         <property name="hibernate.transaction.jta.platform" value="org.integrationtest.config.CustomJtaPlatform" />
     </properties>
 </persistence-unit>

and append the following dependency to Maven project:

 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
 </dependency>

Now data sources and entity manager factories should be implemented. We will use AtomikosDataSourceBean and EntityManagerFactoryBuilder:

@Bean(name = "primaryMySqlDataSource")
@Primary
public DataSource primaryMySqlDataSource() {
    AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
    atomikosDataSourceBean.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
    Properties properties = new Properties();
    properties.put("user",  "dmitry");
    properties.put("password",  "****");
    properties.put("url", "jdbc:mysql://localhost/databaseOne");
    atomikosDataSourceBean.setXaProperties(properties);
    atomikosDataSourceBean.setUniqueResourceName("primaryMySqlDataSourceResource");
    return atomikosDataSourceBean;
 }

@Bean(name = "primaryMySqlEntityManagerFactory")
@Primary
@DependsOn({"transactionManager", "primaryMySqlDataSource"})
public LocalContainerEntityManagerFactoryBean primaryMySqlEntityManagerFactory(
  EntityManagerFactoryBuilder builder, 
  DataSource primaryMySqlDataSource) {
    return builder
    .dataSource(primaryMySqlDataSource)
    .persistenceUnit("primaryUnit")
    .packages(Cat.class)
    .build();
}

@Bean(name = "secondaryMySqlDataSource")
public DataSource secondaryMySqlDataSource() {
    AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
    atomikosDataSourceBean.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
    Properties properties = new Properties();
    properties.put("user",  "dmitry");
    properties.put("password",  "***");
    properties.put("url", "jdbc:mysql://localhost/databaseTwo");
    atomikosDataSourceBean.setXaProperties(properties);
    atomikosDataSourceBean.setUniqueResourceName("secondaryMySqlDataSourceResource");
    return atomikosDataSourceBean;
 }

@Bean(name = "secondaryMySqlEntityManagerFactory")
@DependsOn({"transactionManager", "secondaryMySqlDataSource"})
public LocalContainerEntityManagerFactoryBean secondaryMySqlEntityManagerFactory(
  EntityManagerFactoryBuilder builder, 
  @Qualifier("secondaryMySqlDataSource") DataSource secondaryMySqlDataSource) {
    return builder
    .dataSource(secondaryMySqlDataSource)
    .persistenceUnit("secondaryUnit")
    .packages(Dog.class)
    .build();
 }

@DependsOn guaranties what beans are being instantiated in required order, @Primary requires to provide correct default autowiring and  @Qualifier points out on the correct datasource instantiation.

Correspondent REST service is

@RestController
@RequestMapping("/myapp/rest/dog")
@Transactional
public class DogController {

    @PersistenceContext(unitName = "secondaryUnit")
    private EntityManager emSecondary;
    @PersistenceContext(unitName = "primaryUnit")
    private EntityManager emPrimary;

    @RequestMapping(
      method = {RequestMethod.POST}, 
      value = "/{name}")
    public void append(@PathVariable("name") String name) {
        Cat cat = new Cat(UUID.randomUUID());
        Dog dog = new Dog(UUID.randomUUID());
        cat.setName(name);
        dog.setName(name);
        emPrimary.persist(cat);
        emSecondary.persist(dog);
    }
}

The request

curl -X POST 'http://localhost:8080/myapp/rest/dog/Bess'

inserts data in every database:

mysql> select name from dog;
+-------+
| name |
+-------+
| Bess |
| Peter |
+-------+
...
mysql> select name from cat;
+-------+
| name |
+-------+
| Bess |
| Peter |
+-------+

OK, who is better ? From my perspective it’s a pity what WildFly transaction manager does not support distributed transactions by default and man should undertaken some magic to get it working. From another side this requires less efforts as in Spring. No winner ?

 

Posted in Spring Boot | Tagged , , , | Leave a comment

Spring Boot vs WildFly – 1:0

REST service supposes sometimes the return of complex structures, for example object and the collection of it’s properties in one request. In this article I create the simple data model what consists of entities Cat and Puppy, Cat is related to Puppy as one-to-many.

@Entity
@Table(name="cat")
@XmlRootElement
public class Cat implements Serializable {
 
    @Id
    private UUID id;
    private String name;

    @OneToMany(mappedBy="cat")
    private Collection<Puppy> puppies;
 
    public Cat() {
    }

    public Cat(UUID id) {
        this.id = id;
    }

    @XmlElement
    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }

    @XmlElement
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @XmlElement
    public Collection<Puppy> getPuppies() {
        return puppies;
    }

    public void setPuppies(Collection<Puppy> puppies) {
        this.puppies = puppies;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 97 * hash + Objects.hashCode(this.id);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(this instanceof Cat)) {
            return false;
        }
        final Cat other = (Cat) obj;
        if (!Objects.equals(this.id, other.id)) {
            return false;
        }
        return true;
    }

}
@Entity
@Table(name="puppy")
@XmlRootElement
public class Puppy implements Serializable {
 
    @Id
    private UUID id;
    private String name;

    @ManyToOne
    @JoinColumn(name="catId")
    private Cat cat;

    @XmlTransient
    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }
 
    @XmlElement
    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }

    @XmlElement
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 37 * hash + Objects.hashCode(this.id);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(this instanceof Puppy)) {
            return false;
        }
        final Puppy other = (Puppy) obj;
        if (!Objects.equals(this.id, other.id)) {
            return false;
        }
        return true;
    }
 
}

Notice what Cat getter is marked in Puppy as @XmlTransient – it’s required to avoid infinite recursion (Cat -> Puppy -> Cat -> Puppy…) when Cat object is being proceeded by marshaller.

In Spring Boot 2.0.0 I have configured DataSource and EntityManagementFactory bean to get working with MySQL database and use it throug persistence context in REST controller. Also it’s worth to mention what Spring Boot and WildFly both use Hibernate as JPA provider.

REST controller in Spring Boot contains three methods – two methods to create Cat and Puppy and return Cat by it’s unique Id:

@RestController
@RequestMapping("/cat")
@Transactional
public class CatController {
 
    @PersistenceContext(unitName = "primaryTest")
    private EntityManager em;
 
    @RequestMapping("/{id}")
    @ResponseBody
    public Cat getCat(@PathVariable UUID id) {
        Cat cat = em.find(Cat.class, id);
        return cat;
    }
 
    @RequestMapping(method = {RequestMethod.POST}, value="")
    @ResponseBody
    public Cat append(@RequestBody Cat cat) {
        cat.setId(UUID.randomUUID());
        em.persist(cat);
        return cat;
    } 
 
    @RequestMapping(method = {RequestMethod.POST}, value="/puppy/{catId}")
    @ResponseBody
    public Puppy append(@PathVariable UUID catId, @RequestBody Puppy puppy) {
        puppy.setId(UUID.randomUUID());
        puppy.setCat(new Cat(catId));
        em.persist(puppy);
        return puppy;
    } 

}

I have created one Cat record and two related Puppy records. Request of XML-formatted output returns us

<cat>
    <id>18c53219-fca2-4c7e-a16d-ee2843c4b5d9</id>
    <name>Mom Cat</name>
    <puppies>
        <id>c535e61c-2540-479a-9bcd-33f4596aeec5</id>
        <name>Tom</name>
    </puppies>
    <puppies>
        <id>ca5f5d43-afbc-414d-9f25-e3846e20af42</id>
        <name>Mary</name>
    </puppies>
</cat>

OK, looks good.

Now try to do the same in WildFly 12.0.0

@Path("cat")
@Stateless
public class CatResource {

    @PersistenceContext(unitName = "primaryTest")
    private EntityManager em;

    @POST
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Cat createCat(Cat cat) {
        cat.setId(UUID.randomUUID());
        em.persist(cat);
        return cat;
    }

    @Path("{id}")
    @GET
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Cat get(@PathParam("id") UUID id) {
        Cat cat = em.find(Cat.class, id);
        return cat;
    } 
 
    @Path("puppy/{catId}")
    @POST
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Puppy createPuppy(@PathParam("catId") UUID catId, Puppy puppy) {
        puppy.setId(UUID.randomUUID());
        puppy.setCat(new Cat(catId));
        em.persist(puppy);
        return puppy;
    } 
 
}

The request of Cat object leads to internal server error:

Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.integrationtest.model.animal.Cat.puppies, could not initialize proxy - no Session
 at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:584)
 at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:201)
 at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:563)
 at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:132)
 at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:277)
 at com.sun.xml.bind.v2.runtime.reflect.Lister$CollectionLister.iterator(Lister.java:281)
 at com.sun.xml.bind.v2.runtime.reflect.Lister$CollectionLister.iterator(Lister.java:268)
 at com.sun.xml.bind.v2.runtime.property.ArrayElementProperty.serializeListBody(ArrayElementProperty.java:133)
 at com.sun.xml.bind.v2.runtime.property.ArrayERProperty.serializeBody(ArrayERProperty.java:159)
 at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:360)
 at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:593)
 at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:341)
 at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:494)
 at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:328)
 at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:256)
 at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:74)
 at org.jboss.resteasy.plugins.providers.jaxb.AbstractJAXBProvider.writeTo(AbstractJAXBProvider.java:156)
 at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.writeTo(AbstractWriterInterceptorContext.java:137)
 at org.jboss.resteasy.core.interception.ServerWriterInterceptorContext.writeTo(ServerWriterInterceptorContext.java:61)
 at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:124)
 at org.jboss.resteasy.security.doseta.DigitalSigningInterceptor.aroundWriteTo(DigitalSigningInterceptor.java:146)
 at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:129)
 at org.jboss.resteasy.core.ServerResponseWriter.lambda$writeNomapResponse$2(ServerResponseWriter.java:140)
 at org.jboss.resteasy.core.interception.ContainerResponseContextImpl.filter(ContainerResponseContextImpl.java:395)
 at org.jboss.resteasy.core.ServerResponseWriter.executeFilters(ServerResponseWriter.java:207)
 at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:85)
 at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:59)
 at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:530)
 ... 52 more

Oops. “Lazy initialization” problem, seems session is closed before XML marshaller proceeds the object completely. Looks not perfect, does it ?

Of course any collection initialization action fixes the problem, for example:

 @Path("{id}")
 @GET
 @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
 public Cat get(@PathParam("id") UUID id) {
     Cat cat = em.find(Cat.class, id);
     cat.getPuppies().size();
     return cat;
 }

but I can not say I’m happy with such a trick – I personally would like to rely on internal mechanisms of platform. BTW – GlassFish works like Spring Boot, e.g. no need to worry about the collection initialization.

Posted in Uncategorized | Leave a comment

REST services, returning collections: migration from platform to platform

This is not the usual case, when you need to migrate your services from one application server to another or from JEE to Spring and vice versa. But this can become the developer’s nightmare, because the specification leaves so many white spots, and the provider can fill them out by quite different colors. One of the particular case I would like to describe there.

REST services as a rule are often designed to return object collections. And what works on one server/platform like a charm can be a problem on another one. Let’s consider the case when the same REST service is being worked on GlassFish 4.1, WildFly 12.0.0 and Spring Boot 2.0.0 platform.

Let’s create the simplest class FakeAnimal and the simplest REST service what should return set of FakeAnimal class instances:

@XmlRootElement
public class FakeAnimal {
	
	private String name;

	@XmlElement
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public FakeAnimal() {
	}

	public FakeAnimal(String name) {
		this.name = name;
	}

	@Override
	public int hashCode() {
		int hash = 7;
		hash = 97 * hash + Objects.hashCode(this.name);
		return hash;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		final FakeAnimal other = (FakeAnimal) obj;
		if (!Objects.equals(this.name, other.name)) {
			return false;
		}
		return true;
	}

}

JAX-RS REST service looks like

@Path("animals")
public class AnimalsResource {

	public AnimalsResource() {
	}

	@GET
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public Set getAnimals() {
		FakeAnimal godsilla = new FakeAnimal("Godsilla");
		FakeAnimal caprocorn = new FakeAnimal("Caprocorn");
		Set set = new HashSet<>();
		Collections.addAll(set, godsilla, caprocorn);
		return set;
	}

}

I omit here the details of registering REST in application server context and adjustment of the context name. The results of the request

curl -X GET --header 'Accept: application/json' 'http://localhost:8080/myapp/rest/animals'

returns me the same result on GF 4.1 and WF 12.0.0:

[
    {"name":"Caprocorn"},
    {"name":"Godsilla"}
]

But for XML-formatted response

curl -X GET --header 'Accept: application/xml' 'http://localhost:8080/myapp/rest/animals'

are different:

<collection>
    <fakeAnimal>
        <name>Caprocorn</name>
    </fakeAnimal>
    <fakeAnimal>
        <name>Godsilla</name>
    </fakeAnimal>
</collection>

on Wildfly 12 and

<fakeAnimals>
    <fakeAnimal>
        <name>Caprocorn</name>
    </fakeAnimal>
    <fakeAnimal>
        <name>Godsilla</name>
    </fakeAnimal>
</fakeAnimals>

on GF 4.1.

Is it a problem ? Speaking strongly – yes, it’s a problem for your clients what can use different kinds of client libraries and rely on stable XML element naming.

Of course, in WildFly you can use the workaround like this

	
@GET
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Wrapped(element = "fakeAnimals")
public Set getAnimals() {
	FakeAnimal godsilla = new FakeAnimal("Godsilla");
	FakeAnimal caprocorn = new FakeAnimal("Caprocorn");
	Set set = new HashSet<>();
	Collections.addAll(set, godsilla, caprocorn);
	return set;
}

But this already requires to append RestEasy library into projects depencency, and this workaround is RestEasy-specific.

The migration to Spring Boot 2.0.0 causes another kind of problems. Suppose we implement simplest REST service like above using the following class:

@RestController
@RequestMapping("/myapp/rest/animals")
public class FakeAnimalsRestController {

    @ResponseBody
    @RequestMapping(method={RequestMethod.GET}, 
        produces = {
            MediaType.APPLICATION_JSON_VALUE, 
            MediaType.APPLICATION_XML_VALUE})
    public Set<FakeAnimal> returnFakeAnimals() {
        FakeAnimal godsilla = new FakeAnimal("Godsilla");
        FakeAnimal caprocorn = new FakeAnimal("Caprocorn");
        Set<FakeAnimal> set = new HashSet<>();
        Collections.addAll(set, godsilla, caprocorn);
        return set;
    }

    @ResponseBody
    @RequestMapping(method={RequestMethod.GET}, 
        produces = {
            MediaType.APPLICATION_JSON_VALUE, 
            MediaType.APPLICATION_XML_VALUE},
            path="/single")
    public FakeAnimal returnFakeAnimal() {
        FakeAnimal godsilla = new FakeAnimal("Godsilla");
        return godsilla;
    }

}

If we try to request JSON all works as before but with XML content we get problems. Single object is returned well, but the collection method returns HTTP 406. To resolve this problem we should append the dependency in Boot project:

 <dependency>
     <groupId>com.fasterxml.jackson.dataformat</groupId>
     <artifactId>jackson-dataformat-xml</artifactId>
 </dependency>

Now it’s working, but the result again does not like the original one:

<Set>
    <item>
        <name>Caprocorn</name>
    </item>
    <item>
        <name>Godsilla</name>
    </item>
</Set>

Moreover, we have broken also the result of single-object-method, it returns

<FakeAnimal>
    <name>Godsilla</name>
</FakeAnimal>

instead of

<fakeAnimal>
    <name>Godsilla</name>
</fakeAnimal>

And we do not have easy way to fix – jackson-dataformat-xml library does not consider JAXB annotations bu default. We have to use Jackson-specific ones like

@JacksonXmlRootElement(localName = "fakeAnimal")
public class FakeAnimal {...

And so on. The old client compatibility becomes the problem, because we don’t have easy way to control collection root element name.

The appropriate way to get rid of these problems is to avoid default agreements in class mapping. Let’s do in original model one change – add the wrapper class

@XmlRootElement
public class FakeAnimals {
 
    private Set<FakeAnimal> fakeAnimals = new HashSet<>();

    @XmlElement(name="fakeAnimal")
    public Set<FakeAnimal> getFakeAnimals() {
        return fakeAnimals;
    }

    public void setFakeAnimals(Set<FakeAnimal> fakeAnimals) {
        this.fakeAnimals = fakeAnimals;
    }

}

Corresponsing REST method looks like

 @GET
 @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
 public FakeAnimals getAnimals() {
     FakeAnimal godsilla = new FakeAnimal("Godsilla");
     FakeAnimal caprocorn = new FakeAnimal("Caprocorn");
     FakeAnimals container = new FakeAnimals();
     Collections.addAll(container.getFakeAnimals(), godsilla, caprocorn);
     return container;
 }

The result of JSON request from application servers looks like

{
    "fakeAnimal":[
        {"name":"Caprocorn"},
        {"name":"Godsilla"}
    ]
}

XML:

<fakeAnimals>
    <fakeAnimal>
        <name>Caprocorn</name>
    </fakeAnimal>
    <fakeAnimal>
        <name>Godsilla</name>
    </fakeAnimal>
</fakeAnimals>

In SpringBoot we also change method:

 @ResponseBody
 @RequestMapping(method={RequestMethod.GET}, 
     produces = {
     MediaType.APPLICATION_JSON_VALUE, 
     MediaType.APPLICATION_XML_VALUE})
     public FakeAnimals returnFakeAnimals() {
     FakeAnimal godsilla = new FakeAnimal("Godsilla");
     FakeAnimal caprocorn = new FakeAnimal("Caprocorn");
     FakeAnimals container = new FakeAnimals();
     Collections.addAll(container.getFakeAnimals(), godsilla, caprocorn);
     return container;
 }

XML is the same as before:

<fakeAnimals>
    <fakeAnimal>
        <name>Caprocorn</name>
    </fakeAnimal>
    <fakeAnimal>
        <name>Godsilla</name>
    </fakeAnimal>
</fakeAnimals>

but JSON is differ:

{
    "fakeAnimals":[
         {"name":"Caprocorn"},
         {"name":"Godsilla"}
    ]
}

The fix is quite easy:

 @JsonProperty("fakeAnimal")
 private Set<FakeAnimal> fakeAnimals = new HashSet<>();

Of course the using of wrappers instead of explicit collections is not the universal method, but wrappers have one obvious advantage – they provide you the ability to control naming using annotations and changes in your own code. This can be a significant win.

Posted in Spring Boot | Tagged , , , , | Leave a comment

Spring singleton + prototype + pooling

As JEE both Spring are the most popular IoC ecosystems in the world.
If in JEE @Stateless annotation guarantees us that every call shall deal with thread-specific copy of bean (creating it or getting from pool – no matter),
in Spring the life is not so easy. Spring treats components by default as singletons and does not care of thread-safety in case of components autowiring.
This is the very often question in programmer’s chats – how to guarantee the thread safety in the singleton component what uses other
prototype component ?
For example: https://stackoverflow.com/questions/36278595/prototype-bean-doesnt-get-autowired-as-expected
Let’s enumerate the most popular solutions of this problem.
Take as the example one simple component what counts requests to the instances

public interface CounterInterface {
	int count();
}

@Component
@Scope("prototype")
public class CounterBean implements CounterInterface {

	private AtomicInteger ai;
	private String id;
	
	@PostConstruct
	private void init() {
		Logger.getLogger(CounterBean.class.getName()).log(Level.INFO,"New bean instance created");
		ai=new AtomicInteger(0);
		SecureRandom random = new SecureRandom();
		id = new BigInteger(130, random).toString(32); 
	}
	
	@Override
	public int count() {
		Logger.getLogger(CounterBean.class.getName()).log(Level.INFO,"Bean id = {0}",id);
		try {
			TimeUnit.SECONDS.sleep(60);
		} catch (InterruptedException ex) {
			Logger.getLogger(CounterBean.class.getName()).log(Level.SEVERE, null, ex);
		}
		return ai.incrementAndGet();
	}
	
}

I have added 1 minute delay in count() method to allow parallel requests work concurrently.
The REST controller shall be the singleton:

@RestController
public class CommonRestController {
	
	@Autowired
	private CounterInterface counterBean;
	
	@RequestMapping("/count")
	public String count() {
		return "Count: "+counterBean.count();
	}
}

Now, if we create simple SpringBoot application, start it and execute at the same time couple of requests using curl utility:

curl -X GET --header 'Accept: application/text' 'http://localhost:8080/count'

we get the following output in log:

2017-06-19 13:59:06.695  INFO 8228 --- [nio-8080-exec-1] c.a.m.springbootroot.bean.CounterBean    : Bean id = f3gd2jn50hgcnfedldh5ngbjst
2017-06-19 13:59:13.194  INFO 8228 --- [nio-8080-exec-2] c.a.m.springbootroot.bean.CounterBean    : Bean id = f3gd2jn50hgcnfedldh5ngbjst

and

Count: 1
Count: 2

in request outputs.
Everything looks as expected – REST singleton is instantiated once and during instantiation requested the instance of prototype bean, which it will use in future in every request. It is not worth to mention what this architecture is not thread-safe.
We don’t consider the workaround based on implementation ApplicationContextAware interface and creation of the method to get new component instance
using getBean() method. We would like to keep the implicit IoC paradigm.
Other solution is to use proxies what are created per request:

@Component
@Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class CounterBean implements CounterInterface {

As a result of requests the same as above we get

2017-06-19 14:21:59.711  INFO 8689 --- [nio-8080-exec-1] c.a.m.springbootroot.bean.CounterBean    : New bean instance created
2017-06-19 14:21:59.716  INFO 8689 --- [nio-8080-exec-1] c.a.m.springbootroot.bean.CounterBean    : Bean id = rito7inshuulfv1se8082qcmv5
2017-06-19 14:22:03.602  INFO 8689 --- [nio-8080-exec-2] c.a.m.springbootroot.bean.CounterBean    : New bean instance created
2017-06-19 14:22:03.603  INFO 8689 --- [nio-8080-exec-2] c.a.m.springbootroot.bean.CounterBean    : Bean id = cisjvn9h3h63l2obaf6covg5ka

We have got two different scoped proxies, each one per request.
Because our class implements the interface, we can use INTERFACES proxy mode (and get JDK proxy instead of CGLIB one):

@Component
@Scope(value="prototype", proxyMode=ScopedProxyMode.INTERFACES)
public class CounterBean implements CounterInterface {

The result conceptually is the same:

2017-06-19 14:41:47.489  INFO 9099 --- [nio-8080-exec-1] c.a.m.springbootroot.bean.CounterBean    : New bean instance created
2017-06-19 14:41:47.491  INFO 9099 --- [nio-8080-exec-1] c.a.m.springbootroot.bean.CounterBean    : Bean id = 73tp8cdft97mgabmkhaoq58idd
2017-06-19 14:41:51.465  INFO 9099 --- [nio-8080-exec-2] c.a.m.springbootroot.bean.CounterBean    : New bean instance created
2017-06-19 14:41:51.465  INFO 9099 --- [nio-8080-exec-2] c.a.m.springbootroot.bean.CounterBean    : Bean id = r283h747apr3j75uroqgsndn4e

Finally, we can rely on the pooling mechanism, which also allows us to control the number of reusable component instances.
To implement that we coming back to DEFAULT (e.g. NO) proxy mode.

@Component
@Scope(value="prototype")
public class CounterBean implements CounterInterface {

and implement Configuration bean which will be used in the application context:

@Configuration
public class RootConfiguration {
	
	@Bean
	public CommonsPool2TargetSource CounterPooledTargetSource() {
		final CommonsPool2TargetSource commonsPoolTargetSource = new CommonsPool2TargetSource();
		commonsPoolTargetSource.setTargetBeanName("counterBean");
		commonsPoolTargetSource.setTargetClass(CounterBean.class);
		commonsPoolTargetSource.setMaxSize(2);
		return commonsPoolTargetSource;
	}
	
	@Bean
	@Autowired
	public ProxyFactoryBean CounterProxyFactoryBean(CommonsPool2TargetSource counterPooledTargetSource) {
		final ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
		proxyFactoryBean.setTargetSource(counterPooledTargetSource);
		return proxyFactoryBean;
	}
	
	@Bean
	@Autowired
	public CounterInterface myProxy(ProxyFactoryBean counterProxyFactoryBean) {
		return (CounterInterface)counterProxyFactoryBean.getObject();
	}
	
}

Here we configure CommonsPool of CounterBean with maximum pool size = 2. ProxyFactoryBean component in turn is configured using CommonsPool2TargetSource,
and we finally provide the proxy bean, which extracts the correspondent object from the pool (myProxy).
In REST component we use usual name-based Bean resolution:

@RestController
public class CommonRestController implements ApplicationContextAware {
	
	@Autowired
	private CounterInterface myProxy;
	
	@RequestMapping("/count")
	public String count() {
		return "Count: "+myProxy.count();
	}
}

Now we execute at the same time three requests as above.
As a result we get initially

2017-06-19 17:14:37.571  INFO 13697 --- [nio-8080-exec-1] c.a.m.springbootroot.bean.CounterBean    : New bean instance created
2017-06-19 17:14:37.573  INFO 13697 --- [nio-8080-exec-1] c.a.m.springbootroot.bean.CounterBean    : Bean id = qtj9gc3urk3n59osof6vu9t41
2017-06-19 17:14:41.153  INFO 13697 --- [nio-8080-exec-2] c.a.m.springbootroot.bean.CounterBean    : New bean instance created
2017-06-19 17:14:41.153  INFO 13697 --- [nio-8080-exec-2] c.a.m.springbootroot.bean.CounterBean    : Bean id = p5kqql559galgclhhlensne5gl

, while third request is waiting for a while – until one of pooled beans shall be free for request. Finally we get

2017-06-19 17:15:37.575  INFO 13697 --- [nio-8080-exec-3] c.a.m.springbootroot.bean.CounterBean    : Bean id = qtj9gc3urk3n59osof6vu9t41

This scenario looks very similar to the stateless bean pool in JEE application servers.

Posted in Spring | Tagged , , | 2 Comments

Oracle 12c IDENTITY and popular ORMs.

Since Oracle 12c came to the market with new IDENTITY options I faced few times with the cases what developers using ORM like ExclipseLink and Hibernate experienced issues. I decided to summarize something from my work with this option here.

In normal when we use similar option in MySQL 5.:

CREATE TABLE accounts (
     id INT NOT NULL AUTO_INCREMENT,
     username VARCHAR(30) NOT NULL,
     password VARBINARY(255) NOT NULL,
     email  VARCHAR(255) NOT NULL,
     PRIMARY KEY (id)
) ENGINE=INNODB;

nothing prevents us from using standard JPA GenerationType.IDENTITY strategy both in EclipseLink and Hibernate:

package com.volt.master;

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.GenerationType;
import javax.persistence.GeneratedValue;
import javax.persistence.Column;

@Entity
@Table(name="accounts", schema="dmitry")
public class Account {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Integer id;
	
	@Column
	private String username;
...

We can use the following code to persist our entity and get key value, generated in auto:

       EntityManagerFactory emf = Persistence.createEntityManagerFactory("MySQLUnit");
       EntityManager em = emf.createEntityManager(); 
       em.getTransaction().begin(); 
       Account acc = new Account();
...
	acc.setUsername(username);
	acc.setPassword(byteData);
	acc.setEmail(email);
	em.persist(acc);
	em.getTransaction().commit();
	System.out.println( acc.getId() );
       em.close();
       emf.close();

 Hibernate uses the following piece of code to provide AUTO_INCREMENT column extraction:

 @Override
 public String getIdentitySelectString() {
  return "select last_insert_id()";
 }

Eclipselink uses the very same:

    @Override
    public ValueReadQuery buildSelectQueryForIdentity() {
        ValueReadQuery selectQuery = new ValueReadQuery();
        StringWriter writer = new StringWriter();
        writer.write("SELECT LAST_INSERT_ID()");
        selectQuery.setSQLString(writer.toString());
        return selectQuery;
    }

The case becomes freak when we come to Oracle IDENTITY.

EclipseLink provides the functionality what supports Oracle-specific syntax statement returning generated values of columns:

    @Override
    public DatabaseCall buildCallWithReturning(SQLCall sqlCall, Vector returnFields) {
        SQLCall call = new SQLCall();
        call.setParameters(sqlCall.getParameters());
        call.setParameterTypes(sqlCall.getParameterTypes());

        Writer writer = new CharArrayWriter(200);
        try {
            writer.write("BEGIN ");
            writer.write(sqlCall.getSQLString());
            writer.write(" RETURNING ");

            for (int i = 0; i < returnFields.size(); i++) {
                DatabaseField field = (DatabaseField)returnFields.elementAt(i);
                writer.write(field.getNameDelimited(this));
                if ((i + 1) < returnFields.size()) {
                    writer.write(", ");
                }
            }

            writer.write(" INTO ");

            for (int i = 0; i < returnFields.size(); i++) {
                DatabaseField field = (DatabaseField)returnFields.elementAt(i);
                call.appendOut(writer, field);
                if ((i + 1) < returnFields.size()) {
                    writer.write(", ");
                }
            }

            writer.write("; END;");

            call.setQueryString(writer.toString());

        } catch (IOException exception) {
            throw ValidationException.fileError(exception);
        }

        return call;
    }

Hibernate doesn’t though. OK, the time of experiments has come. Let’s take Oracle 12c and create the correspondent table (I don’t use JPA schema autogeneration feature – this is important):

CREATE TABLE accounts (
     id INT GENERATED AS IDENTITY,
     username VARCHAR(30) NOT NULL,
     password RAW(255) NOT NULL,
     email  VARCHAR(255) NOT NULL,
     PRIMARY KEY (id)
) TABLESPACE users

Try now the code where GenerationType.IDENTITY strategy is used.

       EntityManagerFactory emf = Persistence.createEntityManagerFactory("OracleUnit");
       EntityManager em = emf.createEntityManager(); 
       em.getTransaction().begin(); 
       Account acc = new Account();
	acc.setUsername("Test");
	acc.setPassword("Test".getBytes());
	acc.setEmail("Test@Test.com");
	em.persist(acc);
	em.getTransaction().commit();
	System.out.println( "New ID is "+acc.getId() );
       em.close();
       emf.close();   

We get «ORA-02289: sequence not exists.». What’s up ?

When we use Oracle provider GenerationType.IDENTITY strategy inside uses predefined sequence object SEQ_GEN_IDENTITY:

public static final String DEFAULT_IDENTITY_GENERATOR = "SEQ_GEN_IDENTITY

Sure we get a problem, because we did not create this sequence ! But – we don’t want this sequence to use, we have autogenerated ID and want to get a value back post insertion. Do we have an option ?

Yes, we have , and this option was previously used when triggers were the standard way of autogenerated keys in Oracle. This is EclipseLink-specific @ReturnInsert annotation:

    @Id
    @Column(name = "ID")
    @ReturnInsert
    private BigDecimal id;

But when we try. we get another fail:

Error Code: 32795
Call: BEGIN INSERT INTO ACCOUNTS (ID, EMAIL, PASSWORD, USERNAME) VALUES (?, ?, ?, ?) RETURNING ID INTO ?; END;
...
Internal Exception: java.sql.SQLException: ORA-32795: cannot insert into a generated always identity column

Problems are very clear – IDENTITY in it’s pure incarnation prevents the client from the explicit insertion of ID value into auto-generated ID. Very logical BTW, isn’t it ? Else how can we guarantee uniqueness ?

Now one point to remember. Oracle allows to overrode this problem using DEFAULT ON NULL option (we CAN insert null into id and OLNY in this case IDENTIRY shall work – poor idea IMHO for the reasons I described above, but it works):

CREATE TABLE accounts (
id INT GENERATED BY DEFAULT ON NULL AS IDENTITY,
username VARCHAR(30) NOT NULL,
password RAW(255) NOT NULL,
email  VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
) TABLESPACE users

In this case we get the generated ID:

New ID is 1

Fine. But I don’t want to allow clients non-consistent values insert, let’s get back to original option:

CREATE TABLE accounts (
     id INT GENERATED AS IDENTITY,
     username VARCHAR(30) NOT NULL,
     password RAW(255) NOT NULL,
     email  VARCHAR(255) NOT NULL,
     PRIMARY KEY (id)
) TABLESPACE users

And my purpose now to exclude ID from the column and value list in INSERT statement. Can I do it and how ? Yes, I can, using “returnOnly” option of the annotation:

@ReturnInsert(returnOnly=true)

Now everything works like a charm and as expected:

New ID is 1

Now – come to Hibernate.

Hibernate’s Oracle10gDialect prevents IDENTITY strategy from use at all:

Dialect does not support identity key generation

But Hibernate allows us to customize the value generator and solve this problem. Of course this requires a lot of coding – much more than in EclipseLink. There is the thread in Hibernate forum describing this methos: https://forum.hibernate.org/viewtopic.php?t=973262 — thanks  much for Jean-Pol Landrain !
I shall provide here the example what is  based purely on OraclePreparedStatement approach to illustrate how  Oracle-specific JDBC  code  can handle the problem:

package org.hibernate.id;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import oracle.jdbc.OraclePreparedStatement;

import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.SequenceIdentityGenerator.NoCommentsInsert;
import org.hibernate.id.insert.AbstractReturningDelegate;
import org.hibernate.id.insert.IdentifierGeneratingInsert;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;

public class Ora12IdentityGenerator extends AbstractPostInsertGenerator {

   @Override
   public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(
      PostInsertIdentityPersister persister, Dialect dialect, boolean isGetGeneratedKeysEnabled)
      throws HibernateException {
      return new Delegate(persister, dialect);
   }

   public static class Delegate extends AbstractReturningDelegate {

      private Dialect dialect;
      private String[] keyColumns;
      private int keyId;

      public Delegate(PostInsertIdentityPersister persister, Dialect dialect) {
         super(persister);
         this.dialect = dialect;
         this.keyColumns = getPersister().getRootTableKeyColumnNames();
         if (keyColumns.length > 1) {
            throw new HibernateException(
               "trigger assigned identity generator cannot be used with multi-column keys");
         }
      }

      @Override
      public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
         return new NoCommentsInsert(dialect);
      }

      @Override
      protected PreparedStatement prepare(String insertSQL, SessionImplementor session) 
              throws SQLException {
          insertSQL = insertSQL + " returning "+keyColumns[0]+" into ?";
          OraclePreparedStatement os = (OraclePreparedStatement)session.connection().prepareStatement(insertSQL);
          keyId = insertSQL.split("\\?").length;
          os.registerReturnParameter(keyId, Types.DECIMAL);
          return os;
      }

      @Override
      protected Serializable executeAndExtract(PreparedStatement insert, SessionImplementor session)
         throws SQLException {
          
        OraclePreparedStatement os = (OraclePreparedStatement)insert;
        os.executeUpdate();
      
        ResultSet generatedKeys = os.getReturnResultSet();
        if (generatedKeys == null) {
            throw new HibernateException("Nullable Resultset");
        }
        try {
           return IdentifierGeneratorHelper.getGeneratedIdentity(
                   generatedKeys, 
                   keyColumns[0],
                   getPersister().getIdentifierType());
        } finally {
            generatedKeys.close();
        }
      }
   }
}

Correspondent  changes in entry code  with @GenericGenerator Annotation should  be:

    @Id
    @Column(name = "ID")
    @Basic(optional=false)
    @GeneratedValue(generator="Identity12c")
    @GenericGenerator(name="Identity12c", strategy="org.hibernate.id.Ora12IdentityGenerator")
    private BigDecimal id;

Now  we have

New ID: 3

Another example what is Oracle-independent (in terms of JDBC library, not in syntax !) you can see here.

Posted in Oracle | 3 Comments