Thursday, December 07, 2006

Run/Debug java webapp without packaging/deploying it with Jetty and under Maven2

Goals

As the title mentioned, run(with maven2 command or in IDE)/debug(in IDE) java webapp without packaging the war file, installing/configuring any appserver like tomcat or deploying your code to anywhere. The practice will be "compile and run".

Why Jetty?

Jetty is a lightweight, flexible and embeddable Java servlet container. It's so flexible that it can even start servicing a webapp directly from given classpath and a web.xml file without assembling them into a standard webapp.

Step by step

Introduce several dependencies into you pom file. This is required whatsoever if you want to run/debug jetty in IDE and make sure the servlet-api, xercesImpl and java tools.jar(if you want to run JSP) are in dependency list.

<dependency>
<groupId>jetty</groupId>
<artifactId>org.mortbay.jetty</artifactId>
<version>5.1.10</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>jetty</groupId>
<artifactId>jasper-runtime</artifactId>
<version>4.2.20RC0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>jetty</groupId>
<artifactId>jasper-compiler</artifactId>
<version>4.2.20RC0</version>
<scope>runtime</scope>
</dependency>
Create jetty config file PROJECT_BASE_DIR/src/test/config/jetty.xml, assuming the web.xml file is already under src/main/webapp/WEB-INF directory. Feel free to change the directory and port if you want.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
"http://jetty.mortbay.org/configure.dtd">
<Configure class="org.mortbay.jetty.Server">
<Call name="addListener">
<Arg>
<New class="org.mortbay.http.SocketListener">
<Set name="port">
<SystemProperty name="jetty.port" default="8081" />
</Set>
</New>
</Arg>
</Call>
<Call name="addWebApplication">
<Arg>/</Arg>
<Arg>./src/main/webapp</Arg>
</Call>
</Configure>
Now, after injecting add dependencies into you IDE(For eclipse, run mvn eclipse:eclipse [-DdownloadSources=true] or use m2eclipse eclipse plugin), you should be able to run/debug the webapp after the code compiles. A java application with org.mortbay.jetty.Server as main class and src/test/config/jetty.xml as argument will start the webapp on port 8081.

Jetty6.x required less jar file dependencies, particularly only one jetty jar file if you don't want to run JSP, and syntax of jetty.xml file will be different. Please look at http://www.mortbay.org for more details.

Friday, November 24, 2006

Simplify Avalon components development by switching to plexus

Avalon vs. PicoContainer or Spring
Looking for the best IoC container is not the purpose of this article. Well... there is no "the best" without conditions, which means the answer varies according to the project you are working on. Personally I found Avalon useful in several projects where the requirement and scope is extremely uncertain.

PicoContainer project has an article which outlines the difference between several popular ways of containing components (not necessarily IoC containers). In general, comparing to the other options, Avalon depicts the lifecycle of a component elegantly and meticulously. And the compensation is to require the code to depend on its framework API.

Headache of Avalon? Simplified version of Avalon container that costs you less, Plexus
Assume you decide to take what Avalon requires and build the application on top Avalon API as I have done, the headache coming along is usally about:
  • Maintain endless XML meta files for each componenent
  • Building wierd .sar and .bar files
  • Choose a non-embeddable container like Loom or something on top of Fortress
  • Unit testing is doable but awkward
These issues stopped me from introducing Avalon to my manager for a while until Plexus project decoupled the Avalon personality nicely in 2003. The decoupled personality made Plexus a slim version of Avalon container which doesn't require anything like meta file or .bar files. It only requires, by default, xml descriptors to define Avalon components, similar to the bean definition xml file in springframework.

Start new project from scratch
Finally we can talk about code.

Step 1 is to create empty java project, com.planetexpress:plexus2avalon, with maven2 and add some dependencies into pom.xml.
mvn archetype:create -DgroupId=com.planetexpress -DartifactId=plexus2avalon
mkdir src/main/resources src/test/resources
vi pom.xml ##Add some dependencies described later
mvn eclipse:eclipse -DdownloadSources=true
Three dependencies are added into pom.xml file, although you may want to try out the latest plexus packages.
<dependency>
<groupId>avalon-framework</groupId>
<artifactId>avalon-framework</artifactId>
<version>4.1.4</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-container-default</artifactId>
<version>1.0-alpha-9</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-avalon-personality</artifactId>
<version>0.13</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
    <groupId>plexus</groupId>
    <artifactId>plexus-container-default</artifactId>
</exclusion>
</exclusions>
</dependency>
Step 2, write Avalon components. This is the most enjoyable part if you appreciate the effort made by Avalon. However I won't go into detail of how to write Avalon components. Following interface, class and config is created:
--./src/main/java/com/planetexpress/plexus2avalon/HelloWorld.java--
package com.planetexpress.plexus2avalon;

public interface HelloWorld
{
String ROLE = HelloWorld.class.getName();

String sayHelloTo( String name );
}
--./src/main/java/com/planetexpress/plexus2avalon/HelloWorldImpl.java--
package com.planetexpress.plexus2avalon;

public class HelloWorldImpl
implements HelloWorld
{
public String sayHelloTo( String name )
{
return "Good news, " + name;
}
}
--./src/main/resources/META-INF/plexus/components.xml--
<?xml version="1.0" encoding="UTF-8"?>
<plexus>
<components>
<component>
    <role>com.planetexpress.plexus2avalon.HelloWorld</role>
    <implementation>com.planetexpress.plexus2avalon.HelloWorldImpl</implementation>
    <life-cycle-handler>avalon</life-cycle-handler>
</component>
</components>
</plexus>
Step 3, test it and start it. Plexus provides a base test case class which starts a testing plexus container instance during setUp(), and disposes it in tearDown(). Although instantiation of container in test case is considered as anti-pattern and too integrated, I let it go in this example.
--./src/test/java/com/planetexpress/plexus2avalon/HelloWorldTest.java--
package com.planetexpress.plexus2avalon;

import org.codehaus.plexus.PlexusTestCase;

public class HelloWorldTest
extends PlexusTestCase
{
public void testSayHelloTo()
 throws Exception
{
 HelloWorld hello = (HelloWorld) lookup( HelloWorld.ROLE );
 assertEquals( "Good news, Fry", hello.sayHelloTo( "Fry" ) );
}
}
There are more work to do to finish this example. Like changing config to add the notion of Avalon personality, since Plexus doens't know Avalon by default. This config change is required if you want to take advantage of the Avalon component lifecycle, which could be the whole point of using Avalon. Please take a look at Plexus development guide for more details.

Conclusion: It's really simple!
With Plexus Avalon personality, building components doesn't require anything but XML descriptor file. With a configuration hierarchy, this work can be even simplified and distributed.

Wednesday, October 18, 2006

Vote: What do you want to find out in this website

This vote is published to the home page. Blogger.com doesn't seem to allow any form or input tag to be in blog. Stupid pollpub.com doesn't allow me to change anything after the poll is published. The typo will stay there for ever and ever. Anyway...

Friday, February 03, 2006

Is this my design?

I just came across this website design on deviantart.com. It resembles the home page of my waterview project amazingly.

--http://www.deviantart.com/view/28556278--



--http://cyclops-group.sourceforge.net/projects/waterview--

Thursday, February 02, 2006

Related content

The definition of a "project"
* Have a specific objective to be completed within certain specification
* Have a defined start and end date
* Have finding limits (if applicable)
* Consume human and none human resources (i.e., Money, people, equipments)
* Are multifunctiotnal (i.e., cut across several function lines)

Project management is the art of creating illusion that any outcome is a result of series predetermined, deliberate act when, in fact, it was dumb luck

Project management and planning class note #1

CS6388.501 Software Project Planning and Management.
Instructor: John Cole

Project Success: What is it?
Project Failure? If you don’t have any failures, you aren’t taking enough risks.
Control and commitment of resources: the Chiang Kai-Shek(蒋介石) model.

The six stages of a project:
* Wild enthusiasm
* Disillusionment
* Panic
* Search for the guilty
* Punishment of the innocent
* Praise and rewards for the non-participants.


“I want a list of all unforseen problems we’re going to encounter with this project.”
Lines of communication in decision-making need to be as short as possible

Project classification:
* In-house R&D
* Small construction
* Large construction
* Aerospace/defense
* MIS (does this include software development?)
* Engineering

Problems of placing highly technical people in charge of projects.
Moving a non-project-driven organization into project management.

“Life is a game, and money is one way of keeping score.” So projects that pay off well are scoring well. Should project managers be paid more than line management?

Why is software engineering different? Is it really engineering?

Are there any particular risks associated with software projects that aren’t found in other kinds of projects?

Tuesday, January 17, 2006

继续和jetty/maven2浴血奋战

jetty6的最新版本似乎有很多问题。至少在我拿到的jetty-6.0-SNAPSHOT.jar中,任何post的表单都会把现成卡住。这是个很大的问题。jetty的支持对我说这个问题已经被处理,但我没有拿到任何新的版本的包。

不管怎么样,jetty这个古老的项目肯定是有稳定的可用版本的。查了一下5.1.10可能是最稳定的版本,所以我决定在已有的plugin里自己写一个启动jetty的Mojo来测试。 这个mojo不必像jetty自己的那个那么灵活可以设置端口,contextPath等,因为我的web应用里总是会有一个测试用的jetty.xml文件的。所以只要plugin允许用户告诉它这个文件在哪儿就行了。

这个程序非常简单,最终只是简单的调用了org.mortbay.jetty.Server.main, 带一个参数是jetty.xml的路径。它的源码可以在这里找到。

Thursday, January 12, 2006

用jetty6和maven2启动,调试web应用

maven1的日子里对付web应用我一般的做法是用maven-appserver-plugin在项目的target目录里搭建只给当前项目开发用的 tomcat实例,通过配置maven-war-plugin, 可以把程序部署到这个tomcat实例里。调试时因为tomcat实例已经存在了,只要确定tomcat的bootstrap包在classpath里, 就可以在IDE里很容易的启动tomcat的实例。这个流程一旦配置好了很容易用,所以在过去的三年里我启动,调试web应用时都是这样做的。

maven2 里我一直在找类似的做法,可appserver插件没有了,war插件工作起来也不太一样,assembly最终必须产成一个文件,tomcat插件则是 通过tomcat的admin webapp把war热部署到运行的tomcat上,不容易调试。最终我决定试一试maven2的jetty插件。

jetty-maven-plugin是org.codehaus.mojo的子项目,还没有发布过什么稳定的版本,几个maven2 repository里也都没有,所以只能从svn上拿到源码手工安装。mojo项目源码在这里有 介绍。奇怪的是codehaus上各项目svn的位置这几天扑朔迷离, 公布的url经常无效。按照文档配置了maven-war-plugin和maven-jetty-plugin后(主要是设web.xml的位置),在 项目里执行 mvn clean compile war:war jetty:run-war, 报错说log4j找不到。log4j在pom.xml里有年头了,很多别的插件都在用它,这个错误没什么道理。

在maven2 repository里看到了jetty6的插件。这是一个和jetty插件,mojo项目无关的插件,是jetty6项 目里的一个子项目。与其把jetty插件里log4j的问题搞定,还不如试一试这个最新的东西。从codehaus svn上拿下jetty项目的源码,进入modules/maven-plugin, maven package install这个plugin。 在项目的pom.xml中做如下配置(这个修改最终可以放到这些web应用的父pom里去)


<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webXml>${basedir}/src/main/webapp/WEB-INF/web.xml</webXml>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty6-plugin</artifactId>
<version>6.0-SNAPSHOT</version>
<configuration>
<goalPrefix>jetty6</goalPrefix>
<contextPath>/app</contextPath>
</configuration>
</plugin>
</plugins>
</build>


在项目下执行mvn jetty6:run, 居然程序很顺利的跑起来了。

下一个要解决的问题是调试。调试和上面的配置以及maven2没关系。jetty6的启动是用org.mortbay.jetty:start包。在maven2的dependency里加入如下两个包:


<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.0.0beta7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>start</artifactId>
<version>6.0.0beta7</version>
<scope>test</scope>
</dependency>


scope是test因为他们的存在只是为了测试,这两个包最终是不会跑到war里的。main类是org.mortbay.start.Main, 带一个参数,参数是jetty的配置xml文件。此物在这里有详细介绍。我只是从jetty6项目考了一个到自己项目的src/test里,这样就可以在IDE里启动了。程序参数是jetty.xml, 主类是org.mortbay.start.Main。 jetty文件的内容如下

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC
"-//Mort Bay Consulting//DTD Configure//EN"
"http://jetty.mortbay.org/configure_1_2.dtd">
<Configure class="org.mortbay.jetty.Server">
<Set name="ThreadPool">
<New class="org.mortbay.thread.BoundedThreadPool">
<Set name="minThreads">20</Set>
<Set name="maxThreads">100</Set>
</New>
</Set>
<Set name="connectors">
<Array type="org.mortbay.jetty.Connector">
<Item>
<New class="org.mortbay.jetty.bio.SocketConnector">
<Set name="port">8080</Set>
<Set name="maxIdleTime">50000</Set>
</New>
</Item>
</Array>
</Set>
<Set name="handlers">
<Array type="org.mortbay.jetty.Handler">
<Item>
<New class="org.mortbay.jetty.webapp.WebAppContext">
<Set name="contextPath">/app</Set>
<Set name="war">./src/main/webapp</Set>
<Get name="SessionHandler">
<Set name="SessionManager">
<New class="org.mortbay.jetty.servlet.HashSessionManager" />
</Set>
</Get>
</New>
</Item>
</Array>
</Set>
<Set name="stopAtShutdown">true</Set>
<Call name="start" />
</Configure>


参考


Tuesday, January 10, 2006

几个Maven2 repository proxy


dotsrc.org
http://mirrors.dotsrc.org/maven2
central

ggi-project.org
http://ftp.ggi-project.org/pub/packages/maven2
central

sunsite.dk
http://mirrors.sunsite.dk/maven2
central

planetmirror.com
http://public.planetmirror.com/pub/maven2
central

lsu.edu
http://ibiblio.lsu.edu/main/pub/packages/maven2
central

ibiblio.net
http://www.ibiblio.net/pub/packages/maven2
central

Tapestry 4.0 is released

Tapestry一直是我很稀饭的一个Java Web Application Framework。 4.0的发布标志这它的成熟。

What is Tapestry's philosophy?

Tapestry is organized around four key principles:

  • Simplicity - web applications shouldn't be rocket science!
  • Consistency - what works in pages should work in components. What works in small applications should work in large applications. Different developers should find similar solutions to similar problems.
  • Efficiency - applications should be performant and scalable
  • Feedback - when things go wrong, the framework should not get in the way; in fact, it should provide useful diagnostics

These four principles drive the meta-principle: The simplest choice should be the correct choice. The decisions you make early in a project should not come back later to haunt you.

We have found that a component approach to web development is the best way of realizing these principles. Structuring the framework and your application around components makes it possible to move all the boring plumbing code out of your application and into the framework. You write less code, you write simpler code, sometimes you write no code. Tapestry picks up the slack.

Monday, January 09, 2006

又换皮了

简化了页面的结构,把控制皮的css单独拿了出来。新的皮基本上抄袭 deviantart.com上某人的作品

Thursday, January 05, 2006

Maven2的牛刀小试

想把CyclopsGroup.com上所有的项目里的Maven升级到Maven2已经很久了,一直有很多顾虑。比如怎么和现有的AntHill集成, 过去已经发布的包包该怎么办。为了不影响大局,在新的arapaho这个独立,简单项目上,我决定完全抛弃Maven1。 安装maven2很简单,下载maven-2.0.1的包,解压在某处。把bin/mvn在/usr/local/bin下做一个symbolic link就行了。

第一件要做的事儿是吧CyclopsGroup.com网站搬过来。按照其他maven2里项目的模样,我创建了一个名 叫com.cyclopsgroup.arapaho的项目,并在下面创建arapaho-main-site子项目。这个arapaho-main- site是将来存放网站内容的地方。按照maven网站上创建站点的文档, 我只是简单的把xdoc目录考到src/site/目录下,创建site.xml文件,执行mvn site。静态页面很顺利的生成到了target/site下。

现在的问题是我需要把一段googleanalytics的javascript放到每页的头上。文档上没有指出该怎么做但是从代码上看,在site.xml文件里应该可以写<head>标记。


<project>
<bannerLeft>...
<body>...
<head>
<script src="http://www.google-analytics.com/urchin.js"...
...


很遗憾,mvn site后改动没有任何效果。这是因为当前的maven是2.0.1release,很多包都不是最新的。所以需要下载最近的maven。
svn co http://svn.apache.org/repos/asf/maven/components/trunk, 进入trunk执行./bootstrap.sh, 在maven-core/target下产生最新的maven-2.1-SNAPSHOT-bin。解压到另一个目录,在/usr/local/bin里 重建mvn连接。

再次mvn site还没效果,手工安装最新的插件试一试。 svn co http://svn.apache.org/repos/asf/maven/plugins/trunk, 进入拿到的trunk执行mvn package install。 报错说需要的maven-2.0.2-SNAPSHOT不存在。

原来刚刚做的新的maven是 maven-2.1-SNAPSHOT, 这里插件需要的是2.0.2-SNAPSHOT。 在trunk上最新的maven已经抛开了别的插件走的太远了。果然,在maven/components/branchs里有个maven-2.0.x 分支。把这个分支搞下来:svn co http://svn.apache.org/repos/asf/maven/components/branches/maven-2.0.x 再用./bootstrap.sh做一个maven包,这回是maven-2.0.2-SNAPSHOT-bin。

解压新的包,重建/usr/local/bin/mvn, 再在arapaho-main-site里做mvn site, 得到一些velocity错误,大概是说244行的$item.toUnescapedString()不认识。打开site的velocity模板, 这行偏偏就是我要的东西。


#if ( $decoration.body.head )
#foreach( $item in $decoration.body.head.getChildren() )
#if ( $item.name == "script" )
$item.toUnescapedString()
#else
$item.toString()
#end
#end
#end


$decoration 是何许人士?这些代码已经跑到了maven的doxia子项目里面,大概还是包版本的问题。svn拿到doxia项目,mvn package install, 可问题还没有解决。难道maven-site-plugin一定要求用户用它自己的模板,我想要个简单的都不行吗?不会的。打开这段程序, 从execute方法里可以看出,site插件不仅像maven1一样允许你指定一个自己的velocity template, 还可以在site.xml里指定一个site skin包。

在arapaho大项目里建arapaho-skin-default子项目,内容很简单:

src/main/resource
+ images/some iamges
+ css/site.css
+ META-INF/maven/site.vm(modified)

site.vm里加进去了我想要的javascript,并对页面的格式做了很多修改。给arapaho-skin-default做mvn package install, 修改arapaho-main-site的site.xml

<project>
<skin>
<groupId>com.cyclopsgroup.arapaho</groupId>
<artifactId>arapaho-skin-default</artifactId>
<version>1.0-SNAPSHOT</version>
</skin>
<bannerLeft>...
...

原来的head标签就不用了。

再mvn site, 问题解决。这个arapaho-skin-default将来也可以在别的项目上重用。