27 10月 2009

emma-maven-plugin always executes test cases twice

這是碎碎唸
如果同時要出Emma Code Coverage跟Surefire Test report的話,一定要跑2次 Unit Test,聽起來很合理,但其實根本就不用。
只要在Surefire執行前先將test classes做instrument,這様測出來的就是具有coverage的unit test.
<plugin>
 <groupId>org.sonatype.maven.plugin</groupId>
 <artifactId>emma-maven-plugin</artifactId>
 <version>1.1</version>
 <inherited>true</inherited>
 <executions>
  <execution>
   <id>instrument</id>
   <phase>process-classes</phase>
   <goals>
    <goal>instrument</goal>
   </goals>
  </execution>
 </executions>
</plugin>
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-surefire-plugin</artifactId>
 <version>2.4.3</version>
 <inherited>true</inherited>
 <configuration>
  <forkMode>once</forkMode>
        <reportFormat>xml</reportFormat>
  <classesDirectory>${project.build.directory}/generated-classes/emma/classes</classesDirectory>
 </configuration>
</plugin>
這様跑完就㑹產生coverage.em, coverage.ec等產生Code Coverage Report所需要的東西。


而jira上也提出這個問題很久了…就是他X的沒人解決,
Allow generating reports outside the emma:emma goal


現在只好偷偷用emma4it-maven-plugin,單獨來跑report,只是不能出現在project reports裡
<plugin>
 <groupId>org.sonatype.maven.plugin</groupId>
 <artifactId>emma4it-maven-plugin</artifactId>
 <version>1.3</version>
 <executions>
  <execution>
   <id>report</id>
   <phase>post-integration-test</phase>
   <goals>
    <goal>report</goal>
   </goals>
   <configuration>
    <sourceSets>
     <sourceSet>
      <directory>${project.build.sourceDirectory}</directory>
     </sourceSet>
    </sourceSets>
   </configuration>
  </execution>
 </executions>
</plugin>

20 10月 2009

hibernate3-maven-plugin直接使用Spring AnnotationSessionFactoryBean

新公司有自己的一套Framework,並自行開發一套code gen的工具,稍為喚起了我曽經用過Hibernate Tools的記憶,一方面是因為用了JDK5的Generic後,要寫的Code變少,另一方面也是因為如果有修改,code gen出來的東西比較不易配合更動,所以也沒有納入我常用的工具之一。不過既然想起了Hibernate tools,那就再用用看有沒有什麼改變,或許會有驚喜吧。

不過似乎沒有…特別是不能直接用Spring Configuration Xml中已經訂好的設定,一定要另訂hibernate.cfg.xml,這就又讓我難過了一下,再發現居然連hibernate3-maven-plugin也無法直接使用Spring 裡訂好的AnnotationSessionFactoryBean或LocalSessionFactoryBean,只好試著改看看有沒有辦法可以讓我懶一點不要再去重訂hibernate.cfg.xml。


發現hibernate3-maven-plugin中有一個AnnotationComponentConfiguration,看來似是一個不錯的切入點,稍為改了一下還發現真能work!下面列的就是實做出來的東西。


1.pom.xml

改maven的plugin不用maven也太說不過去了吧,所以pom.xml是一定要的。

<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>org.elliot.hibernate</groupId>
 <artifactId>maven-hibernate3-jdk15</artifactId>
 <version>1.0-SNAPSHOT</version>
 <name>Maven Hibernate3 Implementation - JDK15</name>
 <packaging>jar</packaging>
 <dependencies>
  <dependency>
   <groupId>org.apache.maven</groupId>
   <artifactId>maven-model</artifactId>
   <version>2.0.6</version>
  </dependency>
  <dependency>
   <groupId>org.apache.maven</groupId>
   <artifactId>maven-plugin-api</artifactId>
   <version>2.0.6</version>
  </dependency>
  <dependency>
   <groupId>org.apache.maven</groupId>
   <artifactId>maven-artifact</artifactId>
   <version>2.0.6</version>
  </dependency>
  <dependency>
   <groupId>org.apache.maven</groupId>
   <artifactId>maven-project</artifactId>
   <version>2.0.6</version>
  </dependency>
  <dependency>
   <groupId>org.codehaus.mojo.hibernate3</groupId>
   <artifactId>maven-hibernate3-jdk15</artifactId>
   <version>2.2</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>3.3.2.GA</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
   <version>3.4.0.GA</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>ejb3-persistence</artifactId>
   <version>1.0.2.GA</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-annotations</artifactId>
   <version>3.4.0.GA</version>
  </dependency>
  <dependency>
   <groupId>jboss</groupId>
   <artifactId>jboss-common</artifactId>
   <version>4.0.2</version>
  </dependency>
  <dependency>
   <groupId>javassist</groupId>
   <artifactId>javassist</artifactId>
   <version>3.4.GA</version>
  </dependency>
  <!--可視情形改用其他版本的Spring-->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>org.springframework.orm</artifactId>
   <version>3.0.0.RC1</version>
  </dependency>
 </dependencies>
 <build>
  <plugins>
   <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
     <source>1.5</source>
     <target>1.5</target>
    </configuration>
   </plugin>
  </plugins>
 </build>
</project>
2. SpringComponentConfiguration.java

我暫用org.codehaus.mojo.hibernate3.configuration這package,怕AnnotationComponentConfiguration會不會有什麼method不能用,後來看code發現沒問題,也可以改成你喜歡的名稱

package org.codehaus.mojo.hibernate3.configuration;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.maven.plugin.MojoExecutionException;
import org.codehaus.mojo.hibernate3.ExporterMojo;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean;
import org.springframework.util.StringUtils;

public class SpringComponentConfiguration extends
  AnnotationComponentConfiguration {
 private static final Log logger = LogFactory
   .getLog(SpringComponentConfiguration.class);

 private ExporterMojo exporterMojo;
 private ApplicationContext applicationContext;

 public String getName() {
  return "springconfiguration";
 }

 public ExporterMojo getExporterMojo() {
  return exporterMojo;
 }

 public void setExporterMojo(ExporterMojo exporterMojo) {
  this.exporterMojo = exporterMojo;
 }

 @Override
 protected Configuration createConfiguration() {
  String sessionFactoryBean = getExporterMojo().getComponentProperty(
    "sessionFactoryBean", "sessionFactory");
  String appContextLocations = getExporterMojo().getComponentProperty(
    "appContextLocations", "classpath*:spring*.xml");
  logger.info("Initial info: [sessionFactoryBean]=" + sessionFactoryBean
    + ", [appContextLocations]=" + appContextLocations);

  String[] locations = StringUtils.delimitedListToStringArray(
    appContextLocations, ",");
  // Initial ApplicationContext from spring configuration xml files.
  this.applicationContext = new FileSystemXmlApplicationContext(locations);

  // Get AnnotationSessionFactoryBean from spring
  AnnotationSessionFactoryBean asfb = (AnnotationSessionFactoryBean) applicationContext
    .getBean("&" + sessionFactoryBean);

  DataSource dataSource = asfb.getDataSource();
  ThreadLocalHolder.setDataSource(dataSource);

  Configuration configuration = asfb.getConfiguration();
  configuration.setProperty(Environment.CONNECTION_PROVIDER,
    ThreadLocalConnectionProvider.class.getName());

  return configuration;
 }

 @Override
 protected void validateParameters() throws MojoExecutionException {
  // don't validate
 }

}
3.ThreadLocalConnectionProvider.java

因為Configuration不能直接設DataSource,所以只好放到ThreadLocal

package org.codehaus.mojo.hibernate3.configuration;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.connection.ConnectionProvider;

public class ThreadLocalConnectionProvider implements ConnectionProvider {
 private static final Log logger = LogFactory.getLog(ThreadLocalConnectionProvider.class);
 public ThreadLocalConnectionProvider() {
 }
 public void close() throws HibernateException {

 }

 public void closeConnection(Connection conn) throws SQLException {
  conn.close();
 }

 public void configure(Properties props) throws HibernateException {
  //Do nothing

 }

 public Connection getConnection() throws SQLException {
  DataSource dataSource = ThreadLocalHolder.getDataSource();
  if (null == dataSource) {
   logger.error("Please check ThreadLocalHolder.setDataSource has been invocked.");
   throw new SQLException("No usable DataSource.");
  }
  return dataSource.getConnection();
 }

 public boolean supportsAggressiveRelease() {
  // TODO Auto-generated method stub
  return true;
 }

}
4.ThreadLocalHolder.java

package org.codehaus.mojo.hibernate3.configuration;

import javax.sql.DataSource;

public abstract class ThreadLocalHolder {
 private static ThreadLocal<DataSource> dataSourceHolder = new ThreadLocal<DataSource>();

 public static DataSource getDataSource() {
  return dataSourceHolder.get();
 }

 public static void setDataSource(DataSource dataSource) {
  dataSourceHolder.set(dataSource);
 }
 
}
5.components.xml

這檔案放在src/main/resources/META-INF/plexus下,是maven plugin的設定檔

<component-set>
  <components>
 <component>
      <role>org.codehaus.mojo.hibernate3.configuration.ComponentConfiguration</role>
      <role-hint>springconfiguration</role-hint>
      <implementation>org.codehaus.mojo.hibernate3.configuration.SpringComponentConfiguration</implementation>
    </component>
  </components>
</component-set>
完成之後就用mvn install裝到repository裡以供使用,使用方式就只是改變hibernate3-maven-plugin中的dependency。

<build>
   <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>hibernate3-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
     <componentProperties>
      <jdk5>true</jdk5>
      <implementation>springconfiguration</implementation>
      <appContextLocations>classpath*:applicationContext.xml</appContextLocations>
      <sessionFactoryBean>sessionFactory</sessionFactoryBean>
     </componentProperties>
    </configuration>
    <dependencies>
     <dependency>
      <groupId>org.elliot.hibernate</groupId>
      <artifactId>maven-hibernate3-jdk15</artifactId>
      <version>1.0-SNAPSHOT</version>
     </dependency>
     <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-ehcache</artifactId>
      <version>3.3.2.GA</version>
     </dependency>
     <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-annotations</artifactId>
      <version>3.4.0.GA</version>
     </dependency>
     <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.2.121</version>
     </dependency>
    </dependencies>
   </plugin>
  </plugins>
 </build>
加入configuration中列出的設定,appContextLocations是專案中spring設定檔的檔案位置,sessionFactoryBean則是spring中你定義的AnnotationSessionFactoryBean名稱。

再來就可以用mvn hibernate3:hbm2ddl 之類的goals來測試囉。

14 10月 2009

載入特定目錄下的jar做為CLASSPATH

一種是用FIND
#!/bin/sh

CLASSPATH=`find lib -name *.jar|xargs|sed "s/ /:/g"`
CLASSPATH=".:$CLASSPATH"
echo $CLASSPATH

另一種就是自己組囉…
#!/bin/sh

CLASSPATH=.
for file in lib/*;
do CLASSPATH=${CLASSPATH}:$file;
done
echo $CLASSPATH