Tuesday, May 27, 2014

Create ConcurrentHashmap with a Bean as Key (in Spring Project)

Step 1: Create the Bean Key:

In our case, the Key is a Bean. Normally it should be an immutable, but as I am working with Spring and it is difficult to use immutable beans, I have created a normal bean.


Step 2: Override Equal and HashCode:


Using eclipse, you can generate source code for the hashcode and equal. So you need just to right-clic -> Source -> Generate Hash


So the Bean which will be the Key (here it is called User.java) is :

package main.org.qmic.nr.beans;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("singleton")
public class User {

    String enterprise;
    String name;
    
    
    public User() {
        super();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((enterprise == null) ? 0 : enterprise.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }


    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        User other = (User) obj;
        if (enterprise == null) {
            if (other.enterprise != null)
                return false;
        } else if (!enterprise.equals(other.enterprise))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
    
    public User(String enterprise, String name) {
        super();
        this.enterprise = enterprise;
        this.name = name;
    }

    public String getEnterprise() {
        return enterprise;
    }


    public void setEnterprise(String enterprise) {
        this.enterprise = enterprise;
    }

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



Step3: Declare the ConcurrentHashMap


private final Map<Device, LatestInfo> latestInfoCache = new ConcurrentHashMap<Device, LatestInfo>();


That's all :)

Wednesday, May 21, 2014

Resolved: Change the Project facet Dynamic Web Module and fix web-app_2_5 xsd

I was using an existing web application. So when I copied the code, I get this error:

Description Resource Path Location Type
Cannot change version of project facet Dynamic Web Module to 2.4. webappVaadin line 1 Maven Java EE Configuration Problem

In fact, in web.xml I have seen that the version of the servlet was 2.4.


1
2
3
<web-app id="WebApp_ID" version="2.4"
 xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">


But in the Project facets, it was Servlet 2.5.



By only changing the version from 2.4 to 2.5, I still get error:

Description Resource Path Location Type
Referenced file contains errors (http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd). 

At the end I have find the solution: For Web 2.5, the namespace has changes from j2ee to javaee, So the declaration should be :


1
2
3
4
5
<web-app 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-app_2_5.xsd"
       version="2.5">

Using Servlet 3.0, we need to declare it like this :

1
2
3
4
5
<web-app 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-app_3_0.xsd"
       version="3.0">



Thursday, May 15, 2014

Spring Tutorial : Develop your Software based on Spring 4, JPA 2.0 and JBoss EAP 6.2 / JBoss AS 7.3

I will speak today about how can we integrate Spring 4 with JBoss AS 7 (JBoss EAP 6). 

Step 1 : Persistence.xml

The persistence.xml creation will depend on weather the Entity Manager will be managed by the container (JBoss so JPA 2.0) or by Spring (Where we will use JPA 2.1).

JBoss manage the EntityManager: Use of JPA 2.0 with JTA transaction Type

In this case, the Transaction Type must be JTA. JBoss EAP 6.2 (based on JBoss 7.3) comes with JPA2.0 :


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 
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">
    <persistence-unit name="module_enterprise" transaction-type="JTA">
  <jta-data-source>java:/myDS</jta-data-source>   
   <properties> 
   <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
   <property name="hibernate.show_sql" value="true" />
   <property name="hibernate.hbm2ddl.auto" value="validate"/>
  </properties>
 </persistence-unit>  
</persistence>

- Spring manage the EntityManager: Use of JPA2.0 with RESOURCE_LACAL transaction type


As we need to let Spring manage the persisitence unit and e want to use RESOURCE_LOCAL transaction type, we need to exclude JPA and Hibernate coming from JBoss.
So the steps are:
- Create a jpa-persistence.xml. In the case where you don't want that JBoss inject its own libraries, it is better to change the name of the persistence.xml ( JBoss will load JPA implicitely if it detects perssistence.xml file).

- Use RESOURCE_LOCAL and not JTA, if you don't really need to access multiple data sources

- Use "org.hibernate.jpa.HibernatePersistenceProvider" as a provider

- Add a property in the persistence.xml
<property name="jboss.as.jpa.managed" value="false"/>

In fact, according to JBoss Reference, jboss.as.jpa.managed can be set to false to disable container managed JPA access to the persistence unit.  The default is true, which enables container managed JPA access to the persistence unit.  This is typically set to false for Seam 2.x + Spring applications. 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 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">
<!-- <persistence version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence 
    http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> -->
    <persistence-unit name="module_enterprise" transaction-type="RESOURCE_LOCAL">
  <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
  <non-jta-data-source>java:/myDataSource</non-jta-data-source>   
   <properties> 
   <property name="jboss.as.jpa.managed" value="false" />
   <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
   <property name="hibernate.show_sql" value="true" />
   <property name="hibernate.hbm2ddl.auto" value="validate"/>
  </properties>
 </persistence-unit>  
</persistence>

Step 2 : Spring Configuration

Same think here: 

JBoss manage the EntityManager or EntityManagerFactory and Spring have just the jndi nameTransaction Type here must be JTA. The ApplicationContext looks like this:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?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:context="http://www.springframework.org/schema/context"
 xmlns:jee="http://www.springframework.org/schema/jee"
 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/context http://www.springframework.org/schema/context/spring-context.xsd
 http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

 <!-- post-processors for all standard config annotations -->
 <tx:annotation-driven />
 <context:annotation-config />
 <context:component-scan base-package="org.test"/>
 
 <jee:jndi-lookup id="myDataSource" jndi-name="java:/myDS"/>
 <jee:jndi-lookup id="entityManagerFactory_module" jndi-name="java:comp/env/test/myfact"  expected-type="javax.persistence.EntityManagerFactory"/>   
 <tx:jta-transaction-manager/>
</beans> 


In this case, the application uses a server-deployed persistence unit. Thus the javax.persistence classes and the persistence provider (Hibernate) are contained in modules in JBoss ( in JBOSS_HOME\modules\system\layers\base\javax\persistence\api\main) and added automatically by the application while the deployment (When detecting the persistence.xml or persistence-unit, JBoss inject implicitly Hibernate).
So using the server-deployed persistence unit, you need also to declare the JNDI persistence context in the Web.xml:
NB: The persistence-unit-name specified in web.xml should be the same  in perssitence.xml file  ( <persistence-unit name="module_enterprise" )


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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-app_3_0.xsd" metadata-complete="true">

 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
 <persistence-unit-ref>
  <persistence-unit-ref-name>test/myfact</persistence-unit-ref-name>
  <persistence-unit-name>module_enterprise</persistence-unit-name>
 </persistence-unit-ref>
</web-app>

Note here that the name that we have declared in the application context is java:comp/env/test/myfactand in the web.xml, we need to put just test/myfact

If Spring cannot find the Default JBoss transaction Manager, you can guide it like explained in this post.

- Spring manage the EntityManager.

Using JPA2.0, we can enable the JPA in JBoss and use the first possibility. But as we have disabled JPA in JBoss, Spring is now responsible of creating th Transaction and the EntityManagerFactory.
My applicationContext looks like this:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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:context="http://www.springframework.org/schema/context"
 xmlns:jee="http://www.springframework.org/schema/jee"
 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/context http://www.springframework.org/schema/context/spring-context.xsd
 http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

 <!-- post-processors for all standard config annotations -->
 <tx:annotation-driven />
 <context:annotation-config />
 <context:component-scan base-package="org.test"/>
 
 <jee:jndi-lookup id="myDataSource" jndi-name="java:/myDataSource"/>
 
 <bean id="emfEnterprise" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="myDataSource" />
  <property name="jpaVendorAdapter" ref="jpaAdapter" />
  <property name="persistenceUnitName" value="module_enterprise"/>
   <property name="persistenceXmlLocation" value="classpath*:META-INF/jpa-persistence.xml"/>  
 </bean> 
 
 <bean id="mddEnterpriseTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="emfEnterprise" />
 </bean> 

  <bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="showSql" value="true" />
        <property name="generateDdl" value="true" />
        <property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
    </bean>
</beans>



That's all, so now you are able to integrate JBoss with Spring either by allowing JBoss manage your persistence unit or by letting only Spring doing this and EXCLUDE JBOSS JPA and Hibernate modules.




Thursday, May 8, 2014

JPA Criteria API Queries (CriteriaBuilder, CriteriaQuery) Create Dynamic Query instead of appending String

Here is an example about how to create a Dynamic Query.


 This is very efficient when you have a method where some parameters may be null. so instead of creating a String builder (or String Buffer) and append the SQL Query, it is better to use the Criteria API Queries.



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
public List<Devices> findDevice(String enterpriseId, List<String> modelIds, List<String> deviceIds, Date constructionDate) {
 
 CriteriaBuilder builder = entityManager.getCriteriaBuilder();
 CriteriaQuery<Devices> query = builder.createQuery(Devices.class);
 Root<Devices> deviceRoot = query.from(Devices.class);

 List<Predicate> predicateList = new ArrayList<Predicate>();
 if(enterpriseId !=null ){
  predicateList.add(builder.equal(deviceRoot.get("enterprise").get("enterpriseId"), enterpriseId));

 }
 if(modelIds != null && !modelIds.isEmpty()){
  predicateList.add(builder.isTrue(deviceRoot.get("deviceModel").get("modelId").in(modelIds)));
 }
 if(deviceIds != null && !deviceIds.isEmpty()){
  predicateList.add(builder.isTrue(deviceRoot.get("deviceId").in(deviceIds)));
 }

 predicateList.add(builder.greaterThan(deviceRoot.<Date>get("constructionDate"), constructionDate));

 query.where(builder.and(predicateList.toArray(new Predicate[predicateList.size()])));

   
 return entityManager.createQuery(query).getResultList();
}

Hope it helped you :) 

Wednesday, May 7, 2014

JPA Mapping Native Query to Bean (POJO, Class) other than the Entity JPA2.1

Working with JPA 2.0, every time I need to create a new bean (named PersonDetail) from the result of a query, I create a NamedQuery like this:


1
2
3
@NamedQuery(name="findPerson", 
 query = "SELECT new  org.nd.enterprise.bean.PersonDetail(e.personId, e.birthDate)"
   + " FROM Person p WHERE p.personId = :person")

A constructor with the desired output should be set in the PersonDetail bean.

But using the NativeQuery, this is not possible as the Query will be executed like it is given.
In fact while creating the query (NativeQuery or NamedQuery), we can fore the result to be the desired Bean, but this  will generate this error:



1
2
3
4
// Here we declare a @NamedNativeQuery   
List<PersonDetail> rec = entityManager.createNamedQuery("findPerson", PersonDetail.class).getResultList();
// Here we declare just a String query = "select ...."
List<PersonDetail> rec2 = entityManager.createNativeQuery(query,PersonDetail.class).getResultList();

org.hibernate.MappingException: Unknown entity: org.nd.enterprise.bean.PersonDetail

This is normal as it accepts only an Entity and not a Class (See this post to know how to fix that with JPA2.0). Fortenately JPA2.1 comes with the solution, by adding the @ConstructorResult in the @SqlResultSetMapping where you cone construct your Class (In JPA 2.0 only the Entity cn be specified and not another POJO .)




 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 Query q = em.createNativeQuery(
      "SELECT personId, birthDate)"
   + " FROM Person p",
      "PersonDetailsResult");

   @SqlResultSetMapping(
       name="PersonDetailsResult",
       classes={
          @ConstructorResult(
               targetClass=org.nd.enterprise.bean.PersonDetail,
                 columns={
                    @ColumnResult(name="personId"),
                    @ColumnResult(name="birthDate")
                    }
          )
       }
      )

Tuesday, May 6, 2014

Resolved : Logging in Jboss EAP 6 (JBoss AS7), Spring using Log4j

I was for a good time wondering why my Log4j is logging only the messages coming from my classes but not frameworks like  Spring, JDBC Template, CSveed. Every think was well configured, no error messages but I see framework messages only in the server.log.





So finally, I found the right steps to do.

Step 1:


Exclude Logging from JBoss. This previous article explains how to do.
So here is my jboss-deployment-structure.xml :


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<jboss-deployment-structure>
 <deployment>
 <!-- Add this line in the server lauch parameter : -Dorg.jboss.as.logging.per-deployment=false -->
  <exclusions>
   <module name="org.apache.log4j"/>
   <module name="org.apache.commons.logging"/>
              <module name="org.slf4j" />
             <module name="org.slf4j.impl" />
  </exclusions>   
 </deployment>
</jboss-deployment-structure>

Step 2:


Put you log4j.xml or log4j.properties in your classpath. In a Maven project, it should be under "\src\main\resources". Othrwhise, you can fix the classpath in the web.xml



1
2
3
4
5
6
7
8
<listener>
 <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

<context-param>
 <param-name>log4jConfigLocation</param-name>
 <param-value>classpath:/main/resources/META-INF/log4j.xml</param-value>
</context-param>


Step 3:

So Now, we need to add the required jar, AND Exclude some Jars. In the pom.xml, we need to exclude commons-logging from Spring-context jar and we need to add slf4j-api, slf4j-log4j12, jcl-over-slf4j, log4j.



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context</artifactId>
 <version>${spring.version}</version>
 <exclusions>
  <exclusion>
   <artifactId>commons-logging</artifactId>
   <groupId>commons-logging</groupId>
  </exclusion>
 </exclusions>
</dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-api</artifactId>
 <version>${slf4j-log4j12.version}</version>
 <scope>runtime</scope>
</dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-log4j12</artifactId>
 <version>${slf4j-log4j12.version}</version>
 <scope>runtime</scope>
</dependency>
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>jcl-over-slf4j</artifactId>
 <version>${slf4j-log4j12.version}</version>
 <scope>runtime</scope>
</dependency>
<dependency>
 <groupId>log4j</groupId>
 <artifactId>log4j</artifactId>
 <version>${log4j.version}</version>
</dependency>

So this step was the latest and Important step that I have discovered and which blocked my logging before.

Sunday, May 4, 2014

Resolve JBoss Startup Failure due to a missing deployment

On JBoss startup, I get one time this kind of error:



ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) JBAS014613: Operation ("add") failed - address: ([("deployment" => "myproject.war")]) - failure description: "JBAS018717: No deployment content with hash dcb4683c68f8ef7f07383bc7e0e0d3f039632c5f is available in the deployment content repository for deployment 'myproject.war'. This is a fatal boot error. To correct the problem, either restart with the --admin-only switch set and use the CLI to install the missing content or remove it from the configuration, or remove the deployment from the xml configuration file and restart."

In fact, Jboss tried to start but didn't find my war (myproject.war) in jbossHome/standalone/deployments.

In order to fix this error, you need to go to the standalone.xml, I have two deployments entries(app.war which don't cause error and myproject.war), and the one who is missing is myproject.war. So all what I need to do is to remove this entry.



1
2
3
4
5
6
7
8
    <deployments>
        <deployment name="app.war" runtime-name="app.war">
            <content sha1="e976bdb628576b38ac1bdee7299c55441807aa8d"/>
        </deployment>
        <deployment name="myproject.war" runtime-name="myproject.war">
            <content sha1="dcb4683c68f8ef7f07383bc7e0e0d3f039632c5f"/>
        </deployment>
    </deployments>



1
2
3
4
5
    <deployments>
        <deployment name="app.war" runtime-name="app.war">
            <content sha1="e976bdb628576b38ac1bdee7299c55441807aa8d"/>
        </deployment>
    </deployments>

Now my JBoss starts with no problems. NB : If you need to remove ALL the DEPLOYMENTS, do it but don't remove the <deployments>

Articles les plus consultés