Add JNDI Sample

Add a sample application that can be deployed to WildFly to demonstrate
how JTA can be used when running in a application server.

See gh-947
pull/1323/merge
Josh Long 10 years ago committed by Phillip Webb
parent 239d19d38c
commit 85cfd016a6

@ -40,6 +40,7 @@
<module>spring-boot-sample-jetty</module>
<module>spring-boot-sample-jta-atomikos</module>
<module>spring-boot-sample-jta-bitronix</module>
<module>spring-boot-sample-jta-jndi</module>
<module>spring-boot-sample-liquibase</module>
<module>spring-boot-sample-parent-context</module>
<module>spring-boot-sample-profile</module>

@ -0,0 +1,148 @@
## Introduction
This application is intended to run inside of a Java EE application server such as
JBoss Wildfly. It demonstrates Spring Boot's auto-configuration defaulting for a
container-managed `TransactionManager` and `DataSource`. This example unfortunately
requires a fully configured Wildfly installation. You'll need to configure a PostgreSQL
XA `DataSource` and an XA `ConnectionFactory` in the Java EE application server's
JNDI machinery.
## Setup
### Postgres
We will use postgres as the underlying database, v9.3.5 or above is recommend. Follow
the installation instructions from http://www.postgresql.org/[postgresql.org] or use
a package manager to install the appropriate binaries.
Once installed you will need to initialize and start the server.
[source,indent=0]
----
$ initdb /usr/local/var/postgres -E utf8
$ pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start
----
With the server running you can create a user and a database:
[source,indent=0]
----
$ createuser springboot
$ createdb bootdemo
----
Finally you can type `psql bootdemo` to configure a password:
[source,indent=0]
----
ALTER USER springboot WITH PASSWORD 'springboot';
\q
----
### WildFly 8.1
Download an install WildFly 8.1 from http://wildfly.org/downloads/[wildfly.org]. Once
installed you will need to add a management user by running `$JBOSS_HOME/bin/add-user.sh`
(see the WildFly documentation for details).
You will also need to add a postgresql module. The following commands setup the basic
structure:
[source,indent=0]
----
$ cd $JBOSS_HOME
mkdir -p modules/org/postgresql/main
wget http://jdbc.postgresql.org/download/postgresql-9.3-1102.jdbc41.jar
mv postgresql-9.3-1102.jdbc41.jar modules/org/postgresql/main
----
You can then add the following to `$JBOSS_HOME/modules/org/postgresql/main/module.xml`:
[source,indent=0]
----
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.0" name="org.postgresql">
<resources>
<resource-root path="postgresql-9.3-1102.jdbc41.jar"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.transaction.api"/>
</dependencies>
</module>
----
## Configuration
A custom WildFly configuration is required for the XA `DataSource` and `ConnectionFactory`
elements. The `$JBOSS_HOME/standalone/configuration/standalone-full.xml` is a good
starting point, copy this file to
`$JBOSS_HOME/standalone/configuration/standalone-boot-demo.xml` then make the following
changes.
### DataSource
You need to register a PostgreSQL XA `Driver` and then configure an `xa-datasource`.
Here's a complete listing of the `xa-datasource` contribution to the `datasources`
element, and the `driver` contribution to the `drivers` element to configure a PostgreSQL
DB connection to localhost.
https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/6/html-single/Administration_and_Configuration_Guide/index.html#Install_a_JDBC_Driver_with_the_Management_Console[You can learn more from the documentation].
[source,xml,indent=0,subs="verbatim,attributes"]
----
<datasources>
...
<xa-datasource
jndi-name="java:jboss/datasources/bootdemo"
pool-name="CrmXADS"
enabled="true">
<xa-datasource-property name="url">jdbc:postgresql://localhost:5432/crm</xa-datasource-property>
<driver>postgres</driver>
<xa-pool>
<min-pool-size>10</min-pool-size>
<max-pool-size>20</max-pool-size>
<prefill>true</prefill>
</xa-pool>
<security>
<user-name>springboot</user-name>
<password>springboot</password>
</security>
</xa-datasource>
<drivers>
...
<driver name="postgres" module="org.postgresql">
<xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
</driver>
</drivers>
</datasources>
----
### JMS Destination
You will also need to configure a `javax.jms.Destination` by contributing the following to
the `hornetq-server` element:
[source,xml,indent=0,subs="verbatim,attributes"]
----
<jms-destinations>
<jms-queue name="accounts">
<entry name="java:/jms/queue/bootdemo"/>
</jms-queue>
...
</jms-destinations>
----
## Running and deploying the sample
Run Wildfly with the following command:
[source,indent=0]
----
$JBOSS_HOME/bin/standalone.sh -c standalone-boot-demo.xml
----
Once running you can deploy the application by copying
`target/spring-boot-sample-jta-jndi.war` to `$JBOSS_HOME/standalone/deployments`.
Open a browser to http://localhost:8080/spring-boot-sample-jta-jndi to trigger the
sample. You should see the current count (it will increment by one on each refresh). If
you check the logs you should see a `----> Josh` message and some counts. Notice how the
`error` message triggers an exception with causes both the database insert and the JMS
message to be rolled back.

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>1.2.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-jta-jndi</artifactId>
<name>Spring Boot JNDI JTA Sample</name>
<packaging>war</packaging>
<description>Spring Boot JNDI JTA Sample</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>jms-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-sample-jta-jndi</finalName>
</build>
</project>

@ -0,0 +1,43 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed 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 sample.jndi;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Account {
@Id
@GeneratedValue
private Long id;
private String username;
Account() {
}
public Account(String username) {
this.username = username;
}
public String getUsername() {
return this.username;
}
}

@ -0,0 +1,23 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed 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 sample.jndi;
import org.springframework.data.repository.CrudRepository;
public interface AccountRepository extends CrudRepository<Account, Long> {
}

@ -0,0 +1,47 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed 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 sample.jndi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class AccountService {
private final JmsTemplate jmsTemplate;
private final AccountRepository accountRepository;
@Autowired
public AccountService(JmsTemplate jmsTemplate, AccountRepository accountRepository) {
this.jmsTemplate = jmsTemplate;
this.accountRepository = accountRepository;
}
public void createAccountAndNotify(String username) {
this.jmsTemplate.convertAndSend("java:/jms/queue/bootdemo", username);
Account entity = new Account(username);
this.accountRepository.save(entity);
if ("error".equals(username)) {
throw new RuntimeException("Simulated error");
}
}
}

@ -0,0 +1,30 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed 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 sample.jndi;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
@Component
public class Messages {
@JmsListener(destination = "java:/jms/queue/bootdemo")
public void onMessage(String content) {
System.out.println("----> " + content);
}
}

@ -0,0 +1,28 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed 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 sample.jndi;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class SampleJndiApplication {
}

@ -0,0 +1,29 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed 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 sample.jndi;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class SampleJndiInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SampleJndiApplication.class);
}
}

@ -0,0 +1,51 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed 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 sample.jndi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class WebController {
private final AccountService service;
private final AccountRepository repository;
@Autowired
public WebController(AccountService service, AccountRepository repository) {
this.service = service;
this.repository = repository;
}
@RequestMapping("/")
public String hello() {
System.out.println("Count is " + this.repository.count());
this.service.createAccountAndNotify("josh");
try {
this.service.createAccountAndNotify("error");
}
catch (Exception ex) {
System.out.println(ex.getMessage());
}
long count = this.repository.count();
System.out.println("Count is " + count);
return "Count is " + count;
}
}

@ -0,0 +1,5 @@
spring.jpa.generate-ddl=true
spring.datasource.jndi-name=java:jboss/datasources/bootdemo
# Workaround SPR-12118
spring.jpa.open-in-view=false
Loading…
Cancel
Save