Saturday, March 28, 2009

在 Web 上套用統計圖表 Jfreechart + cewolf

在開發 Web Page 時若要將統計圖表放到 Web 上,可以使用 JFreeChart 將數據轉換成圖形後,再以檔案的方式存起來,當 WebPage 顯示時再利用 URL 的方式將 image 從檔案中讀取出來並顯示在 Web Page 上

這個方式雖然可以解決統計圖顯示在 Web Page 上問題,但彈性太小,無法透過參數的方式指定圖形型態,大小,資料等,而 cewolf 這個 open source 便提供了此彈性的解決方案

Cewolf 在實作上是一個 JFreeChart 的 Wrapper 並提供 Tag Library 讓 Developer 可以方便的在 JSP 上套用統計圖表。要在 JSP 下顯示圖表可以參考以下步驟

1. Setup Libarary of JFreeChart and Cewolf
從 JFreeChart 與 Cewolf 下載 library 後,必須在 web.xml 中設定 servlet 與 servlet mapping 讓圖形可以透過此 mappng 讓 Browser 下載圖表









2. Add Chart into JSP
在 JSP 中若要加 JFreeChart 的圖表,需要加上 tag 如下 ([] 取代 <>)
[%@ taglib uri="/WEB-INF/cewolf.tld" prefix="cewolf" %]
[cewolf:chart id="line" title="EPS" type="verticalbar" xaxislabel="category" axislabel="value"]
[cewolf:data]
[cewolf:producer id="datasetProducer" /]
[/cewolf:data]
[cewolf:chartpostprocessor id="dataColor"/]
[/cewolf:chart]
[cewolf:img chartid="line" renderer="/cewolf" width="600" height="300" /]

3. Assign data to id associated to chart
這邊是用 struts 2 的方式,所以在 action 中加上 API 就可以當 id 用

public DatasetProducer getDatasetProducer() {
DatasetProducer producer = new DatasetProducer() {
/**
* Produces some random data.
*/
public Object produceDataset(Map params) throws DatasetProduceException {
log.info("producing data.");

// create the dataset...
DefaultCategoryDataset dataset = new DefaultCategoryDataset();

dataset.addValue(100, "serial1", "category1");
dataset.addValue(120, "serial2", "category1");
dataset.addValue(120, "serial3", "category1");

dataset.addValue(130, "serial1", "category2");
dataset.addValue(140, "serial2", "category2");
dataset.addValue(140, "serial3", "category2");

return dataset;
}

/**
* This producer's data is invalidated after 5 seconds. By this method the
* producer can influence Cewolf's caching behaviour the way it wants to.
*/
public boolean hasExpired(Map params, Date since) {
log.debug(getClass().getName() + "hasExpired()");
//return (System.currentTimeMillis() - since.getTime()) > 5000;
return true;
}

/**
* Returns a unique ID for this DatasetProducer
*/
public String getProducerId() {
return "PageViewCountData DatasetProducer";
}


/**
* @see java.lang.Object#finalize()
*/
protected void finalize() throws Throwable {
super.finalize();
log.debug(this + " finalized.");
}
};
return producer;
}

public ChartPostProcessor getDataColor() {
ChartPostProcessor dataColor = new ChartPostProcessor() {
public void processChart(Object chart, Map params) {
CategoryPlot categoryplot = (CategoryPlot) ((JFreeChart) chart) .getPlot();
categoryplot.setDomainGridlinesVisible(true);
categoryplot.setRangeCrosshairVisible(true);
categoryplot.setRangeCrosshairPaint(Color.blue);
NumberAxis numberaxis = (NumberAxis)categoryplot.getRangeAxis();
numberaxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
BarRenderer barrenderer = (BarRenderer)categoryplot.getRenderer();
barrenderer.setDrawBarOutline(false);
GradientPaint gradientpaint = new GradientPaint(0.0F, 0.0F, Color.blue, 0.0F, 0.0F, new Color(0, 0, 64));
GradientPaint gradientpaint1 = new GradientPaint(0.0F, 0.0F, Color.green, 0.0F, 0.0F, new Color(0, 64, 0));
GradientPaint gradientpaint2 = new GradientPaint(0.0F, 0.0F, Color.red, 0.0F, 0.0F, new Color(64, 0, 0));
barrenderer.setSeriesPaint(0, gradientpaint);
barrenderer.setSeriesPaint(1, gradientpaint1);
barrenderer.setSeriesPaint(2, gradientpaint2);
barrenderer.setLegendItemToolTipGenerator(new StandardCategorySeriesLabelGenerator("Tooltip: {0}"));
CategoryAxis categoryaxis = categoryplot.getDomainAxis();
categoryaxis.setCategoryLabelPositions(CategoryLabelPositions.createUpRotationLabelPositions(0.52359877559829882D));

}
};
return dataColor;
}

如此就可以在畫面上顯示














PS:
1. 在 cewolf:img chartid="line" renderer="/cewolf" width="600" height="300"中的 renderer 需要加上 "/" 因為 servlet 的 mapping 設定是以 "/" 為主
2. 這個 cewolf package 在 resin 下若顯示兩個圖表以上會有 bug

這邊提供一個範例 webchart ,提供給需要實作的人可以快速上手

Friday, March 27, 2009

學如逆水行舟,不進則退

因為工作的關係,最近接觸了 WebWork,與之前的 struts 1.x 比起來,實在方便許多,開發上實在大大的簡化了許多步驟,一個基本的 CRUD 可以在 1 個小時之內完成,若再配合 code-gen 的方式,開發的速度可謂神奇

從 struts 1.x 跳到 WebWork 可能無法接受,但跳到 struts 2.x 就滿容易的,設定檔其實差不多,只是在觀念上要改變,把 action 也當作 bean 看待便行了

說實在的,要進步,就要動起來。只守在原本的模式而不創新只會帶來退步而失去商機

Wednesday, March 25, 2009

Maven 使用技巧

最近看到一篇文章敘述如何成功的使用 Maven 作為 build tool ,每家公司都應該使用此 tool 作為軟體開發時的共同程式庫,減少要 start a new project 時所需要的時間,同時也可以將開發過的東西做成 Plugin ,隨時都可以引用

Five tips for successfully deploying Maven

Maven is one of those things that people seem to hate rather intensely, but nevertheless adoption is steadily rising in the Java community. I've worked with Maven almost daily since the 1.0 betas, and here are five things that I think could help your team working more efficiently with Maven.

  1. Use a repository manager

    A repository manager is basically an on-demand mirroring repository cache that you set up inside your IT infrastructure and use as primary repository for your builds. They basically work like this: if you build a project that depends on, for example, commons-lang-2.4.jar, the repository manager will download the artifact from the main Maven repository on the web, cache it locally and return it to the build client that asked for it. All subsequent builds that use the same managed repository will get the commons-lang jar delivered from the cache, not from the web.

    This has many advantages. First of all, it's fast. All project members, except the first one, will download any given dependency at LAN speed, which is especially nice when you're setting up a build environment from scratch (new project member, staging a clean build, etc). And of course it saves external bandwidth for other purposes and to lower costs.

    Second, it's safer. It allows you to run centralized and incremental backups on all external dependencies that you projects use, and you reduce your dependency on the availability of public repositories.

    Third, it's convenient. From time to time you will need a library that's not (yet) available in any public repository, so you have to publish it somewhere. A repository manager makes that really easy. And if you're sharing internal libraries or interfaces between projects, it's extremely handy to deploy to the managed repository. You can even set up your continuous integration build to automatically deploy snapshots.

    I've had a pleasant experience working with Nexus, but there are others. A repository manager should be as natural a part of you infrastructure as SCM and CI if you're using Maven.

  2. Specify plugin versions

    By default, Maven will automatically download a new version of any given plugin whenever there is one available. Given that Maven is 99% made up of plugins (there's even a plugin plugin!), this is a potential point of breakage over time and in my opinion a design mistake.

    As of version 2.0.9, the default behaviour is improved by locking down the versions of the core plugins (where "core" is defined by this list). However, you still need to explicitly define versions for all non-core plugins, and that can be done at the top level pom.xml in a hierarchial project using the pluginManagement section.




    maven-assembly-plugin
    2.2-beta-2


    maven-antrun-plugin
    1.2




    Do this for the plugins that you actually use. Note that for plugins with group id org.apache.maven.plugin, you can omit the groupId element.

    This will make your builds more stable and eliminate a fairly rare but very annoying and confusing set of problems.

  3. Learn how to use the dependency plugin

    Maven introduced the concept of transitive depedencies to the Java community, and has been a source of confusion ever since. The dependency plugin is an invaluable tool for analyzing the results of the dependency algorithm, and to handle dependencies in various ways. Here are a couple of things you can do with it:

    • dependency:tree
      shows (you guessed it) the dependency tree for the project, what dependencies are being pulled in and why. It's a nice overview and can help you tweak the dependency structure by excluding artifacts or override versions and so on. Example output:

      [INFO] +- org.apache.activemq:activemq-core:jar:5.2.0:compile
      [INFO] | +- org.apache.camel:camel-core:jar:1.5.0:compile
      [INFO] | +- org.apache.geronimo.specs:geronimo-jms_1.1_spec:jar:1.1.1:compile
      [INFO] | +- org.apache.activemq:activeio-core:jar:3.1.0:compile
      [INFO] | | \- backport-util-concurrent:backport-util-concurrent:jar:2.1:compile
      [INFO] | \- org.apache.geronimo.specs:geronimo-j2ee-management_1.0_spec:jar:1.0:compile
    • dependency:go-offline
      Downloads all project dependencies and plugins, transitively. It's a good command to run both if you want to work offline for a while and if you want to get as many of the external dependencies in place in a single shot with no manual intervention while you go grab a cup of coffee and/or read another item in Effective Java ;-)

    • dependency:copy
      dependency:copy-dependencies
      If you ever need to handle artifacts as files, copying all or some of them to a custom location for whatever reason, this is a good approach.

    There are many more things you can do with it, and mastering it will help you get on top of the transitive dependency situation.

  4. Use the documentation

    Well, duh. But a weak point of Maven in the eyes of many people is the lack of documentation and the sometimes poorly organized information. There are a few good points of reference though, that you can spread around you team by setting up links on the Wiki for example:

    • The Definitive Guide to Maven: a free book from Sonatype, available both as HTML and PDF. Good for the beginner, and sometimes as a reference. If you don't know where to start, start here.

    • The plugin list: a comprehensive list to the official plugins, with links to each project page and JIRA subsection. Most of the core functionality is actually performed by one of these plugins, and you can learn a lot by studying things like the resources plugin documentation.

    • The POM reference: for the slightly more advanced user. Every element in the POM is explained. Don't forget to specify the XSD information in your POM file to get the most help from your XML editor.

  5. Understand the conventions

    Maven is a conventions-based tool, relieving you from scripting common task like compiling source code, running tests or packaging a web application into a war file. Learning the conventions - directory structure, build phases - and working along them will make your life easier a lot of the time.

    There are definitely situations even in moderately sized projects to customize the build however, and Maven can sometimes be quite cumbersome to work with when you need to break the conventions. But by understanding the conventions and having the mindset that there is a good chance what you're trying to do can be accomplished within the realms of the conventions, you might be able to find a different approach than you otherwise might have.

    Perhaps that ugly jar-splitting, file-copying, token-replacing antrun hack that you spent an agonizing week writing could be replaced by extracting part of the project into a separate module and included as a dependency instead? It's a lot easier to swim downstream than upstream.

    Maven just released version 2.1.0 with many improvements. Another build tool that shows great promise is Gradle, which combines transitive dependencies and conventions à la Maven, the Ant task library and the scripting abilities of Groovy.