tag:blogger.com,1999:blog-47285954801353908792024-02-21T20:28:28.580-08:00You Xu's blog The World of Programming. This blog mainly contains my notes on the development on Java EE platform.You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.comBlogger104125tag:blogger.com,1999:blog-4728595480135390879.post-22330528462609917812024-01-06T19:38:00.000-08:002024-01-06T19:38:23.446-08:00Order Management System<iframe width="480" height="270" src="https://youtube.com/embed/BOv5LaEL-RU?si=dbzWUlVAz5E_jLjO" frameborder="0"></iframe>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-63607617290243439092023-12-27T19:36:00.000-08:002023-12-27T19:36:59.039-08:00How to Debug Angular code using Chrome Developer tools<p>To debug Angular application in Chrome, open the Developer tools. </p>
<p>Click on the "Sources" tab.
The left panel is a tree list. The last item is "webpack://". Inside it, the second item is
just a dot "." character. Click on it. You can see it has a "src" folder that contains
all the source Angular code. </p>
<p>A better way is to use the middle panel. Put your mouse inside it and type the key Control-P.
It will give a popup where you can type the name of the source file and it will show the
file names underneath that match the name you typed. You can then click to select the file. </p>
<p>The 'Console' in the Develper tools is also very useful. It can output the log messages in the Angular
components. But every time a page is navigated to a new page, the previous log messages can be cleared up.
To preserve the console log, on the top right of the console output window there is the 'Console Settings' icon.
Click on that. There is a checkbox for 'Preserve log'. Click on the checkbox to enable it. Now the console log
messages will not be removed when the page is navigated to a new page. </p>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-55655465190843165492021-08-12T13:54:00.004-07:002021-08-12T13:57:03.366-07:00Spring JPA, Native Query, POJO, and Java Stream<p>Spring JPA can return a stream of objects in its repository API. This is a very good feature. It will help to resolve the performance issue when millions of data is returned from the database by a query. It seems to me that Spring JDBC template does not have this ability. The JdbcTemplate has a result set callback function. But that does not seem to achieve the same thing as a stream can do.
<p>An issue with using the ORM is that the many domain data classes need to map to tables. For a legacy system, the easiest way is to use POJO and native query so that such a map is not needed. In this example application, I use POJO and native query in JPA to return a stream of the POJO object.
<p> pom.xml
<pre>
<?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>
<groupId>spring-stream-sample</groupId>
<artifactId>spring-stream-sample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-tutorial-basics</name>
<description>Spring Boot Tutorial - Basic Concept Examples</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath />
</parent>
<properties>
<hibernate.version>5.4.0.Final</hibernate.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>
</dependency>
<!-- <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId>
</dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId>
</dependency> -->
<!-- https://mvnrepository.com/artifact/javax.persistence/javax.persistence-api -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>12.2.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
</pre>
<p> App.java
<pre>
package com.sample.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication(scanBasePackages = { "com.sample.app" })
@ComponentScan({ "com.sample.app" })
@EnableConfigurationProperties
@EnableAutoConfiguration
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
</pre>
<p>SampleController.java
<pre>
package com.sample.app.controller;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.sample.app.model.ShipmentModel;
import com.sample.app.repository.SampleRepository;
@RestController
@RequestMapping(value = "/sample")
public class SampleController {
@PersistenceContext
EntityManager entityManager;
@Autowired
private SampleRepository sampleRepository;
@GetMapping(value = "/shipmentModel/stream/csv/{userId}")
@Transactional(readOnly = true)
public void generateCSVUsingStreamShipmentModel(@PathVariable("userId") long userId,
HttpServletResponse response) {
response.addHeader("Content-Type", "application/csv");
response.addHeader("Content-Disposition", "attachment; filename=shipmentModel.csv");
response.setCharacterEncoding("UTF-8");
System.out.println("Start working. Time is " + new Date());
try (Stream<ShipmentModel> shipmentsStream = sampleRepository.findShipmentModels(userId);) {
PrintWriter out = response.getWriter();
// print column header
out.write(String.join(",", ShipmentModel.headers));
out.write("\n");
shipmentsStream.forEach(shipmentModel -> {
out.write(shipmentModel.toString());
out.write("\n");
entityManager.detach(shipmentModel);
});
System.out.println("Before flush. Time is " + new Date());
out.flush();
out.close();
shipmentsStream.close();
System.out.println("Stream closed. Time is " + new Date());
} catch (IOException ix) {
throw new RuntimeException("There is an error while downloading shipments.csv", ix);
}
}
}
</pre>
<p>ShipmentModel.java This is the POJO class.
<pre>
package com.sample.app.model;
import javax.persistence.Column;
import javax.persistence.ColumnResult;
import javax.persistence.ConstructorResult;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedNativeQuery;
import javax.persistence.SqlResultSetMapping;
/*
* Notes:
* 1. This is a POJO. It does not map to any database table. It is annotated as an Entity. But there is no annotation @Table for it.
* 2. The query is a pure native query. It does not use any entity mapping.
* 3. The critical connection between the query result set and the POJO is the @CoumnResult annotation. In that annotation, the "name"
* attribute must exactly match the column name returned by the native query. After that, the POJO uses its constructor to get the value.
* In the constructor, the position of the argument determines what a property of the POJO will be assigned the column value. The name
* of the property does not matter. For example, the property "shipmentLabel" of the class ShipmentModel can be any name. This is different
* from the entity class that maps to an actual table. In that case, there can an implicit mapping between the property name and
* column name of the table. For example, the camel case property name shipmentLabel will map to the table column name shipment_label.
*
*/
@NamedNativeQuery(name = "findShipmentModels", query = ShipmentModel.sampleSql_native, resultClass = ShipmentModel.class, resultSetMapping = "ShipmentModelMapping")
@SqlResultSetMapping(name = "ShipmentModelMapping", classes = {
@ConstructorResult(targetClass = ShipmentModel.class, columns = { @ColumnResult(name = "id", type = Long.class),
@ColumnResult(name = "username", type = String.class),
@ColumnResult(name = "label", type = String.class)
}) })
@Entity
public class ShipmentModel {
public static String[] headers = { "userId", "userName", "label" };
public static void main(String[] args) {
System.out.println(String.join(",", headers));
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(updatable = false)
private Long userId;
private String userName;
private String shipmentLabel;
public ShipmentModel(Long userId, String userName, String label) {
this.userId = userId;
this.userName = userName;
this.shipmentLabel = label;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getShipmentLabel() {
return shipmentLabel;
}
public void setShipmentLabel(String shipmentLabel) {
this.shipmentLabel = shipmentLabel;
}
@Override
public String toString() {
return String.join(",", "" + this.getUserId(), "" + this.getUserName(), "" + this.shipmentLabel);
}
final static String sampleSql_native = " SELECT user.*, shipment.label "
+ " from user JOIN shipment ON user.id = shipment.user_id " + " WHERE user.id = :userId";
}
</pre>
<p> SampleRepository.java
<pre>
package com.sample.app.repository;
import java.util.stream.Stream;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import com.sample.app.model.ShipmentModel;
public interface SampleRepository extends JpaRepository<ShipmentModel, Long> {
@Query(nativeQuery = true, name = "findShipmentModels")
public Stream<ShipmentModel> findShipmentModels(@Param("userId") Long userId);
}
</pre>
<p>application.properties in main/resources
<pre>
server.port=9123
server.servlet.context-path=/api
spring.datasource.url=jdbc:mysql://localhost:3306/planet
spring.datasource.username=zach
spring.datasource.password=planet
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
logging.level.org.hibernate.type=trace
spring.jpa.properties.hibernate.type=trace
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.format_sql=true
</pre>
<p> create.sql in main/resources
<pre>
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`first_name` varchar(50) DEFAULT NULL,
`last_name` varchar(50) DEFAULT NULL,
`gender` varchar(10) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`status` tinyint(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=10001 ;
CREATE TABLE IF NOT EXISTS `shipment` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`label` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (user_id) REFERENCES user(id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=10001 ;
INSERT INTO `user` (`id`, `username`, `first_name`, `last_name`, `gender`, `password`, `status`) VALUES
(1, 'rogers6', 'rogers', 'miller', 'M', 'psqwer', 1),
(2, 'rogpa', 'rogers', 'paul', 'M', 'dfgderet', 1),
(3, 'davm', 'david', 'merz', 'M', 'wetwet', 1),
(4, 'ma', 'mary', 'sanders', 'M', 'dfghsgh', 1),
(5, 'jhojho', 'jhonson', 'johnson', 'F', '45yhererh', 1),
(6, 'dansc', 'daniel', 'scott', 'F', 'gfndftyet', 1);
INSERT INTO `shipment` (`id`, `user_id`, `label`) VALUES
(1, '2', 'heavy box'),
(2, '1', 'interseting work'),
(3, '3', 'stones'),
(4, '3', 'computers'),
(5, '4', 'Green pepper'),
(6, '5', 'tables');
</pre>
<p>readme.txt
<pre>
Reference Source:
https://github.com/greyseal/spring-boot-csv-download
To run the application, the MySQL database needs to be started first. Open Windows task manager and
go to the service tab. Find "MYSQL57" and start it.
Question: Where in the application is it configured to use MySQL?
Answer: It is configured in the file application.properties.
To test shipment csv download, use the following url:
http://localhost:9123/api/sample/shipmentModel/stream/csv/{userId}
In the above, {userId} is the user Id. Example:
http://localhost:9123/api/sample/shipmentModel/stream/csv/3
</pre>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-41842577220116142082020-12-10T20:18:00.013-08:002021-12-12T08:20:36.109-08:00Spring AOP vs AspectJThis link to the official Spring document is very helpful: https://docs.spring.io/spring-framework/docs/2.0.x/reference/aop.html#aop-understanding-aop-proxies
The following paragraph from the above document contains important technical details.
<font color="blue">
<p>
6.8.1.2. Working with multiple application contexts
The AnnotationBeanConfigurerAspect used to implement the @Configurable support is an AspectJ singleton aspect. The scope of a singleton aspect is the same as the scope of static members, that is to say there is one aspect instance per classloader that defines the type. This means that if you define multiple application contexts within the same classloader hierarchy you need to consider where to define the <aop:spring-configured/> bean and where to place spring-aspects.jar on the classpath.
<p>
Consider a typical Spring web-app configuration with a shared parent application context defining common business services and everything needed to support them, and one child application context per servlet containing definitions particular to that servlet. All of these contexts will co-exist within the same classloader hierarchy, and so the AnnotationBeanConfigurerAspect can only hold a reference to one of them. In this case we recommend defining the <aop:spring-configured/> bean in the shared (parent) application context: this defines the services that you are likely want to inject into domain objects. A consequence is that you cannot configure domain objects with references to beans defined in the child (servlet-specific) contexts using the @Configurable mechanism (probably not something you want to do anyway!).
<p>
When deploying multiple web-apps within the same container, ensure that each web-application loads the types in spring-aspects.jar using its own classloader (for example, by placing spring-aspects.jar in 'WEB-INF/lib'). If spring-aspects.jar is only added to the container wide classpath (and hence loaded by the shared parent classloader), all web applications will share the same aspect instance which is probably not what you want.
</font>
<h3>Overview of Spring AOP</h3>
<p>
This programming style is very confusing because of the two different imlementations of AOP and two different styles of configuration.
From the above link, my conclusion is summarized below.
<ol>
<li> There are basically two implemenations of AOP: AspectJ and Spring AOP. </li>
<li> There are 3 different configuration styles: Aspect language style, @AspectJ annotation style, and XML style. </li>
<li> If choose AspectJ, you have two styles to choose: Aspect language style and @AspectJ.
If choose Spring AOP, you have two styles to choose: @AspectJ and XML style. </li>
<li> In Spring AOP, even if you choose the @AspectJ style, it does not mean that you are using the AspectJ implemenation. It is still Spring AOP. See below paragraph in the above link: </li>
</ol>
<font color="blue">
<p>
6.2.1. Enabling @AspectJ Support
To use @AspectJ aspects in a Spring configuration you need to enable Spring support for configuring Spring AOP based on @AspectJ aspects, and autoproxying beans based on whether or not they are advised by those aspects. By autoproxying we mean that if Spring determines that a bean is advised by one or more aspects, it will automatically generate a proxy for that bean to intercept method invocations and ensure that advice is executed as needed.
<p>
The @AspectJ support is enabled by including the following element inside your spring configuration:
<p>
<aop:aspectj-autoproxy/>
</font>
<p>
To use XML style, use
<pre>
<aop:config>
.....
</aop:config>
</pre>
<h3> Proxy vs Code Weaving </h3>
<p>Spring AOP is proxy-based. There are two kinds of implementation for the proxy. JDK dynamic proxy and the CGLIB proxy. If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created.
<p> You can force the use of CGLIB proxies in configuration. See the referred link.
<p> Because Spring AOP is proxy based, there is the self-invocation issue. See "6.6.1. Understanding AOP proxies" of the linked document. AspectJ does not have this self-invocation issue.
<p> AspectJ is not proxy-based. AspectJ uses AspectJ compiler/weaver. There are two kinds of weaving: build-time weaving and load-time weaving. The build-time weaving happens when you compile the application. The load-time weaving happens when the class is loaded into JVM. Further, for load-time weaving, there are two ways of the implemenation: (1) weaving at the JVM launch time (2) weaving when a class is loaded into JVM after JVM started.
<p> See https://stackoverflow.com/questions/5717883/aspectj-compiler-ajc-vs-load-time-weaving#:~:text=1%20Answer&text=ajc%20(compile%20time)%20will%20only,that%20are%20affected%20by%20aspects.&text=The%20reason%20is%20that%20under,is%20not%20necessary%20under%20CTW.
<h3> Mix Spring AOP and AspectJ</h3>
<p>
What if you do want to use AspectJ instead of or in addition to Spring AOP? Section "6.8. Using AspectJ with Spring applications" of the linked document covers this.
<h3>AspectJ</h3>
<p>
AspectJ is an example of using the Java Instrument feature. This feature is a fundamentally separate from other Java programming areas.
See JavaDoc https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html for more information.
<p> To use this feature, there are two options:
<ol>
<li> To specify an agent on the command line when a JVM is launched.</li>
<li> To configure the application the JVM will use it after JVM is launched. </li>
</ol>
See https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html.
<p> The pure AspectJ uses the first option. You need to specify the agent on the command line to start JVM.
<p> The Spring support for AspectJ adds the second option so you can weave the Aspects when the class is loaded into JVM. This enables the applicaiton to do AspectJ on the classloader level.
<h3>An Example</h3>
<p>
Below is an example in to create an AOP class in a Spring boot application. It will set customized information when a database connection is created in Oracle.
<p> To use this class, add the following dependency to the pom.xml file:
<pre>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</pre>
The AOP class is below. It uses pure annotation.
<pre>
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
@Aspect
@Configuration
public class ClientIdentifierConnectionPreparer {
private static final Logger LOGGER = LoggerFactory.getLogger(ClientIdentifierConnectionPreparer.class);
private static String CLIENT_IDENTIFIER = "-MyApplication-";
private static String CLASS_NAME = "ClientIdentifierConnectionPreparer.CLASS";
private static String METHOD_NAME = "afterGetConnection(JoinPoint joinPoint, Object result)";
@AfterReturning(value = "execution(java.sql.Connection javax.sql.DataSource.getConnection(..))", returning = "result")
public void afterGetConnection(JoinPoint joinPoint, Object result) throws SQLException {
Connection connection = (Connection) result;
String prepSql = "{ call DBMS_SESSION.SET_IDENTIFIER(?) }";
try (CallableStatement cs = connection.prepareCall(prepSql)) {
cs.setString(1, CLIENT_IDENTIFIER);
cs.execute();
}
prepSql = "{ call DBMS_APPLICATION_INFO.SET_ACTION(?)}";
try (CallableStatement cs = connection.prepareCall(prepSql)) {
cs.setString(1, METHOD_NAME);
cs.execute();
}
prepSql = "{ call DBMS_APPLICATION_INFO.SET_CLIENT_INFO(?)}";
try (CallableStatement cs = connection.prepareCall(prepSql)) {
cs.setString(1, CLASS_NAME);
cs.execute();
}
}
}
</pre>
When there is a connection created, you can use the following query on Oracle to see the session with the specified client identifier.
<pre>
select * from v$session where client_identifier = '-MyApplication-'
</pre>
<p>The following query can show the actual queries used in the session.
<pre>
select
se.sid,
se.username,
se.machine,
se.seconds_in_wait,
se.state,
se.prev_exec_start,
oc.cursor_type,
trim(sql_text) sql_txt
from dual
join v$session se
on client_identifier = '-MyApplication-'
join v$open_cursor oc
on oc.sql_id = se.prev_sql_id and oc.sid = se.sid
</pre>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-72049173917091674042018-05-29T13:44:00.000-07:002018-05-30T08:31:54.321-07:00Use Spring AOP
You can apply AOP on classes directly.
<pre>
<bean id="myService" class="com.example.MyService">
...
</bean>
<bean id="MethodHijackerAdvice" class="com.example.MethodHijacker" />
<bean id="myAdvisor"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="mappedName" value="methodNameToBeHijacked" />
<property name="advice" ref="MethodHijackerAdvice" />
</bean>
<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>*Service</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
</list>
</property>
</bean>
</pre>
<p>With this configuration, all the classes whose name ends with "Service" will be hijacked by this AOP. The myService
class is one of them.
<p>
<pre>
MethodHijacker.java
====================
public class MethodHijacker implements MethodInterceptor
{
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("MethodHijacker : Before method hijacked!");
Object result = methodInvocation.proceed();
System.out.println("MethodHijacker : After method hijacked!");
return result;
}
}
</pre>
<br>
<p>
In the example above, the AOP proxies will be created on the classes that match the name pattern. Spring has another more powerful proxy creator DefaultAdvisorAutoProxyCreator. This one does not need to know the class names. It
just creates the proxies for all the classes that match any advisor configured.
<pre>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
</pre>
<br>
You can also apply AOP on interfaces.
<ol>
<li> Use Spring AOP factory to create bean instance.
<pre>
<bean id="abstractParent" class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true">
<property name="interceptorNames">
<list>
<value>myIntroductionAdvisor</value>
</list>
</property>
</bean>
</pre>
<p> Note that it has the value "true" for the attribute "abstract". So this does not
actually create any bean by itself.
<p> To actually create a bean, one example is below that makes use of the abstract bean as the parent:
<pre>
<bean id="myAOPbean" parent="abstractParent">
<property name="proxyInterfaces" value="com.example.myBeanInterface" />
<property name="target">
<bean parent="abstractParentTarget">
<constructor-arg value="com.example.Abc" />
</bean>
</property>
</bean>
<bean id="abstractParentTarget" class="com.example.CommonBehavior" abstract="true"/>
</pre>
<p> Note that com.example.myBeanInterface is an interface. This makes sense because the interface can then be proxied by AOP.
</li>
<li> In the configuration for AOP factory, specify the interceptor name (myIntroductionAdvisor in this case)
</li>
<li> The interceptor is an advisor. The example below extends the Spring Advisor DefaultIntroductionAdvisor:
<pre>
MyIntroductionAdvisor
===========================
import org.springframework.aop.support.DefaultIntroductionAdvisor;
public class MyIntroductionAdvisor extends DefaultIntroductionAdvisor {
public MyIntroductionAdvisor() {
super(new MyInterceptor());
}
@Override
public boolean matches(Class clazz) {
return MyMatcher.class.isAssignableFrom(clazz);
}
}
</pre>
<p> In this advisor, you specify two things:
<br> (1) What classes will be AOP'ed. ( The MyMatcher classes )
<br> (2) What class will perform the AOP action. ( MyInterceptor in this case)
<pre>
public interface MyMatcher<T> {
// can optionally define common behavior here. For example the method below:
// List<T> process(String method, Object[] queryArguments);
}
</pre>
<p>MyInterceptor can now apply behavior on the methods of all the MyMatcher classes. The interceptor API method is below.
<pre>
public Object invoke(MethodInvocation methodInvocation) throws Throwable
</pre>
<p>CommonBehavior implements the MyMatcher interface.
</li>
<li> The essential thing of the whole configuration is the interceptor that defines what you want to accomplish.
The interceptor will defines what methods will be intercepted and what actions will be performed.
</li>
</ol>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-63025102258147914242018-04-12T08:19:00.000-07:002018-04-12T13:30:48.245-07:00Hibernate one to many relationship persistence<h3>Introduction</h3>
The example below is based on the application https://www.mkyong.com/hibernate/hibernate-one-to-many-relationship-example-annotation.
I modified it a little bit to show how to automatically save the "many" records when the "one" record is saved in a one-to-many relationship.
<br />
<h3>pom.xml</h3>
<pre><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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mkyong.common</groupId>
<artifactId>HibernateExample</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>HibernateExample</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>JBoss repository</id>
<url>http://repository.jboss.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<!-- MySQL database driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.15</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.6.3.Final</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
<!-- logback logging framework-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>0.9.28</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>0.9.28</version>
</dependency>
</dependencies>
</project>
</pre>
<br />
<h3>Database scripts</h3>
<pre>
DROP TABLE IF EXISTS `stock`;
CREATE TABLE `stock` (
`STOCK_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`STOCK_CODE` varchar(10) NOT NULL,
`STOCK_NAME` varchar(20) NOT NULL,
PRIMARY KEY (`STOCK_ID`) USING BTREE,
UNIQUE KEY `UNI_STOCK_NAME` (`STOCK_NAME`),
UNIQUE KEY `UNI_STOCK_ID` (`STOCK_CODE`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `mkyongdb`.`stock_daily_record`;
CREATE TABLE `mkyongdb`.`stock_daily_record` (
`RECORD_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`PRICE_OPEN` float(6,2) DEFAULT NULL,
`PRICE_CLOSE` float(6,2) DEFAULT NULL,
`PRICE_CHANGE` float(6,2) DEFAULT NULL,
`VOLUME` bigint(20) unsigned DEFAULT NULL,
`DATE` date NOT NULL,
`STOCK_ID` int(10) unsigned NOT NULL,
PRIMARY KEY (`RECORD_ID`) USING BTREE,
UNIQUE KEY `UNI_STOCK_DAILY_DATE` (`DATE`),
KEY `FK_STOCK_TRANSACTION_STOCK_ID` (`STOCK_ID`),
CONSTRAINT `FK_STOCK_TRANSACTION_STOCK_ID` FOREIGN KEY (`STOCK_ID`)
REFERENCES `stock` (`STOCK_ID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8;
</pre>
<br />
<h3>Domain classes</h3>
<pre>
package com.mkyong.stock;
import static javax.persistence.GenerationType.IDENTITY;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "stock", catalog = "workshop", uniqueConstraints = { @UniqueConstraint(columnNames = "STOCK_NAME"),
@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {
private Integer stockId;
private String stockCode;
private String stockName;
private List<StockDailyRecord> stockDailyRecords = new LinkedList<StockDailyRecord>();
public Stock() {
}
public Stock(String stockCode, String stockName) {
this.stockCode = stockCode;
this.stockName = stockName;
}
public Stock(String stockCode, String stockName, List<StockDailyRecord> stockDailyRecords) {
this.stockCode = stockCode;
this.stockName = stockName;
this.stockDailyRecords = stockDailyRecords;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "STOCK_ID", unique = true, nullable = false)
public Integer getStockId() {
return this.stockId;
}
public void setStockId(Integer stockId) {
this.stockId = stockId;
}
@Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
public String getStockCode() {
return this.stockCode;
}
public void setStockCode(String stockCode) {
this.stockCode = stockCode;
}
@Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
public String getStockName() {
return this.stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "stock")
public List<StockDailyRecord> getStockDailyRecords() {
return this.stockDailyRecords;
}
public void setStockDailyRecords(List<StockDailyRecord> stockDailyRecords) {
this.stockDailyRecords = stockDailyRecords;
}
}
</pre>
<pre>
package com.mkyong.stock;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "stock_daily_record", catalog = "workshop", uniqueConstraints = @UniqueConstraint(columnNames = "DATE"))
public class StockDailyRecord implements java.io.Serializable {
private Integer recordId;
private Stock stock;
private Float priceOpen;
private Float priceClose;
private Float priceChange;
private Long volume;
private Date date;
public StockDailyRecord() {
}
public StockDailyRecord(Stock stock, Date date) {
this.stock = stock;
this.date = date;
}
public StockDailyRecord(Stock stock, Float priceOpen, Float priceClose,
Float priceChange, Long volume, Date date) {
this.stock = stock;
this.priceOpen = priceOpen;
this.priceClose = priceClose;
this.priceChange = priceChange;
this.volume = volume;
this.date = date;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "RECORD_ID", unique = true, nullable = false)
public Integer getRecordId() {
return this.recordId;
}
public void setRecordId(Integer recordId) {
this.recordId = recordId;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "STOCK_ID", nullable = false)
public Stock getStock() {
return this.stock;
}
public void setStock(Stock stock) {
this.stock = stock;
}
@Column(name = "PRICE_OPEN", precision = 6)
public Float getPriceOpen() {
return this.priceOpen;
}
public void setPriceOpen(Float priceOpen) {
this.priceOpen = priceOpen;
}
@Column(name = "PRICE_CLOSE", precision = 6)
public Float getPriceClose() {
return this.priceClose;
}
public void setPriceClose(Float priceClose) {
this.priceClose = priceClose;
}
@Column(name = "PRICE_CHANGE", precision = 6)
public Float getPriceChange() {
return this.priceChange;
}
public void setPriceChange(Float priceChange) {
this.priceChange = priceChange;
}
@Column(name = "VOLUME")
public Long getVolume() {
return this.volume;
}
public void setVolume(Long volume) {
this.volume = volume;
}
@Temporal(TemporalType.DATE)
@Column(name = "DATE", unique = true, nullable = false, length = 10)
public Date getDate() {
return this.date;
}
public void setDate(Date date) {
this.date = date;
}
}
</pre>
<br/>
<h3>Test program</h3>
<pre>
package com.mkyong;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import org.hibernate.Session;
import com.mkyong.stock.Stock;
import com.mkyong.stock.StockDailyRecord;
import com.mkyong.util.HibernateUtil;
/**
* Note that this project is from
* https://www.mkyong.com/hibernate/hibernate-one-to-many-relationship-example-annotation/
*
* @author yxu
*
*/
public class AppList {
public static void main(String[] args) {
System.out.println("Hibernate one to many (Annotation)");
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Stock stock = new Stock();
stock.setStockCode("70523");
stock.setStockName("PADIN2");
// session.save(stock);
List<StockDailyRecord> stockDailyRecordsSet = new LinkedList<StockDailyRecord>();
for (int i = 0; i < 3; i++) {
StockDailyRecord stockDailyRecords = new StockDailyRecord();
stockDailyRecords.setPriceOpen(new Float("1.2"));
stockDailyRecords.setPriceClose(new Float("1.1"));
stockDailyRecords.setPriceChange(new Float("10.0"));
stockDailyRecords.setVolume(3000000L);
stockDailyRecords.setDate(new Date(2018, 5, 16 + i));
stockDailyRecords.setStock(stock);
stockDailyRecordsSet.add(stockDailyRecords);
}
stock.setStockDailyRecords(stockDailyRecordsSet);
session.save(stock);
session.getTransaction().commit();
System.out.println("Done");
}
}
</pre>
<p/>
Run AppList, you should see that it creates the stock and the 3 StockDailyRecord records in the database.
<p/>
Note that I used List as the collection type in Stock for StockDailyRecord. You can also use Set.
<p/>
Note that the annotation "@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "stock")"
in the class Stock is the most important in the setup. Without "casecade=CascadeType.ALL", the StockDailyRecord will not be saved into the database when the Stock is saved.
<p/>
Both the tables STOCK and STOCK_DAILY_RECORD use the AUTO_INCREMENT ID as the primary key. And the Java annotation uses @GeneratedValue(strategy = IDENTITY). If a sequence is used to generate the ID, change the annotation to something like below: <br/>
<pre>
@Id
@Column(name = "MY_ID")
@SequenceGenerator(name = "MY_SEQ", sequenceName = "MY_SEQ", allocationSize=1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="MY_SEQ")
public Integer getMyId() {
return this.myId;
}
</pre>
<br/>
<h3>Other classes and files</h3>
<pre>
package com.mkyong.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
}
</pre>
hibernate.cfg.xml
<pre>
>?xml version="1.0" encoding="utf-8"?>
>!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
>hibernate-configuration>
>session-factory>
>property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver>/property>
>property name="hibernate.connection.url">jdbc:mysql://localhost:3306/workshop>/property>
>property name="hibernate.connection.username">workshop>/property>
>property name="hibernate.connection.password">za>/property>
>property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect>/property>
>property name="show_sql">true>/property>
>property name="format_sql">true>/property>
>mapping class="com.mkyong.stock.Stock" />
>mapping class="com.mkyong.stock.StockDailyRecord" />
>/session-factory>
>/hibernate-configuration>
</pre>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-26492844270265751262017-12-12T14:13:00.002-08:002017-12-18T12:40:57.950-08:00Debug Javascript in web page in ChromeChrome version used is Version 63.0.3239.84
<ol>
<li> click on the 3 vertical dots at the upper right corner of the Chrome browser screen.
</li>
<li> Select "More tools" -> "Developer tools". The debugger screen will show.
</li>
<li> The debug screen has tabs at the top and the left and right panes below it.
</li>
<li> In the top bar, select "Sources"
</li>
<li> In the left pane, there is a "Network" tab. Select it. It shows a tree structure below. Drill into it to find your page URL. Sometimes you may include some javascript libraries in your page. Those js files will be under the "js" folder in that left pane. You can select it if you want to debug it.
</li>
<li> After the above step, the right pane is just blank. It does not show the html file corresponding to the page URL. You need to reload the
page in order to see the HTML content. A better way is to open the Developer tools before you get to that page in your application.
</li>
<li> Now you can just click on the line number of the HTML content. Just one click is enough. The line number will be high-lighted.
</li>
<li> Now you can click button on the application web page. A new pane will show. If there is Javascript executed, it will stop at the breakpoint.
</li>
<li> The new pane has a "Watch" section. You can write your javascript expressions there to watch their values.
</li>
</ol> You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-13625752172650768972017-09-18T11:56:00.001-07:002017-09-18T12:07:28.139-07:00Spring Application Listener<h2>How to use the Spring Application Listener</h2>
<p>
To set the application to use Spring Application listener, you can do the following.
<br>
1. In the application context file for Spring, declare a bean as follows
<pre>
<bean id="applicationEventMulticaster"
class="org.springframework.context.event.SimpleApplicationEventMulticaster"/>
</pre>
Actually, this may not needed at all. The following method is from Spring. By default, it will create a SimpleApplicationEventMulticaster if none is given.
<pre>
package org.springframework.context.support;
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
/**
* Initialize the ApplicationEventMulticaster.
* Uses SimpleApplicationEventMulticaster if none defined in the context.
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
...
}
</pre>
<br>
2. Create Application listeners
<pre>
import org.springframework.context.ApplicationListener;
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {
...
}
import org.springframework.context.ApplicationEvent;
public class MyEvent extends ApplicationEvent {
...
}
</pre>
<br>
3. Just call the multicastEvent method of the bean applicationEventMulticaster when needed:
applicationEventMulticaster.multicastEvent(event);
<p>
The method will invoke the application listeners to act on the event.
<pre>
public void multicastEvent(final ApplicationEvent event) {
for (final ApplicationListener listener : getApplicationListeners(event)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@SuppressWarnings("unchecked")
public void run() {
listener.onApplicationEvent(event);
}
});
}
else {
listener.onApplicationEvent(event);
}
}
}
</pre>
<p>
<h2>How does the Application know what application listeners are listening?</h2>
<p>
At the application startup, the beans that implement the ApplicationListener interface will be automatically detected and registered.
<pre>
ContextLoaderListener(ContextLoader).initWebApplicationContext(ServletContext)
->
XmlWebApplicationContext(AbstractApplicationContext).refresh();
->
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory). applyBeanPostProcessorsAfterIniitalization
->
AbstractApplicationContext$ApplicationListenerDetector.postProcessAfterInitialization
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof ApplicationListener) {
// potentially not detected as a listener by getBeanNamesForType retrieval
Boolean flag = this.singletonNames.get(beanName);
if (Boolean.TRUE.equals(flag)) {
// singleton bean (top-level or inner): register on the fly
addApplicationListener((ApplicationListener) bean);
}
else if (flag == null) {
if (logger.isWarnEnabled() && !containsBean(beanName)) {
// inner bean with other scope - can't reliably process events
logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
"but is not reachable for event multicasting by its containing ApplicationContext " +
"because it does not have singleton scope. Only top-level listener beans are allowed " +
"to be of non-singleton scope.");
}
this.singletonNames.put(beanName, Boolean.FALSE);
}
}
return bean;
}
public void addApplicationListener(ApplicationListener listener) {
if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.addApplicationListener(listener);
}
else {
this.applicationListeners.add(listener);
}
}
</pre>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-65798937833579651302017-02-19T10:57:00.000-08:002017-03-28T12:30:38.522-07:00How to do unit test on JPAIt will be very inconvenient to test JPA DAO classes if you need to build and deploy the application to an application server. You can
actually do unit tests on JPA without using any application server. The following is one example.
<p>
1. The entity class.
<pre>
@Entity
@Table(name = "T_SAMPLE")
public class SampleData implements Serializable {
private SampleDataId id;
private String xyz;
@EmbeddedId
public SampleDataId getId() {
return this.id;
}
public void setId(final SampleDataId id) {
this.id = id;
}
@Column(name = "XYZ")
public String getXyz() {
return this.xyz;
}
public void setXyz(final String xyz) {
this.xyz = xyz;
}
}
</pre>
2. The DAO class to be tested.
<pre>
packge com.example.dao;
@Repository
public class SampleDataDAO {
protected static final Logger LOG = Logger.getLogger(SampleDataDAO.class);
@PersistenceContext
private EntityManager entityManager;
/**
* Returns List of SampleData type.
*/
public List<SampleData> getSomeData() {
List<SampleData> results = new ArrayList<SampleData>();
EntityManager entityManager = getEntityManager();
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<SampleData> query = builder
.createQuery(SampleData.class);
final Root<SampleData> root = query.from(SampleData.class);
List<Predicate> predicates = new ArrayList<Predicate>();
query.select(root);
......
query.where(predicates.toArray(new Predicate[] {}));
results = entityManager.createQuery(query).getResultList();
return results;
}
public void saveSomeData(SampleData sampleData) {
EntityManager entityManager = getEntityManager();
entityManager.persist(sampleData);
}
}
</pre>
3. The test class:
<pre>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/testApplicationContext.xml" })
public class SampleDataDAOTest {
@Autowired
private SampleDataDAO sampleDataDAO;
@Test
public void testRead() {
sampleDataDAO.getSomeData(...);
}
@Test
@Transactional
public SampleData testSave() {
SampleData sampleData = new SampleData();
sampleData.setXyz("bla");
sampleDataDAO.saveSomeData(sampleData);
SampleData savedData = sampleDataDAO.getSampleDataById(sampleData.getId());
Assert.assertNotNull(savedData);
return savedData;
}
}
</pre>
<p> Notes:
<ol>
<li>The method testSave() has the annotation @Transactional. Without this annotation, the test won't work. The savedData will be null. The @Transactional annotation has to appear somewhere. If the method saveSomeData of sampleDataDAO is annotated with @Transactional, then it is also fine.</li>
<li>The good thing about using @Transactional on the method of a Junit test class is that Spring will automatically rollback the transaction after the test is done. So the above test method testSave() won't actually create a record in the database after the test. Below is from Spring documentation.
<blockquote>
<p>In the TestContext framework, transactions are managed by the TransactionalTestExecutionListener. Note that <code>TransactionalTestExecutionListener</code> is <strong>configured by default</strong>, even if you do not explicitly declare <code>@TestExecutionListeners</code> on your test class. To enable support for transactions, however, you must provide a <code>PlatformTransactionManager</code> bean in the application context loaded by <code>@ContextConfiguration</code> semantics. In addition, <strong>you must declare <code>@Transactional</code> either at the class or method level for your tests</strong>.</p>
</blockquote>
</li>
</ol>
4. The configuration file app/src/test/resources/testApplicationContext.xml.
<pre>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
p:driverClassName="oracle.jdbc.driver.OracleDriver" p:url="jdbc:oracle:thin:@myurl"
p:username="myUsername" p:password="myPassword" p:initialSize="5" p:maxActive="10">
</bean>
<bean
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
id="entityManagerFactory">
<property name="dataSource" ref="dataSource" />
</bean>
<context:component-scan base-package="com.example.dao">
</context:component-scan>
<bean class="org.springframework.orm.jpa.JpaTransactionManager"
id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<context:spring-configured />
<context:annotation-config />
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
</pre>
5. The file app/src/main/java/META-INF/persistence.xml:
<pre>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
version="1.0">
<persistence-unit name="MY-UNIT" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>dataSource</non-jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
<!-- property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/ -->
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="none" />
<prop key="hibernate.connection.autocommit">false</prop>
<prop key="hibernate.connection.release_mode">after_transaction</prop>
</properties>
</persistence-unit>
</persistence>
</pre>
Note that the output log will show that this persistence unit is used.
<p>
Question. The test needs to use the Entity class SampleData.java. How is this class discovered in the test context?
<p>
Answer:
The document http://docs.jboss.org/hibernate/stable/entitymanager/reference/en/html/configuration.html#setup-configuration-bootstrapping
has good explaintion. It seems that by default, JPA will discover all the classes annotated with @Entity in the archive in the bootstrap
process.
<pre>
2.2.1. Packaging
The configuration for entity managers both inside an application server and in a standalone application reside in a persistence archive.
A persistence archive is a JAR file which must define a persistence.xml file that resides in the META-INF folder.
All properly annotated classes included in the archive (ie. having an @Entity annotation), all annotated packages and all
Hibernate hbm.xml files included in the archive will be added to the persistence unit configuration, so by default,
your persistence.xml will be quite minimalist:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="sample">
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
</persistence>
</pre>
<p> The actual command line of the test run is something like below:
<pre>
C:\tools\jre-7u79-windows-x64\jre1.7.0_79\bin\javaw.exe -ea
-classpath C:\Users\me\workspace\app\target\test-classes;
C:\Users\me\workspace\app\target\classes;
......
-testLoaderClass org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader
-loaderpluginname org.eclipse.jdt.junit4.runtime
-classNames com.sample.dao.SampleDataDAOTest
</pre>
Debugging of the program shows the following details.
1. The file testApplicationContext.xml has the bean entityManagerFactory of the class org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.
2. When this bean is created, its method afterPropertiesSet() calls HibernatePersistence.createContainerEntityManagerFactory(), which calls Ejb3Configuration.buildEntityManagerFactory(), and so on. Eventually it will create a map that contains all the classes with the annotation @Entity. The last few classes used in this chain of call are JPA or Hibernate classes which
are not related to Spring. So these classes must have the builtin ability to discover the @Entity classes by following the JPA standards.
<p>
Note that if you just use Hibernate without using the generic JPA,then you use the configuration file hibernate.cfg.xml. And you can
list the entity classes or packages in that file as the following example shows.
<pre>
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class>"com.mysql.mysql.Driver</property>
......
<mapping class="com.sample.SampleData"/>
</session-factory>
</hibernate-configuration>
</pre>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com1tag:blogger.com,1999:blog-4728595480135390879.post-87808512655924628432017-02-09T12:58:00.002-08:002017-02-09T14:15:57.941-08:00CDI, Tomcat and JPA
Contexts and Dependency Injection (CDI) for the Java EE platform is one of several Java EE 6 features that help to knit together the
web tier and the transactional tier of the Java EE platform. CDI is a set of services that, used together, make it easy for developers
to use enterprise beans along with JavaServer Faces technology in web applications. Designed for use with stateful objects, CDI also
has many broader uses, allowing developers a great deal of flexibility to integrate various kinds of components in a loosely coupled but typesafe way.
<p>
Tomcat (Tomcat 7 in this blog) is a web container. It is not a Java EE container. So it does not recognize @PersistenceContex, @EJB, etc.
<p>Say we have the persistence.xml file for JPA(Java Persistence API) in the META-INF folder.
<pre>
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="my_unit">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.username" value="username" />
<property name="hibernate.connection.password" value="pwd" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost/somepath" />
</properties>
</persistence-unit>
</persistence>
</pre>
<p> In a Java EE server, you can inject EntityManager as below:
<pre>
public class MyApp {
@PersistenceContext(unitName = "my-unit")
private EntityManager entityManager;
public void doSomething {
entityManager.persist(...);
}
}
</pre>
<p> But this won't work in Tomcat. In Tomcat, typically you need to do the following to get the EntityManager.
<pre>
EntityManagerFactory emf = Persistence.createEntityManagerFactory("my_unit");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// do some thing here
em.getTransaction().commit();
</pre>
<p>There is a way so that you can still use @PersistenceContext in Tomcat. The trick is to use the Spring class PersistenceAnnotationBeanPostProcessor. Below is the Spring documentation of this class.
<p>
<font color="blue">
This post-processor will inject sub-interfaces of EntityManagerFactory and EntityManager if the annotated fields or methods are declared as such. The actual type will be verified early, with the exception of a shared ("transactional") EntityManager reference, where type mismatches might be detected as late as on the first actual invocation.
<p>
Note: In the present implementation, PersistenceAnnotationBeanPostProcessor only supports @PersistenceUnit and @PersistenceContext with the "unitName" attribute, or no attribute at all (for the default unit). If those annotations are present with the "name" attribute at the class level, they will simply be ignored, since those only serve as deployment hint (as per the Java EE 5 specification).
<p>
This post-processor can either obtain EntityManagerFactory beans defined in the Spring application context (the default), or obtain EntityManagerFactory references from JNDI ("persistence unit references"). In the bean case, the persistence unit name will be matched against the actual deployed unit, with the bean name used as fallback unit name if no deployed name found. Typically, Spring's LocalContainerEntityManagerFactoryBean will be used for setting up such EntityManagerFactory beans. Alternatively, such beans may also be obtained from JNDI, e.g. using the jee:jndi-lookup XML configuration element (with the bean name matching the requested unit name). In both cases, the post-processor definition will look as simple as this:
<pre>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
</pre>
</font>
<p>So in your Spring context.xml file, you can put something like the following:
<pre>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml" />
<property name="persistenceUnitName" value="my_unit" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
</bean>
</property>
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="jpaDialect">
<bean class="......"/>
</property>
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</pre>
You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-551271273205160392016-09-14T11:26:00.005-07:002016-09-14T11:46:23.646-07:00A generic method to get column-value map from Java JDBC ResultSet
The following method creates a column_name to value map from the Java JDBC ResultSet. It makes use of the meta data and Spring JdbcUtils class.
<pre>
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.util.LinkedCaseInsensitiveMap;
public Map<String, Object> getColumnValueMapFromResultSet(ResultSet resultSet)
throws SQLException {
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
Map columnValueMap = new LinkedCaseInsensitiveMap(columnCount );
for (int i = 1; i <= columnCount; i++) {
String columnName = JdbcUtils.lookupColumnName(metaData, i);
Object value = JdbcUtils.getResultSetValue(resultSet, i);
columnValueMap.put(columnName, value);
}
return columnValueMap;
}
</pre>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-63274616932066531902016-09-12T09:11:00.001-07:002016-09-12T13:46:39.893-07:00Java Annotation PostConstruct and InheritanceIt is quite confusing when the Java inheritance is mingled with the PostConstruct annotation. Based on my tests, I found the following.
Suppose there are two classes.
<pre>
class A {
@PostConStruct
private void init(){
}
}
class B extends A {
}
</pre>
<br/><br/>
<h3>Case 1</h3>
For the above case, the execution order is <ol> <li>Constructor of A </li><li> Constructor of B </li><li> init method of A.</li> </ol> So the annotation @PostConstruct is inherited even when the annotated method is private in the parent class.
<br/><br/>
<h3>Case 2</h3>
Now if the class B has its own @PostConstruct method such as the following:
<pre>
@PostConstruct
private void my_init(){
}
</pre>
Then the init() method of A will not be called. Instead, my_init() is called. So it seems that in the class hierarchy, only one PostConstruct annotated method is called.
<br/><br/>
<h3>Case 3</h3>
Now if the class B does not have its own @PostConstruct. But it overrides the @PostConstruct annotated method in its parent class.
<pre>
class A {
@PostConStruct
public void init(){
System.out.println("A init()");
}
}
class B extends A {
@Override
public void init(){
System.out.println("B init()");
}
}
</pre>
In this case the @PostConstruct method init() is called. But the init() method used is from class B. So it will print "B init()". Note that if the init() method in class A is private, then "A init()" will be printed out. In this case, the init() method of B is not an override method. Actually you have to remove its @Override annotation. Otherwise it won't compile.
<br/><br/>
<h3>Conclusion</h3>
In summary, if there is a method with the annotation @PostConstruct in a class C or its parent classes, then the annotated method will be called after the constructor of C is called. In case that @PostConstruct appears in multiple classes in the class hierarchy, the one closest to the class C will be called. Furthermore, if that annotated method is overridden by its subclasses, then the overriding method will be called. Of course, I think it is bad to declare the @PostConstruct annotated method as public or protected. It should be private so it won't be overridden. You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com1tag:blogger.com,1999:blog-4728595480135390879.post-60263930255736470762016-07-27T08:55:00.002-07:002016-09-27T08:59:29.716-07:00A Simple Example to Use Spring BeanWrapper and PropertyAccessorFactoryThe Spring classes BeanWrapper and PropertyAccessorFactory can be used to make it easier to set the nested property values of an object. You can also use the BeanWrapper to get the values of the properties.
<p>First suppose there are the following two object classes. One class has a property that is the other class.
<pre>
public class Animal {
private String name;
private Body body;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Body getBody() {
return body;
}
public void setBody(Body body) {
this.body = body;
}
@Override
public String toString() {
return "Animal [name=" + name + ", body=" + body + "]";
}
}
public class Body {
private double weight;
private double height;
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
@Override
public String toString() {
return "Body [weight=" + weight + ", height=" + height + "]";
}
}
</pre>
<p>Below is the test class.
<pre>
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.PropertyValue;
public class PropertyAccessTest {
public static void main(String[] args) {
Animal animal = new Animal();
animal.setBody(new Body());
System.out.println("animal=" + animal);
// use the wrapper to set the property values
BeanWrapper wrapper = PropertyAccessorFactory
.forBeanPropertyAccess(animal);
PropertyValue p1 = new PropertyValue("body.weight", 123);
wrapper.setPropertyValue(p1);
PropertyValue p2 = new PropertyValue("name", "tiger");
wrapper.setPropertyValue(p2);
System.out.println("animal=" + animal);
// use the wrapper to get the property values
String[] properties = { "name", "body.weight" };
for (String p : properties) {
Object v = wrapper.getPropertyValue(p);
System.out.println(p + ": " + v.getClass() + " " + v);
}
}
}
</pre>
Running the test class generates the following result:
<pre>
animal=Animal [name=null, body=Body [weight=0.0, height=0.0]]
animal=Animal [name=tiger, body=Body [weight=123.0, height=0.0]]
name: class java.lang.String tiger
body.weight: class java.lang.Double 123.0
</pre>
You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-67413854071699227012016-01-29T13:08:00.003-08:002016-01-29T13:39:54.205-08:00Use WebLogic Maven Plugin
This is for WebLogic 12c. First follow the instructions in reference [1] to install the WebLogic Maven Plugin.
Then in the pom.xml file, you can use something like the following:
<pre>
<plugin>
<groupId>com.oracle.weblogic</groupId>
<artifactId>wls-maven-plugin</artifactId>
<version>12.1.2.0</version>
<configuration>
<middlewareHome>C:/oracle/wl12.1.2.0</middlewareHome>
<weblogicHome>C:/oracle/wl12.1.2.0/wlserver</weblogicHome>
<domainHome>C:/oracle/wl12.1.2.0/user_projects/domains/mydomain</domainHome>
<source>path/to/myApp.war</source>
<user>weblogic</user>
<password>welcome1</password>
<name>myApp</name>
</configuration>
</plugin>
</pre>
<p>The property values in the configuration may have their default values. You can configure the values as needed.
<p>The goal for deployment is <strong>com.oracle.weblogic:wls-maven-plugin:deploy</strong>.
<br>
<h3>Reference</h3>
<ol>
<li><a href="https://docs.oracle.com/middleware/1212/wls/WLPRG/maven.htm">https://docs.oracle.com/middleware/1212/wls/WLPRG/maven.htm</a></li>
</ol>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-47661424568612105622015-11-20T08:52:00.001-08:002015-11-20T13:53:13.286-08:00Configuraion for Spring transaction managementTrying to understand the declarative transaction management of Spring.
The following is from the document of Spring Release 3.2.1.
<pre>
<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService" />
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean
below) -->
<tx:advice id="<font color="red"><b>txAdvice</b></font>" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true" />
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution of
an operation defined by the FooService interface -->
<aop:config>
<aop:pointcut id="<font color="red"><b>fooServiceOperation</b></font>"
expression="execution(* x.y.service.FooService.*(..))" />
<font color="red"><aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation" /></font>
</aop:config>
<!-- don't forget the DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy- method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis" />
<property name="username" value="scott" />
<property name="password" value="tiger" />
</bean>
<!-- similarly, don't forget the PlatformTransactionManager -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- other <bean/> definitions here -->
</beans>
</pre>
The central part is the <b>advisor</b> in the <aop:config> section. It takes two arguments "advice-ref" and "pointcut-ref". Roughly speaking, an advisor advices "someone" to do "something". Here pointcut-ref is that "someone". And advice-ref is that "something". Basically what it says is to apply the advices given in "advice-ref" to those classes and/or methods defined in "pointcut-ref". So actually it is a generic thing of AOP. It is not just for transaction management.
<p>A common requirement is to make an entire service layer transactional.The best way to do this is simply
to change the pointcut expression to match any operation in your service layer. For example:
<pre>
<aop:config>
<aop:pointcut id="fooServiceMethods" <font color="red">expression="execution(* x.y.service.*.*(..))"</font> />
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods" />
</aop:config>
</pre>
In this example it is assumed that all your service interfaces are defined in the x.y.service package.
<p>Spring's declarative transaction support is enabled via AOP proxies. The AOP proxy uses a TransactionInterceptor. In the Spring Framework, an AOP proxy will be a JDK dynamic proxy or a CGLIB proxy. So underneath, the implementation uses interceptor. You can actually configure org.springframework.transaction.interceptor.<b>TransactionInterceptor</b> directly in XML for transaction management without using AOP.
<br><br>
<h3>AOP expression</h3>
<p> Below is from Spring document (see the Reference).
<p>Spring AOP users are likely to use the execution pointcut designator the most often. The format of
an execution expression is:
<pre>
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
throws-pattern?)
</pre>
All parts except the returning type pattern (ret-type-pattern in the snippet above), name pattern, and
parameters pattern are optional. The returning type pattern determines what the return type of the
method must be in order for a join point to be matched. Most frequently you will use * as the returning
type pattern, which matches any return type. A fully-qualified type name will match only when the method
returns the given type. The name pattern matches the method name. You can use the * wildcard as all
or part of a name pattern. The parameters pattern is slightly more complex: () matches a method that
takes no parameters, whereas (..) matches any number of parameters (zero or more). The pattern (*)
matches a method taking one parameter of any type, (*,String) matches a method taking two
parameters, the first can be of any type, the second must be a String. Consult the Language Semantics
section of the AspectJ Programming Guide for more information.
<p>Some examples of common pointcut expressions are given below.
<ul>
<li> the execution of any public method:<br>
execution(public * *(..))
</li><li> the execution of any method with a name beginning with "set":<br>
execution(* set*(..))
</li><li> the execution of any method defined by the AccountService interface:<br>
execution(* com.xyz.service.AccountService.*(..))
</li><li> the execution of any method defined in the service package:<br>
execution(* com.xyz.service.*.*(..))
</li><li> the execution of any method defined in the service package or a sub-package:<br>
execution(* com.xyz.service..*.*(..))
</li>
</ul>
<br><br>
<h3>Rollback Configuration</h3>
<p>Spring by default marks a transaction for rollback when a runtime exception is thrown. However you can customize the behavior to let the rollback happen for checked exceptions. The following is an example.
<pre>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="CheckedException1, CheckedException2" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>
</pre>
Note that you use comma-delimited expression in the value for rollback-for. There is another attribute no-rollback-for to mark exceptions that should not trigger rollback.
<br><br>
<H3>Using transaction annotation</h3>
<p>You can annotate the class with @Transactional. The following is from Spring doc.
<pre>
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService" />
<!-- enable the configuration of transactional behavior based on annotations -->
<font color="red"><tx:annotation-driven transaction-manager="txManager" /></font>
<!-- a PlatformTransactionManager is still required -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- other <bean/> definitions here -->
</beans>
</pre>
<p>Comparing this annotation method against the AOP XML method, you may ask what are the things corresponding to "someone" and "something" of the advisor? The annotation seems to solve the problems altogether in a completely different way. The annotation location indicates the "someone" part. And the annotation name itself indicates that transaction management needs to be carried out, which is "something".
<br><br>
<h3>References</h3>
<ol>
<li><a href="http://docs.spring.io/spring/docs/3.2.1.RELEASE/spring-framework-reference/pdf/spring-framework-reference.pdf">http://docs.spring.io/spring/docs/3.2.1.RELEASE/spring-framework-reference/pdf/spring-framework-reference.pdf</a></li>
</ol>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-5126344380895618642015-11-11T08:59:00.001-08:002016-09-08T13:33:04.029-07:00Notes on using the Spring annotationAssuming that Spring 3.2.1.Release or above is used in the following notes.
<ol>
<li>
You can use <context:annotation-config/>. It will look for annotations on the properties and methods of a bean, but not the bean class itself.
</li>
<li>
You use <context:component-scan base-package="org.example" /> to automatically detect classes and register bean definitions. The use of this implicitly enables the functionality of <context:annotation-config>. Also be aware of the following.
<ul>
<li>
When <context:component-scan/> is used, the java classes involved need to be annotated with @Component, @Service, etc. Otherwise they would not be recognized.
</li>
<li>The annotated classes are put into the Spring IoC container. You need to get a bean from this container to get the beans with all the injected properties. Outside this container, the instances of those classes are just plain instances. The annotations have no effect on them.</li>
<li>An interesting case is about the servlet. Below is an example.
<pre>
@Component
public class myServlet extends HttpServlet {
private @Autowired FooService fooService;
}
</pre>
Even though this class is annotated with @Component, the fooService will still be null when this servlet is called in the web application.
I think this is because the servlet instance used here is from the Web container. It is NOT from the Spring IoC container. The Spring IoC container may have a copy of this class with fooService injected. But it is not used. So how to still use annotation in a servlet? First of all, the @Component annotation can be removed on servlet. It is not needed since the servlets follow the standard Web container life cycle. The servlet that gets called is from the Web container instead of Spring IoC container. Inside the servlet, you can make annotation work by overriding the init() method as in the following:
<pre>
public void init(ServletConfig config) throws ServletException {
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,config.getServletContext());
}
</pre>
<p> Or you can just declare fooService as a plain non-annotated field:
<pre>
private FooService fooService;
</pre>
Then in the code inside the servlet, you can get this object from Spring IoC container as below:
<pre>
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
FooService service = (FooService)applicationContext.getBean("fooService");
</pre>
</li>
</ul>
</li>
<li>Annotation injection is performed before XML injection. So the latter configuration will override the former for properties wired through both approaches. You can inject some properties using annotations and other properties using XML. For example, you can have the following:
<pre>
public class Foo{
private @Autowired Bar bar;
private DataSource dataSource;
}
</pre>
You can use XML to inject dataSource.
</li>
<li>You can use @Configuration class so no XML configuration file is needed. But a cost is that you have to create new java classes with the @Configuration annotation.
</li>
<li>
To use a @Configuration class, you use <b>AnnotationConfigApplicationContext</b> to include the class. Say that AppConfig is annotated with @Configuration, you can use the following when the application is started.
<pre>
public static void main(String[] args){
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
</pre>
AnnotationConfigApplicationContext actually is more versatile than that. It can also be used to scan annotated classes as this: ctx.scan("com.sample"). For more information, see Spring documentation.
<p>In the Web application, you use <b>AnnotationConfigWebApplicationContext</b>. The web.xml file can be configured as below:
<pre>
<!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext instead of
the default XmlWebApplicationContext -->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<!-- Configuration location must consist of one or more comma- or space-delimited fully-qualified @Configuration classes.
Fully-qualified packages may also be specified for component-scanning -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.myapp.AppConfig</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</pre>
If you look at the above code snippet, you may wonder how the parameter name "contextClass" is used. It is declared but not referenced anywhere. I think the secret lies in the comment above that parameter. This parameter is implicitly used by the Spring ContextLoaderListener class. As that comment says, ContextLoaderListener uses XmlWebApplicationContext as the contextClass by default. Now we are not using the Spring XML bean definition files, so we change this to AnnotationConfigWebApplicationContext!
</li>
<li>There are debates about which way is better, annotation or XML? Both actually have their own pros and cons. Spring's @Configuration class support does not aim to be a 100% complete replacement for Spring XML. You can choose to use "XML-centric" way or java "Annotation-centric" way.
</li>
</ol>
<h3>References</h3>
<li>https://www.mkyong.com/spring3/spring-3-and-jsr-330-inject-and-named-example</li>
<li>http://stackoverflow.com/questions/18387993/spring-jsf-integration-how-to-inject-a-spring-component-service-in-jsf-managed</li>
<li>http://www.beyondjava.net/blog/integrate-jsf-2-spring-3-nicely</li>
You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-67122408457016176452015-10-23T08:38:00.002-07:002015-10-23T11:52:11.184-07:00Maven phases and goals<p>Maven has a life cycle in its build process. The key concept is "Phase". Do not be confused by another concept "goal". There are different but related. For example, "package" is a phase. It is not a goal.
<p>Maven can support a number of different life cycles. But the mostly used one is the default Maven life cycle.
<p>The default life cycle has the following phases (For a more detailed description, see reference[1]). And they are stepped through in order until it reaches at the phase that is specified in the Maven command.
<ol>
<li>process-resources</li>
<li>compile</li>
<li>process-classes</li>
<li>process-test-resources</li>
<li>test-compile</li>
<li>test</li>
<li>prepare-package</li>
<li>package</li>
<li>install</li>
</ol>
<p>Maven delegates the work to Maven plugins.
<p>Plugin goals can be attached to a life cycle phase. When Maven moves through the phases, it executes the goals attached to each phase. Each phase can have zero or more goals bound to it.
<p>A phase may mean different things to different projects. For example, for the package phase, a JAR project will produce a JAR file, while a web application will produce a WAR file.
<p>If no plugin is specified in the pom file, Maven seems to use its default plugins. See reference[2].
<p>You can run Maven by specifying a phase as in "mvn install". You can also run Maven by specifying the actual goals as in "mvn resources:resources compiler:compile".
<h4>References</h4>
[1] https://maven.apache.org/ref/3.3.3/maven-core/lifecycles.html
<br/>
[2] https://maven.apache.org/ref/3.3.3/maven-core/default-bindings.htmlYou Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-83850442076919322102015-10-21T08:47:00.003-07:002015-10-21T09:13:04.270-07:00Spring session scoped bean and HTTP session attribute
A simple JSF application app has a jsf file a.faces and a jsp file b.jsp. The jsp file has the following:
<pre>
<%
HttpSession sess = rquest.getSession();
FooBean foo = (FooBean)sess.getAttribute("fooBean");
foo.someMethod();
%>
</pre>
And the jsf file has the following:
<pre>
<ice:form onsubmit="#{fooBean.doAction}">
</pre>
Here the tag ice is used because it is using icefaces.
The name fooBean is for a Spring bean defined in applicationContext.xml as below:
<pre>
<bean id="fooBean" class="FooBean" scope="session">
<constructor-arg><ref bean="x" /> <constructor-arg>
<constructor-arg><ref bean="y" /> <constructor-arg>
</bean>
<bean id="x" class="X"/>
<bean id="y" class="Y" scope="session"/>
</pre>
And in the faces-config.xml, there is the following:
<pre>
<faces-config>
<application>
<variable-resolver>
org.springframework.web.jsf.DelegatingVariableResolver
</variable-resolver>
</application>
</faces-config>
</pre>
<p>The application is accessed by app/a.faces. And then you can access the jsp file app/b.jsp without any problem. But
if you access the application using app/b.jsp as the first step, it will give NullPointerException when calling foo.someMethod() because foo is null.
<p>
The tricky thing here is how the "fooBean" is put into the session as an attribute. The answer is in the Spring DelegatingVariableResolver class and DefaultListableBeanFactory class.
<P>
When user types app/a.faces, the resolver sees #{fooBean.doAction}. It checks the applicationContext.xml and finds the bean definition. So it creates
the bean x, y, and then fooBean. The bean x is a singleton. It is already created at the application startup. So the resolver only needs to get the cached
instance of x. But the bean y has a scope of session. So the resolver calls DefaultListableBeanFactory and creates a brand new instance of y. And it puts it as an attribute into the HTTP Session object. After both x and y are created, a new fooBean is created and put into the session as an attribute since its scope is also session.
So these session beans are created lazily when there are referenced.
<p>Now if you access app/b.jsp without firstly accessing app/a.faces, the Spring resolver is not invoked. The session object has no attribute named "fooBean". And you will get a null object for this attribute.
<p> The following is the method stacks showing the Spring bean is resolved and set into the HTTP session as an attribute.
<pre>
owns: InterceptingServletSession (id=15648)
MyAttributeListener.attributeAdded(HttpSessionBindingEvent) line: 13
EventsManager.notifySessionAttributeChange(HttpSession, String, Object, Object) line: 302
MemorySessionContext(SessionContext).notifySessionAttributeChange(HttpSession, String, Object, Object) line: 1479
MemorySessionData(SessionData).setAttribute(String, Object, boolean) line: 1121
MemorySessionData(SessionData).setAttribute(String, Object) line: 959
InterceptingServletSession(ProxyHttpSession).setAttribute(String, Object) line: 107
ServletSessionAttributeMap.setAttribute(String, Object) line: 20
ServletSessionAttributeMap(AbstractAttributeMap).put(Object, Object) line: 157
FacesRequestAttributes.setAttribute(String, Object, int) line: 108
SessionScope(AbstractRequestAttributesScope).get(String, ObjectFactory) line: 44
SessionScope.get(String, ObjectFactory) line: 90
DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class, Object[], boolean) line: 298
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String, Class, Object[]) line: 185
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 164
XmlWebApplicationContext(AbstractApplicationContext).getBean(String) line: 881
DelegatingVariableResolver.resolveSpringBean(FacesContext, String) line: 136
DelegatingVariableResolver.resolveVariable(FacesContext, String) line: 109
NamedValue.evaluate(ExpressionInfo) line: 145
ComplexValue.evaluate(ExpressionInfo) line: 166
ExpressionEvaluatorImpl.evaluate(ExpressionInfo) line: 263
ValueBindingImpl.getValue(FacesContext, String) line: 160
ValueBindingImpl.getValue(FacesContext) line: 143
HtmlForm(HtmlForm).getOnsubmit() line: 501
</pre>
You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-78598228583587924372015-09-28T11:32:00.002-07:002015-09-28T11:33:01.768-07:00Lunar EclipseThere was the super moon lunar eclipse last night. But it was raining and very cloudy in Pittsburgh. So we could only see the dark sky. You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-7840363631415845682015-01-30T13:47:00.001-08:002016-09-08T13:33:24.738-07:00Tomcat7 Spring Annotation
The following is the list of the related files and the related contents.
<h4>pom.xml</h4>
<pre>
<properties>
<org.springframework.version>3.1.4.RELEASE</org.springframework.version>
</properties>
<!-- SpringFramework -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!-- JSR-330 and CDI -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>1.2</version>
</dependency>
</pre>
<p>Note that the dependency cdi-api is included to test the use of the annotations from CDI. The tests will show that it does not work. The annotation @javax.enterprise.context.SessionScoped is from CDI. But the annotations @Named and @Inject are from JSR330.
<p>Note that another name of CDI is JSR299. Though the number 299 is less than 330, JSR299 is actually on top of JSR330.
<h4>web.xml</h4>
<pre>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-security.xml,
/WEB-INF/application-context.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</pre>
<p>Note that two xml files are included. The file spring-security.xml has mostly the security URL
configurations. It also has some bean declarations. The file application-context.xml will scan the
beans with annotations. It also has some bean declarations.
<h4>faces-config.xml</h4>
<pre>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd"
version="2.1">
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
</pre>
<h4>spring-security.xml</h4>
<pre>
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref='omsUserDetailsService' />
</authentication-manager>
<beans:bean id="omsUserDetailsService"
class="com.lakexy.oms.security.OmsUserDetailService">
<beans:property name="userDao" ref="userDao" />
</beans:bean>
<beans:bean id="userDao" class="com.lakexy.oms.dao.security.UserDaoImpl">
<beans:property name="dataSource" ref="omsDataSource" />
</beans:bean>
<http auto-config="true">
<intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/index.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<logout logout-success-url="/index.jsp" />
</http>
</beans:beans>
</pre>
<h4>application-context.xml</h4>
<pre>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="com.lakexy" />
<bean id="omsDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/inventory" />
</bean>
<!-- *********************************************************** -->
<!-- T R A N S A C T I O N S E T U P -->
<!-- *********************************************************** -->
<!--
Transaction Interceptor set up to do PROPAGATION_REQUIRED on all
methods
-->
<!--
<bean id="omsTransactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
</bean>
-->
<bean id="omsTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="omsDataSource" />
</property>
</bean>
<bean id="matchAllWithPropReq"
class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource">
<property name="transactionAttribute">
<value>PROPAGATION_REQUIRED,-OmsCheckedException</value>
</property>
</bean>
<bean id="matchAllTxInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="omsTransactionManager" />
</property>
<property name="transactionAttributeSource">
<ref bean="matchAllWithPropReq" />
</property>
</bean>
<!--
One BeanNameAutoProxyCreator handles all beans where we want all
methods to use PROPAGATION_REQUIRED
-->
<bean id="autoProxyCreator"
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<idref bean="matchAllTxInterceptor" />
</list>
</property>
<property name="beanNames">
<list>
<idref bean="customerBo" />
<idref bean="locationBo" />
<idref bean="orderBo" />
<idref bean="omsUserDetailsService" />
<idref bean="shipmentBo" />
<idref bean="invoiceBo" />
</list>
</property>
</bean>
</beans>
</pre>
<p>Notes:
<ol>
<li>application-context.xml mixed the component-scan and the explicit bean declaration for
those beans that are used in transaction management.
</li><li> The base-package is "com.lakexy" in component-scan. But that does not mean that any
classes in the package com.lakexy will be registered as a bean in the spring container. A class
will be registered in the spring container only when it has the annotation such as @Named.
</li></ol>
<p>Lastly, the actual java classes with annotations.
<p>Example:
<pre>
import javax.inject.Inject;
import javax.inject.Named;
import org.springframework.context.annotation.Scope;
@Named
// @SessionScoped
@Scope("session")
// @RequestScoped
public class CustomerBean {
private String customerName;
private List<Customer> customers;
private CustomerTableData customersTableData;
@Inject
private ICustomer customerBo;
...
}
</pre>
<p>Notes
<ol>
<li>@Named is from standard java annotation. But nevertheless, spring recognize this annotation.
</li><li> By default, the bean scope in Spring is singleton. So if @Named is the only annotation, this CustomerBean will be a singleton, which essentially make it an Application scoped bean.
</li><li> I tested the use of different scope annotations. Confusingly, there are several variations of session scope.The @SessionScoped can mean one of the followings:
<pre>
javax.faces.bean.SessionScoped
javax.enterprise.context.SessionScoped;
</pre>
But nevertheless, the tests show that this annotation does not have effects. The same CustomerBean instance is used even if you use the application in different browsers. So it acts like a singleton just as when only @Named is used. But the spring annotaion @Scope("session") as in the above code works. The tests do show that it acts as a session bean. So it looks like that Spring supports JSR330 but not CDI. The annotations @Named and @Inject are from JSR330. The annotation javax.enterprise.context.SessionScoped is from CDI. I did some search on internet and did find articles saying that Spring3 supports JSR330 but not JSR299. And there is a way to use JSR299 in Spring3.
</li><li> When @Inject is used, no need to write the getter and setter for the annotated field.
</li>
<li>Injection is type-based. So in the example above, it will search for a bean of the type ICustomer. The name of the bean does not necessarily have to be "customerBo".</li>
</ol>
<h3>References</h3>
<ol>
<li>http://www.mkyong.com/jsf2/jsf-2-0-spring-integration-example</li>
<li>https://www.mkyong.com/spring3/spring-3-and-jsr-330-inject-and-named-example</li>
<li>http://stackoverflow.com/questions/18387993/spring-jsf-integration-how-to-inject-a-spring-component-service-in-jsf-managed</li>
<li>http://www.beyondjava.net/blog/integrate-jsf-2-spring-3-nicely</li>
</ol>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-56972667540223976942015-01-15T09:01:00.001-08:002015-01-15T12:34:50.159-08:00Spring Security Filter Name and Bean Name<h3>Name Matching</h3>
<p>I read some code of an application that uses Spring security library. It has a class to implement the standard javax.servlet.Filter class. And this filter is declared as a bean in the application context xml files for Spring. The bean name is just assumed to be "xyz" here. But this filter is NOT declared in the web.xml file. Normally, in order for a filter class to be invoked, it has to be declared in the web.xml file. But in this application, the web.xml file only declares a filter with the name "xyz" for org.springframework.web.filter.DelegatingFilterProxy. The surprising thing is that the bean "xyz" in the application context IS invoked. After doing some searching, I found the following document that explains the puzzle.
<div style="color: teal;">
<p> When using servlet filters, you obviously need to declare them in your web.xml, or they will be ignored by the servlet container. In Spring Security, the filter classes are also Spring beans defined in the application context and thus able to take advantage of Spring's rich dependency-injection facilities and lifecycle interfaces. Spring's DelegatingFilterProxy provides the link between web.xml and the application context.
<p>When using DelegatingFilterProxy, you will see something like this in the web.xml file:
<pre>
<filter>
<filter-name>myFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</pre>
<p>Notice that the filter is actually a DelegatingFilterProxy, and not the class that will actually implement the logic of the filter. What DelegatingFilterProxy does is delegate the Filter's methods through to a bean which is obtained from the Spring application context. This enables the bean to benefit from the Spring web application context lifecycle support and configuration flexibility. The bean must implement javax.servlet.Filter and it must have the same name as that in the filter-name element. Read the Javadoc for DelegatingFilterProxy for more information
</div>
<h3>References</h3>
<ol>
<li>http://docs.spring.io/spring-security/site/docs/3.0.x/reference/security-filter-chain.html</li>
</ol>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-16140918247660369402015-01-09T14:17:00.000-08:002015-01-13T11:24:25.015-08:00Use JMX to change the log level dynamically <h3> Use JMX to Change the Log level dynamically </h3>
<p>In this note, we will create a program that can change the log level dynamically on the local or remote server. We will use Spring JMX exporter.
Log4j is used. The applicaiton server is Weblogic 10.3.2. On the remote side, the application is deployed to a cluster of two weblogic managed servers weblogic01 and weblogic02. Note that on later versions of Weblogic such as version 11, the MBean server configurations may be different.
<p>There are several files needed for this.
<h3>What are needed on the application side</h3>
1. In the application, create the following classes:
<p>
(1) IAppLogUtils.java
<pre>
package com.yourcompany.app.server.utils;
public interface IAppLogUtils {
public void updateAllLogLevels(String s);
}
</pre>
(2) AppLogUtils.java
<pre>
package com.yourcompany.app.server.utils;
import java.util.Enumeration;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
public class AppLogUtils implements IAppLogUtils {
public void updateAllLogLevels(String s) {
Enumeration enumeration = LogManager.getCurrentLoggers();
do {
if (!enumeration.hasMoreElements())
break;
Logger logger = (Logger) enumeration.nextElement();
logger.setLevel(Level.toLevel(s));
} while (true);
}
}
</pre>
(3) In the application context xml file, add the following to use the Spring JMX exporter.
<pre>
<bean id="jmxServerRuntime" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jmx/runtime"/>
</bean>
<bean id="appLogUtils" class="com.yourcompany.app.server.utils.AppLogUtils" />
</bean>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="APP:name=appLogUtils" value-ref="appLogUtils" />
</map>
</property>
<property name="registrationBehaviorName" value="REGISTRATION_IGNORE_EXISTING"/>
<property name="server" ref="jmxServerRuntime"/>
</bean>
</pre>
Two important notes here.
<p>
(a) A JVM can have multiple MBean servers. We will register our JMX bean on the weblogic runtime mbean server.
Later on when we create the JMX client program to change the log level, we will connect to this mbean server.
A test shows that if we do not specify the server property as in the above, Spring MBeanExporter will export the MBean
to the JVM platform mbean server. On local machine, the jconsole program will detect this JVM platform mbean server. It
does not need to have the username/password to access it.
<p>
(b) The value for the property "registrationBehaviorName" of the MBeanExporter is set. The default value is REGISTRATION_FAIL_ON_EXISTING,
which will give error if Spring registers the Mbean with the same name twice. The error message is like this:
"javax.management.InstanceAlreadyExistsException".
<h3>What are needed on the client side to invoke the JMX bean</h3>
<p>It is relatively easy if the weblogic server is running locally. If the servers are remote, the program is still similar but a little bit more complex. The following program has code for both cases.
<p>In the program, we assume that the two managed weblogic servers have the following host name and port number:
<pre>
host1 14110
host2 14110
</pre>
<p>Note that the port numbers are the same because they are in the same cluster of weblogic managed servers. The weblogic admin server has a different port number. You can also connect to the admin server to get its JMX information. You need to use the username/password to connect to the admin server. Interestingly, you need to use the same username/password to connect to the managed weblogic servers for the JMX connection.
<pre>
import java.io.IOException;
import java.util.Hashtable;
import java.util.Set;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import com.bea.core.repackaged.springframework.util.Assert;
public class WeblogicJMXClient {
// this method changes the log level to "DEBUG" on the locally running weblogic server.
public void testLocT3() throws Exception {
JMXConnector jmxCon = null;
try {
JMXServiceURL serviceUrl = new JMXServiceURL("t3", "localhost",
7001, "/jndi/weblogic.management.mbeanservers.runtime");
System.out.println("Connecting to: " + serviceUrl);
Hashtable env = new Hashtable();
env.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES,
"weblogic.management.remote");
env.put(javax.naming.Context.SECURITY_PRINCIPAL, "username");
env.put(javax.naming.Context.SECURITY_CREDENTIALS, "password");
jmxCon = JMXConnectorFactory.newJMXConnector(serviceUrl, env);
jmxCon.connect();
MBeanServerConnection con = jmxCon.getMBeanServerConnection();
Set<ObjectName> mbeans = con.queryNames(new ObjectName(
"APP:name=appLogUtils"), null);
ObjectName logService = (ObjectName) mbeans.iterator().next();
Object result = con.invoke(logService, "updateAllLogLevels",
new Object[] { "DEBUG" }, new String[] { String.class
.getName() });
} finally {
if (jmxCon != null)
jmxCon.close();
}
}
/**
* This method changes the log level to the specified value "INFO" or "DEBUG" on the remote
* weblogic server cluster
*/
public void changDevLog(String logLevel) {
Assert.isTrue("DEBUG".equals(logLevel) || "INFO".equals("logLevel"));
JMXConnector jmxCon = null;
// change on weblogic01
try {
jmxCon = getJMXConnector("t3", "host1",
14110, "username", "password");
changeLogLevel(jmxCon, logLevel);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (jmxCon != null) {
try {
jmxCon.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
// change on weblogic02
try {
jmxCon = getJMXConnector("t3", "host2",
14110, "username", "password");
changeLogLevel(jmxCon, logLevel);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (jmxCon != null) {
try {
jmxCon.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
private JMXConnector getJMXConnector(String protocol, String host,
int port, String username, String password) throws IOException {
JMXConnector jmxCon = null;
JMXServiceURL serviceUrl = new JMXServiceURL(protocol, host, port,
"/jndi/weblogic.management.mbeanservers.runtime");
System.out.println("Connecting to: " + serviceUrl);
Hashtable env = new Hashtable();
env.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES,
"weblogic.management.remote");
env.put(javax.naming.Context.SECURITY_PRINCIPAL, username);
env.put(javax.naming.Context.SECURITY_CREDENTIALS, password);
jmxCon = JMXConnectorFactory.newJMXConnector(serviceUrl, env);
return jmxCon;
}
private void changeLogLevel(JMXConnector jmxCon, String level)
throws Exception {
jmxCon.connect();
MBeanServerConnection con = jmxCon.getMBeanServerConnection();
Set<ObjectName> mbeans = con.queryNames(new ObjectName(
"APP:name=appLogUtils"), null);
ObjectName logService = (ObjectName) mbeans.iterator().next();
Object result = con
.invoke(logService, "updateAllLogLevels",
new Object[] { level }, new String[] { String.class
.getName() });
}
public static void main(String[] args) throws Exception {
WeblogicJMXClient c = new WeblogicJMXClient();
//c.testLocT3();
c.changDevLog("DEBUG");
}
}
</pre>
<h3>MBean Server on Weblogic</h3>
<p>The following is about Weblogic 10.
<p>At the core of any JMX agent is the MBean server, which acts as a container for MBeans.
<p>
In Weblogic, the JVM for an Administration Server maintains three MBean servers provided by BEA and optionally maintains the platform MBean server, which is provided by the JDK itself. The JVM for a Managed Server maintains only one BEA MBean server and the optional platform MBean server.
<p> The three MBean servers provided by BEA are:
<ol>
<li>Domain Runtime MBean Server. Only the Administration Server hosts an instance of this MBean server. Its INDI name is "weblogic.management.mbeanservers.domainruntime".</li>
<li>Runtime MBean Server. Each server in the domain hosts an instance of this MBean server. Its JNDI name is "weblogic.management.mbeanservers.runtime".</li>
<li>Edit MBean Server. Only the Administration Server hosts an instance of this MBean server. Its JNDI name is "weblogic.management.mbeanservers.edit".</li>
</ol>
<p>The JVM's platform MBean server is provided by the JDK that contain monitoring information for the JVM itself. You can register custom MBeans in this MBean server, but BEA recommends that you register them in its Runtime MBean Server.
<p>You can also configure the WebLogic Server Runtime MBean Server to be the platform MBean server, in which case the platform MBean server provides access to JVM MBeans, Runtime MBeans, and active configuration MBeans that are on a single server instance.
<h3>Register an MBean on Weblogic</h3>
<p>Local clients can access a WebLogic Server instance’s Runtime MBean Server through the JNDI tree instead of constructing a JMXServiceURL object. Only the Runtime MBean Server registers itself in the JNDI tree.
<p>If the classes for the JMX client are located at the top level of an enterprise application (that is, if they are deployed from the application’s APP-INF directory), then the JNDI name for the Runtime MBean Server is:
java:comp/jmx/runtime
<p> If the classes for the JMX client are located in a Java EE module, such as an EJB or Web application, then the JNDI name for the Runtime MBeanServer is:
java:comp/env/jmx/runtime
<p>In our case for the bean AppLogUtils, we use the JNDI name "java:comp/env/jmx/runtime". This bean AppLogUtils is a spring bean and is used in a web module deployed on the Weblogic server. So according to the above stated, this bean is registered at the Weblogic runtime MBean server. And in our subsequent code we do see that the JMX client connects to the runtime MBean server to manage this bean because it uses the JNDI name "/jndi/weblogic.management.mbeanservers.runtime".
<p>One thing that may be misleading is the JNDI name "java:comp/env/jmx/runtime". This name looks like a java standard since it does not contain any thing about Weblogic. But it may just be a proprietary name used by Weblogic for the java platform MBean server. As stated before, all the three Weblogic MBean servers ( Domain Runtime MBean server, Runtime MBean server, Edit MBean server ) have their JNDI name and the client can connect to them programmatically using these JNDI names remotely. But there does not seem to be a JNDI name for the platform MBean server for the remote client to connect to. The JNDI name "java:comp/env/jmx/runtime" is only used locally. The following is from Oracle document about how to access the JVM platform MBean server remotely:<br/>
"Remote access to the platform MBean server can be secured only by standard JDK 1.5 security features (see http://java.sun.com/j2se/1.5.0/docs/guide/management/agent.html#remote). If you have configured the WebLogic Server Runtime MBean Server to be the platform MBean server, enabling remote access to the platform MBean server creates an access path to WebLogic Server MBeans that is not secured through the WebLogic Server security framework."
<h3>JVM Platform MBean Server</h3>
<p>Someone on the internet says the following: "Note that the Platform MBean Server is always present and is created by the JVM during startup." But I am not sure about the meaning of "always present" here. It seems to me that by default, JVM will not create the MBean server during the startup. I did a simple test by using JDK1.7. I created a simple class Temp1.java that just calls the Thread.sleep method so it will not stop immediately after being executed. Then if I simply run the class using the command "java Temp1", jconsole will fail to connect to it. Only if I use the command "java -Dcom.sun.management.jmxremote Temp1", will the jconsole be able to connect to it.
After doing more searching, I found the following from java7 doc:
<p><b>In the Java SE 6 platform, it is no longer necessary to set this system property. Any application that is started on the Java SE 6 platform will support the Attach API, and so will automatically be made available for local monitoring and management when needed.</b>
<p>In the above, the "system property" refers to "com.sun.management.jmxremote". For remote access, the document is the following:
<p><b>To enable monitoring and management from remote systems, you must set the following system property when you start the Java VM.
<br>
com.sun.management.jmxremote.port=portNum
<p>
In the property above, portNum is the port number through which you want to enable JMX RMI connections. Be sure to specify an unused port number. In addition to publishing an RMI connector for local access, setting this property publishes an additional RMI connector in a private read-only registry at the specified port using a well known name, "jmxrmi".
</b>
<p> The Attach API mentioned above is worth studying. Ref[4] has some example code about the Attach API. The main ideas are the following.
<ol>
<li>The Attach API is provided in the tools.jar file from JDK. The main class is VirtualMachine.</li>
<li>The VirtualMachine class represents a specific Java virtual machine(JVM) instance. You connect to a JVM by providing the VirtualMachine class with the process id, and then you load a management agent to do your customized behavior.</li>
<li>After you load the agent with loadAgent, you should call the detach method.</li>
<li>A JMX agent exists in the managment-agent.jar file that comes with the JDK found in the same directory as tools.jar. The sample code is the following:
<pre>
VirtualMachine vm = VirtualMachine.attach(args[0]);
String connectorAddr = vm.getAgentProperties().getProperty(
"com.sun.management.jmxremote.localConnectorAddress");
if (connectorAddr == null) {
String agent = vm.getSystemProperties().getProperty(
"java.home")+File.separator+"lib"+File.separator+
"management-agent.jar";
vm.loadAgent(agent);
connectorAddr = vm.getAgentProperties().getProperty(
"com.sun.management.jmxremote.localConnectorAddress");
}
JMXServiceURL serviceURL = new JMXServiceURL(connectorAddr);
JMXConnector connector = JMXConnectorFactory.connect(serviceURL);
MBeanServerConnection mbsc = connector.getMBeanServerConnection();
ObjectName objName = new ObjectName(
ManagementFactory.THREAD_MXBEAN_NAME);
Set<ObjectName> mbeans = mbsc.queryNames(objName, null);
for (ObjectName name: mbeans) {
ThreadMXBean threadBean;
threadBean = ManagementFactory.newPlatformMXBeanProxy(
mbsc, name.toString(), ThreadMXBean.class);
long threadIds[] = threadBean.getAllThreadIds();
for (long threadId: threadIds) {
ThreadInfo threadInfo = threadBean.getThreadInfo(threadId);
System.out.println (threadInfo.getThreadName() + " / " +
threadInfo.getThreadState());
}
</pre>
In the above, args[0] should be the process ID of the target JVM.</li>
</ol>
<p>It looks like that lots of operating system level functions are needed in the back of the scene for the code to work. You run the code containing the VirtualMachine class in your JVM to connect to the target JVM whose process ID is known to you. At the level of operating system, this is the communication between two processes. There is no port number involved in the above code. There is no TCP/IP in the above code.
<p>One interesting consequence of the Attach API is this. Say that you run a program such as jconsole to try to connect to a JVM to get the JMX MBean server. Suppose that the connection works. Can you conclude from this that the MBean server has been started when the JVM is started? The answer is "NO". This is because that the MBean sever may have been started by your client program by using the Attach API!
<br/>
<h3>References</h3>
<ol>
<li>http://java.dzone.com/articles/taming-jmx-weblogic-server</li>
<li>http://docs.spring.io/spring/docs/2.0.x/reference/jmx.html</li>
<li>http://docs.oracle.com/cd/E11035_01/wls100/jmx/understandWLS.html#MBeanServers</li>
<li>http://www.javaworld.com/article/2071330/the-attach-api.html</li>
</ol>
You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-67486228822534452152015-01-06T18:05:00.002-08:002015-01-06T18:05:55.732-08:00Order Management SystemOrder Management System (OMS) is a new software application that has been developed to help companies to manage orders, customers, product inventory, order shipment, etc. It is an excellent product that can greatly improve business. For details, see the following link: <a href="http://www.lakexy.com">http:www.lakexy.com</a>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-46631790983433059322015-01-06T07:35:00.001-08:002015-01-09T08:12:23.601-08:00Java Annotation Retention<h3>Annotation Retention Policy</h3>
<p>Java annotation works by adding some code to the java file or the compiled java class file. The notation used is the @Retention tag.
<p>The following is the overall picture:
<br/>
Source File( A.java) --> Compiled bytecode(A.class) --> Runtime memory after loaded by classloader
<br/>
<br/>
There are 3 Retention policies. In the following, we use a '*' to indicate that some annotation code has been inserted.
<br/>
<br/>
@Retention(RetentionPolicy.SOURCE)
<br/>
A*.java --> A.class --> A-runtime
<br/>
<br/>
@Retention(RetentionPolicy.CLASS)
<br/>
A*.java --> A*.class --> A-runtime
<br/>
This is the default policy.
<br/>
<br/>
@Retention(RetentionPolicy.RUNTIME)
<br/>
A*.java --> A*.class --> A*-runtime.
<br/>
This is generally used for customer annotations.
<h3>Java Annotation Implementation</h3>
<p>Java annotation is not part of the java standard edition. You can see that the package is javax.annotation. So a java server container such as Weblogic server and Tomcat serfver is generally needed for the annotation to work. The server container has to implement the annotation artifacts so it knows how to parse, compile and run the java classes with the annotations. Of course, you can also put the needed library for annotation on the classpath to run the java programs with annotations successfully without using a sever container. You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0tag:blogger.com,1999:blog-4728595480135390879.post-15342772184351402752014-10-29T20:32:00.000-07:002014-10-30T19:08:35.031-07:00To Take Foreign Language Input in JSF ApplicaitonThe environment I use is the following:
Tomcat 6, MySQL 5.5, JSF2 Facelets.
The application needs to be able to allow the user to type in foreign characters such as Chinese characters and save them into the MySql database. To accomplish this, the following is the list of changes that will make it work. Some of them are actually not needed.
<ol>
<li> The column that can take the input is "comment" in the table xyz. Its type is TEXT. Do the following to modify the character set of the column:
<pre>
alter table xyz modify comment text character set utf8;
</pre>
</li>
<li>
I use the file META-INF/context.xml for the datasource:
<pre>
<Context>
<Manager pathname="" />
<Resource name="jdbc/inventory" auth="Container" type="javax.sql.DataSource"
url="jdbc:mysql://localhost:3306/yourDBName"
...
/>
</Context>
</pre>
Change the url to:
<pre>
url="jdbc:mysql://localhost:3306/yourDBName?useUnicode=true&amp;characterEncoding=UTF-8"
</pre>
</li>
<li>
In the file C:\apache-tomcat-6.0.35\conf\server.xml, add a line URIEncoding="UTF-8" in the Connector configuration:
<pre>
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8" />
</pre>
But this may not be needed.
</li>
<li>
All my xhtml pages use a template. In the template file, there is the line
<pre>
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
...
</h:head>
</pre>
Change the charset in the line to charset=utf-8. But this is actually not needed.
</li>
<li>
I use log4j.xml for the log file. Add a line for Encoding as below. This way the log file will not just show ? for the Chinese characters.
<pre>
<appender name="rollingFileSize" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="${logfile}" />
<param name="MaxFileSize" value="20MB" />
<param name="MaxBackupIndex" value="10" />
<param name="Encoding" value="UTF-8" />
...
</appender>
</pre>
</li>
<li>
In the faces-config.xml file, there is usually the following:
<pre>
<application>
...
<locale-config>
<default-locale>en</default-locale>
<supported-locale>es</supported-locale>
</locale-config>
</application>
</pre>
You can add the following to support Chinese.
<pre>
<supported-locale>zh_CN</supported-locale>
</pre>
But it is not needed for the purpose to accept Chinese character input.
</li>
</ol>You Xu ( Zach )http://www.blogger.com/profile/05947928840376138726noreply@blogger.com0