显示标签为“java”的博文。显示所有博文
显示标签为“java”的博文。显示所有博文

2011年4月2日星期六

你想知道a love b=?吗 --- QLExpress


在项目中时常需要涉及对表达式的处理,如:

  • "3.14 * 2.5 + 103/(24 + 13)" 的值
  • "aVar && (bVar || (cVar && dVar))" 的结果

甚至:

  • "1==2?50 +50:100+100"
  • "if 1==1 then return 100 else return 10;"
  • "a love b"
  • " for ... "

若实现以上要求QLExpress即可,QLExpress是Quick Laconic Express 高效简洁脚本,淘宝玄难的一个开源的表达式项目,纯java实现。

我们不用再借助groovy也能实现复杂,带有简单逻辑的处理代码,可自定义操作符。

项目地址:http://code.taobao.org/project/view/294/

wiki非常详细,再有其他叙述就是多余(包括技术和使用):http://code.taobao.org/trac/QLExpress/wiki/ZhWikiStart

给出一个在项目中实际使用的例子。

需要判断一个用户是否具有某种符合的权限,如用户是否开通某中服务、是否打上某个标记、

是否附上某个权限code,这些权限还要进行与、或、非等逻辑运算、优先级控制如括号,如:


isService(userid) && ( hasTag("LP") || hasCode(256) ) #开通某个指定的服务, 并且具有"LP"标记或有256权限码。


代码截图如附件图:

这里的场景代码很简单,就是把表达式里的name如isService绑定到对象的方法isService上。

在以前如果我们实现这样的需求,一般把expression写成groovy语法脚本,再在java里调用执行。


如果有其他类似表达式的需要,可以试试QLExpress,能减少不少麻烦,代码里有大量的测试用例参考。

2010年5月11日星期二

Map初始化

map = new HashMap();
我们经常使用,然后put对象。
这样创建map对象后,map会自动初始化16的长度Entry数组存放对象,增长因子是0.75f。

有一种使用方法,就是我知道要放多少个(如20个)对象有人这么干:
map = new HashMap(20)
这样好吗?当我们存入15个对象后(20*0.75 = 15),map需要进行resize,容量扩展到40(20 * 2)。
所以正确的做法是:
map = new HashMap(20, 1.0f);
或map = HashMap(20 * 0.75);

这样保证map不会进行resize操作,此操作是map最大的损耗。

尤其对于使用map进行装载大量数据(如10W条),最好指定合理容量,减少不必要的resize。
对于只存放几个对象,不必指定任何参数。

2010年1月13日星期三

运行jboss的JVM参数

/usr/java6/bin/java
-Dprogram.name=run.sh
-server -Xmx1g -Xms1g -Xmn256m -XX:PermSize=128m -Xss256k
-XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled
-XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m
-XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=70 -Djava.awt.headless=true
-Djava.net.preferIPv4Stack=true
-Djava.rmi.server.hostname=[jboss的IP地址]
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=1911
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Djboss.server.home.dir=/home/product/deploy/jboss_server
-Djboss.server.home.url=file:/home/product/deploy/jboss_server
-Dapplication.codeset=GBK
-Ddatabase.codeset=ISO-8859-1
-Ddatabase.logging=false
-Djava.endorsed.dirs=/usr/jboss/lib/endorsed
-classpath /usr/jboss/bin/run.jar:/usr/java6/lib/tools.jar org.jboss.Main

包括了GC配置和jconsole,线上系统可去除jconsole,若你的服务是集群的话,最好保留一个,以便
随时观察线上系统的运行状况。

2009年4月19日星期日

初始化是有顺序的

public class Abc{
private static Abc abc = new Abc();
private static Map STORE = new HashMap();

private Abc() {
_init();
}
public static Abc createAbc() {
return abc;
}

/**
* 初始化
*/
private void _init() {
STORE.size();//报Null异常
}
}

把代码中的2句颠倒即可:
private static Abc abc = new Abc();
private static Map STORE = new HashMap();
->
private static Map STORE = new HashMap();
private static Abc abc = new Abc();

这是初始化时,先执行了new Abc(),在Abc里再执行_init,所以还没有执行STORE赋值,所以为Null。

2009年4月16日星期四

jvm知识点还真多

没记错的话这是出自bluedavy之手。

2009年4月10日星期五

java socket

为了测试用,服务端返回客户输入的信息(回送),quit为退出,调试python socket。

//===========SMain.java
public class SMain {
public static void main(String[] args) {
new Thread(new SListener()).start();
}
}

//===========SListener.java
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ServerSocket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class SListener implements Runnable {

public SListener() {
}

public void run() {
BlockingQueue blockingQueue = new ArrayBlockingQueue(
50);
ExecutorService serverPool = new ThreadPoolExecutor(5, 50, 30,
TimeUnit.SECONDS, blockingQueue,
new ThreadPoolExecutor.DiscardPolicy());

ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(7777);
serverSocket.setSoTimeout(30 * 60 * 1000);
} catch (IOException e) {
e.printStackTrace();
return;
}

while (true) {
try {
serverPool.execute(new Sprocessor(serverSocket.accept()));
} catch (InterruptedIOException iioe) {
continue;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

//===========Sprocessor.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class Sprocessor implements Runnable {
private final Socket socket;

public Sprocessor(Socket socket) {
this.socket = socket;
}

@Override
public void run() {
System.out.println("接受到请求");

PrintWriter out = null;
BufferedReader in = null;
try {
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
String cmd;
while (true) {
cmd = in.readLine();
if (cmd.equals("quit")) {
break;
}
out.write(" " + cmd);
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null)
in.close();
} catch (Exception e) {
}
try {
if (out != null)
out.close();
} catch (Exception e) {
}
try {
if (socket != null)
socket.close();
} catch (Exception e) {
}
}
}
}

//=========python client
import socket
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ss.connect(('localhost',7777))
ss.send("aaa\n")
data = ss.recv(1024)
print data
ss.send("quit\n")

2009年4月6日星期一

获得磁盘(分区)大小

若有个分区"/home",获得该分区的大小方法如下:
File dir = new File("/home");
System.out.println(dir.getFreeSpace());
System.out.println(dir.getTotalSpace());
System.out.println(dir.getUsableSpace()); //更接近df的可用空间,推荐使用

但输出的大小和linux下使用df输出的结果有点不一样,相近。可以使用了,不用再df结果输出到文件,再从文件读取。

注:JDK1.6以及以上适用。

2009年4月5日星期日

java获得本机ip地址

在linux下,获得IP地址,一般做法:
InetAddress.getLocalHost().getHostAddress()
很不幸,是127.0.0.1,不是我们想要的局域网地址,还发现此方法只是简单的获得 /etc/hosts文件信息,不信你试试看,把文件里的127.0.0.1改为其它IP,上述的代码输出的就是改过后的内容。
InetAddress.getLocalHost().getHostName()是读取文件/etc/hostname。不是我们需要的。

有个解决办法,使用NetworkInterface,代码:
Enumeration netInterfaces = null;
try {
netInterfaces = NetworkInterface.getNetworkInterfaces();
while (netInterfaces.hasMoreElements()) {
NetworkInterface ni = netInterfaces.nextElement();
System.out.println("DisplayName:" + ni.getDisplayName());
System.out.println("Name:" + ni.getName());
Enumeration ips = ni.getInetAddresses();
while (ips.hasMoreElements()) {
System.out.println("IP:"
+ ips.nextElement().getHostAddress());
}
}
} catch (Exception e) {
e.printStackTrace();
}

(格式太难看,copy之,格式化之,ctrl+shift+F)
这里需要注意的是输出了所有的网络接口信息,包括回环接口(lo),以及IPv6信息。下面作些过滤,只取出eth0的ipv4地址:
Enumeration netInterfaces = null;
try {
netInterfaces = NetworkInterface.getNetworkInterfaces();
while (netInterfaces.hasMoreElements()) {
NetworkInterface ni = netInterfaces.nextElement();
String name = ni.getName();
if (name.equals("eth0")) {//若有多网卡清调整程序
Enumeration ips = ni.getInetAddresses();
while (ips.hasMoreElements()) {
InetAddress ipv4 = ips.nextElement();
if (ipv4 instanceof Inet4Address) {
System.out.println("IP:"+ ipv4.getHostAddress());
break;
}
}
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}

2009年3月17日星期二

2次出现String.repalce问题

以前遇到类似的问题,今天有来了:
public class Test {
public static void main(String[] args) {
String ss = "aaa $VALUE$ bbb";
System.out.println(ss.replaceAll("$VALUE$", "ccc"));
}
}

说明这个$符号还真不能乱用。结果如下(没变):
================
aaa $VALUE$ bbb

2009年3月4日星期三

java的一小陀问题

  • 1. 多线程jdk5的线程包
wait和sleep比较,join
同步,线程安全。
  • 2. jvm垃圾回收机制、jvm优化。
  • 3. JVM装载,以及classload,Class.forName
  • 4. ThreadLocal
  • 5. 反射,动态代理
public class BusinessObject implements BusinessInterface {

 private Logger logger = Logger.getLogger(this.getClass().getName());
 public void processBusiness(){
  //business processing
  System.out.println(“here is business logic”);
 }
}

BusinessInterface businessImp = new BusinessObject();

InvocationHandler handler = new LogHandler(businessImp);

BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance(
 businessImp.getClass().getClassLoader(),
 businessImp.getClass().getInterfaces(),
 handler);

proxy.processBusiness();

  • 6. 字节码操作CGLIB
  • 7. 系统优化
  • 8.

2009年2月27日星期五

获得AUTO_INCREMENT字段值

对于大多数在OO编程里执行insert操作,都要求返回刚插入记录的ID,而这个ID一般是AUTO_INCREMENT,如:
CREATE TABLE tab1 (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(64) NOT NULL,
PRIMARY KEY(id)
)
一般在Statement.executeUpdate执行insert语句时,返回影响的记录条数,就是1。但我们还需要得到ID值,返回给客户程序,刚刚插入的记录的ID(代表这个记录)。一般有几种办法:

1 在执行insert语句后,再执行SELECT @@IDENTITY AS ID 或 select last_insert_id() as id都是一样的。

2 ibatis做法和上面一样
<insert id="insertTab1">
insert into tab1
(name)
values
(#name#)
<selectKey resultClass="int" keyProperty="id" >
SELECT @@IDENTITY AS ID
</selectKey>
</insert>

3 通过Statment返回auto的值
pstmt = conn.prepareStatement("insert ...",Statement.RETURN_GENERATED_KEYS);
pstmt.setString(1, "name");
pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys();
rs.next();
int id = rs.getInt(1);

学java别忘了sun

刚开始学习java时还上去看看,随着工作年限的持续,反而很少去(出了下载jdk),近期偶然打开,发现不少好东西。
http://java.sun.com/docs

String StringBuffer StringBuilder

String是不可边类型,一旦创建就不能修改,当涉及String和String运算时,会创建多个String对象,有消耗,但不必太在意这点。

StringBuffer是可变类型,可以修改,不会频繁创建对象,并且线程安全,看看源码就知道了,有很多synchronized,在使用性能上要优于String。

StringBuilder和StringBuffer一样,只是少了synchronized,在多线程下不安全,但在使用StringBuilder时,有多少是在多线程下呢?!一般90%以上不在多线程下。推荐使用。

使用的优先顺序StringBuilder > StringBuffer > String

2009年2月25日星期三

java.net.BindException: Permission denied

在运行serverSocket = new ServerSocket(port);
却报出:java.net.BindException: Permission denied错误,在网上看到一段话,解决自己的疑问
-----------------------------
In UNIX environments port numbers below 1024 are reserved and can be bound or used only by root (superuser). So if the non-root users try to run the application they will receive an error message saying that
"java.net.BindException: Permission Denied"
There are two ways to avoid this exception. One way is to login as root and run the program. The other way is to specify a port greater than 1023

2009年2月19日星期四

String replace有问题吗?!

System.out.println("abacae".replaceAll("a", "@"));
System.out.println("abacae".replaceAll("a", "$"));

执行结果:
@b@c@e
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 1

当替换成$时就出异常,没兴趣再查就String实现,自己实现得了不过就3行代码:
int idx;
while((idx = input.indexOf("a")) != -1){
input= input.substring(0, idx) + "$" +input.substring(idx + 1);
}

2009年2月3日星期二

可能不知道我在说什么

紧用于记录,一个小请求流程 或 叫前端框架。并不是在说Command模式,只是记录一个流程。

1. 在action或screen(就是servlet)里
创建一个Command(代表一次请求)对象,信息包括请求的AO对象名、调用AO对象的方法名、和这次请求的参数需要封装(一般用到DO对象)到该Command对象里。
cmd = new CommandSurport("xxxAO", "xxxMethod");
cmd.setParameter();//其实就是放到Map里。

2. 还是在那个action里
在Spring容器中,选择一个CommandDispatcher,其实就是CommandDispatcherLogic(奇怪没有根据Command对象选择)。CommandDispatcher的作用就是把Command分发出去,其实就是执行的开始。
CommandDispatcher.execute(cmd);

3. 在CommandDispatcher.execute里
根据Command对象里的AO名信息从spring里获得(创建)指定AO对象(非单例)。在这里同时使用到spring的BeanPostProcessor(实现类为ApplicationObjectPostProcessor),把command设置到AO对象(其实是AO的父类ActionEvent)里。并把Command对象的参数(在第一步设置的),设置到AO对应的属性上(参看ApplicationObjectPostProcessor类)。

4. 执行AO.execute()
execute方法是在AO的父类ActionEvent里。
根据Command信息后的要调用的方法名如abc(第1步设置的),调用的AO对象的doAbc方法。
这个doAbc就是我们业务要做的。在这个AO对象里,有Command对象(在父类ActionEvent属性里)、和最主要的请求参数已经设置到AO对象的属性上了。有了这些就可以实现我们想要的逻辑。

到此完成,已经很清楚,没有必要有序列图。
逗了一个圈子,在doAbc里根据请求信息,执行业务逻辑。不知道这样做的好处是什么!?

2009年1月18日星期日

System里有那些属性

从没有细看过system的参数信息,随手几行代码打出来看看:
Properties prop = System.getProperties();
Iterator it = prop.keySet().iterator();
String key;
String value;
while(it.hasNext()) {
key = (String)it.next();
value = (String)prop.get(key);
System.out.println(key+": "+value);
}
大多数参数基本不需要我们关心,只有几个需要瞅几眼:
java.version 版本信息
sun.boot.class.path vm启动时装载的我们称之为标准的API(库、jar包)
java.ext.dirs vm启动时附加加载的jar,用户也可以丢进该目录(不推荐这么做),一般应用有自己的class load,如tomcat有全局的class load(tomcat下的lib)以及每个应用也有自己的class load(web-app下的lib)。
user.home 用户的OS主目录。
java.class.version 就是编译后的class文件包含编译器的版本信息,有时1.4编译后跑在1.5上,或相反,可能会抛出这种兼容性异常。

java.runtime.name: Java(TM) SE Runtime Environment
sun.boot.library.path: /usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/i386
java.vm.version: 10.0-b23
java.vm.vendor: Sun Microsystems Inc.
java.vendor.url: http://java.sun.com/
path.separator: :
java.vm.name: Java HotSpot(TM) Client VM
file.encoding.pkg: sun.io
sun.java.launcher: SUN_STANDARD
user.country: CN
sun.os.patch.level: unknown
java.vm.specification.name: Java Virtual Machine Specification
user.dir: /work/shamrock-ws/eclipse-ws/java-test
java.runtime.version: 1.6.0_07-b06
java.awt.graphicsenv: sun.awt.X11GraphicsEnvironment
java.endorsed.dirs: /usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/endorsed
os.arch: i386
java.io.tmpdir: /tmp
line.separator:

java.vm.specification.vendor: Sun Microsystems Inc.
os.name: Linux
sun.jnu.encoding: UTF-8
java.library.path: /usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/i386/client:/usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/i386:/usr/lib/jvm/java-6-sun-1.6.0.07/jre/../lib/i386:/usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/i386/client::/usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/i386::/usr/lib/mozilla/:/usr/lib/mozilla/:/usr/java/packages/lib/i386:/lib:/usr/lib
java.specification.name: Java Platform API Specification
java.class.version: 50.0
sun.management.compiler: HotSpot Client Compiler
os.version: 2.6.24-16-generic
user.home: /home/abc
user.timezone:
java.awt.printerjob: sun.print.PSPrinterJob
file.encoding: UTF-8
java.specification.version: 1.6
java.class.path: /work/shamrock-ws/eclipse-ws/java-test/bin
user.name: abc
java.vm.specification.version: 1.0
java.home: /usr/lib/jvm/java-6-sun-1.6.0.07/jre
sun.arch.data.model: 32
user.language: zh
java.specification.vendor: Sun Microsystems Inc.
java.vm.info: mixed mode, sharing
java.version: 1.6.0_07
java.ext.dirs: /usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/ext:/usr/java/packages/lib/ext
sun.boot.class.path: /usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/resources.jar:/usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/rt.jar:/usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/jsse.jar:/usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/jce.jar:/usr/lib/jvm/java-6-sun-1.6.0.07/jre/lib/charsets.jar:/usr/lib/jvm/java-6-sun-1.6.0.07/jre/classes
java.vendor: Sun Microsystems Inc.
file.separator: /
java.vendor.url.bug: http://java.sun.com/cgi-bin/bugreport.cgi
sun.io.unicode.encoding: UnicodeLittle
sun.cpu.endian: little
sun.desktop: gnome
sun.cpu.isalist:

2008年12月27日星期六

使用maven2构建多环境

这种解决办法有别于maven的官方解决方法。


软件的开发,一般有3个环境:开发(dev)、测试(test)、生产(live)。
dev:开发人员在本机上开发,如也许数据库是本机上,log的级别一般在DEBUG;
test:测试专用,完成构建工作,有专门的测试的库(不允许使用生产环境的库),log级别一般在INFO;
live:实际部署的环境。

每种环境有作一些差异,尤其对于dev来说,每个开发人员环境差异就更大,所以每个开发人员都可以配置自己独特的环境。在每种环境下只需要针对该环境配置一次即可,不需要在每次环境切换时修改配置。

这种情况下,使用maven很容易解决上述问题。文字描述比较难懂,还是例子来说话。

下面的工作前提是安装好maven2。在某目录下创建2个工程,这里只是例子组织名以及工程名根据自己喜好设置:
  • mvn archetype:create -DgroupId=com.kiwiyard.shamrock -DartifactId=shamrock-namenode-dal
工程名:shamrock-namenode-dal,后面简称为dal
创建好工程,即创建一个同工程名的目录,所有工程信息都在此目录下,
并自动创建好java的package:com.kiwiyard.shamrock;
  • mvn archetype:create -DgroupId=com.kiwiyard.shamrock -DartifactId=shamrock-namenode-service
创建一个shamrock-namenode-service工程,简称为service。

再在此目录下创建一个pom.xml和一个和该项目相关的环境配置文件shamrock.properties(文件名可自己随意取),后面会用到。一切完成后目录的结构如下:

|--shamrock.properties
|--pom.xml
|--shamrock-namenode-dal
****|--pom.xml
|--shamrock-namenode-service
****|--pom.xml

需要说明的是service依赖dal,service和dal都继承一个父pom,就是顶层的pom.xml。

  • 父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>com.kiwiyard.shamrock</groupId>
<artifactId>shamrock</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>

<modules>
<module>shamrock-namenode-dal</module>
<module>shamrock-namenode-service</module>
</modules>

</project>


分别到dal和service目录下,创建目录src/main/resources和src/test/resources。到dal的src/main/resources/下创建一个数据库文件datasource.properties,因为在dal里需要访问数据库,这正是我们需要对不同情况处理的。

分别在dal和service的
src/main/resources和src/test/resources下创建log4j.properties的日志配置文件。
  • dal的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">

<parent>
<groupId>com.kiwiyard.shamrock</groupId>
<artifactId>shamrock</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<modelVersion>4.0.0</modelVersion>
<artifactId>shamrock-namenode-dal</artifactId>
<packaging>jar</packaging>
<name>shamrock-namenode-dal</name>
<url>http://www.kiwiyard.com</url>

<dependencies>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
<scope>run</scope>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>run</scope>
</dependency>

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.13</version>
<scope>run</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<directory>${basedir}</directory>
<filters>
<filter>../shamrock.properties</filter>
</filters>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>

</project>

注意这里的
<directory>${basedir}</directory>
若不加这个配置,很难指向上级目录里的文件,maven对下面的解析就行不通:
<filter>../shamrock.properties</filter>
就解析成:dal工程所在的目录/../shamrock.properties,这个解析有点傻,当然就无法识别。加上上面的配置就可以了,不会出现上面情况。 就这个问题害的我花了不少时间。
  • service的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">

<parent>
<groupId>com.kiwiyard.shamrock</groupId>
<artifactId>shamrock</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<modelVersion>4.0.0</modelVersion>
<artifactId>shamrock-namenode-service</artifactId>
<packaging>jar</packaging>
<name>shamrock-namenode-service</name>
<url>http://www.kiwiyard.com</url>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.kiwiyard.shamrock</groupId>
<artifactId>shamrock-namenode-dal</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.13</version>
<scope>run</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
<directory>${basedir}</directory>
<filters>
<filter>../shamrock.properties</filter>
</filters>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>

</project>

dal和service的log4j.properties配置内容如下:
log4j.rootCategory=${log4j.level}, stdout
log4j.appender.stdout=${log4j.appender}
log4j.appender.stdout.layout=${log4j.layout}
log4j.appender.stdout.layout.ConversionPattern=${log4j.pattern}

dal的datasource.properties(其实也可以是其它类型的文件如spring的datasource配置文件)。内容如下:
username = ${db.name}
password = ${db.password}
url = ${db.url}
driverClassName = ${db.driver}
#connectionProperties
defaultAutoCommit = true
#defaultReadOnly
#defaultTransactionIsolation
#defaultCatalog
initialSize = ${db.initialSize}
maxActive = ${db.maxActive}
maxIdle = ${db.maxIdle}
minIdle = ${db.minIdle}
maxWait = ${db.maxWait}
#validationQuery
#testOnBorrow
#testOnReturn
#testWhileIdle
#timeBetweenEvictionRunsMillis
#numTestsPerEvictionRun
#minEvictableIdleTimeMillis
poolPreparedStatements = true
maxOpenPreparedStatements = 0

在这里的log4j和datasource的配置是关键,对于${}符号就是变量,是在不同的环境需要替换成和环境匹配的值。这些匹配的值就需要根据每种环境配置,配置文件为
shamrock.properties,内容如下:
#日志配置================================================
log4j.level = DEBUG
log4j.appender = org.apache.log4j.ConsoleAppender
log4j.layout = org.apache.log4j.PatternLayout
log4j.pattern = %5p %c{2}- %m%n

#数据库配置================================================
db.name = 用户名
db.password = 密码
db.url = jdbc:mysql://dev.kiwiyard.com:3306/shamrock?useUnicode=true&characterEncoding=ut8
db.driver = com.mysql.jdbc.Driver
db.initialSize = 2
db.maxActive = 10
db.maxIdle = 8
db.minIdle = 2
db.maxWait = 2

这里配置的就是环境变量,在编译时就根据这些值替换数据库配置和log4j配置。

在
项目工程的根目录下(就是顶极pom.xml存在的目录下)
#>mvn resources:resources
再到dal和service
工程的target/classes下,看看其datasource.properties和log4j.properties的内容已经被替换。


例子好像很长,
其实就是几个配置文件里一些关键参数需要替换,配置的关键地方用粗题标出。只要你对maven2有一点了解,理解起来就不是难题。这种实现方式个人觉得比官方的对同一文件创建多个针对环境的版本要好,如按官方办法datasource可能就有datasource.dev、datasource.test、datasource.live这么几个文件,显得很罗嗦。

还有若把你的工程放入svn中(99%是这样),最好不要把和环境相关的信息提交到svn上,如开发者A把自己的提交后,开发者B拿到后很可能要修改才能使用,这些配置之适合特定的个体,不要分发出去。如下是一种解决办法,把指向环境配置文件shamrock.properties(以此为例),改为.shamrock.properties(注意文件名前加了一个点)。
<filter>../shamrock.properties</filter>
=>
<filter>../.shamrock.properties</filter>
shamrock.properties是受svn版本控制的,他代表一些配置的共性,也可能是由项目主管提交的一份参考配置。

其它开发人员拿到shamrock.properties后,cp
shamrock.properties .shamrock.properties,自己所有的配置就可以在.shamrock.properties上改动,不用把该文件提交到svn上,所有人的环境配置都在本地,不会找成混乱。


官方的另外一种方式,你可以参考对照2种方式:
http://maven.apache.org/guides/mini/guide-building-for-different-environments.html

2008年12月23日星期二

java和json

  • json是一个数据表示协议,或数据格式,如同xml一样。协议参看:http://www.json.org/。
  • 基本所有的语言都支持json,java有很多实现,请参看:http://www.json.org。我这里用的是json-lib(http://json-lib.sourceforge.net/),并不是org.json。
  • 创建测试代码
创建2个java数据类(google会把格式给干掉了,请复制出并用ide格式化后阅读),一般是用于传输
User.java,类容如下:
import java.io.Serializable;
public class User implements Serializable{
private static final long serialVersionUID = -8604848876542493025L;

private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

CpxUser.java类内容:
import java.io.Serializable;
import java.util.Date;

public class CpxUser implements Serializable{
private User user;
private int age;
private String nickname;
private Date birDay;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Date getBirDay() {
return birDay;
}
public void setBirDay(Date birDay) {
this.birDay = birDay;
}
}

Test.java类内容:
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

public class Test {
/**
* @param args
*/
public static void main(String[] args) {
System.out.println("1.简单对象->json");
User u = new User();
u.setUsername("zzz");
u.setPassword("hhhh");
JSONObject jo = JSONObject.fromObject(u);
System.out.println(jo.toString());

System.out.println("2.map->json (结果和1一样)");
Map mu = new HashMap();
mu.put("username", "zzz");
mu.put("password", "hhhh");
JSONObject jo1 = JSONObject.fromObject(mu);
System.out.println(jo1.toString());

System.out.println("3.复杂对象->json");
CpxUser cpxUser = new CpxUser();
cpxUser.setUser(u);
cpxUser.setAge(26);
cpxUser.setNickname("a chu");
cpxUser.setBirDay(new Date());
JSONObject jo2 = JSONObject.fromObject(cpxUser);
System.out.println(jo2.toString());

System.out.println("4.json->复杂对象 (可以debug看对象内容)");
String json_str = jo2.toString();
CpxUser cpxUser1 = (CpxUser)JSONObject.toBean(jo2, CpxUser.class);
System.out.println(cpxUser1.getAge());

System.out.println("5.简单对象数组->json");
User u1 = new User();
u1.setUsername("zzz1");
u1.setPassword("hhhh1");
User u2 = new User();
u2.setUsername("zzz2");
u2.setPassword("hhhh2");
User[] users = new User[]{u1,u2};
JSONArray arrs = JSONArray.fromObject(users);
System.out.println(arrs.toString());
}
}
  • 输出结果
非常简单,无需多说:

1.简单对象->json
{"username":"zzz","password":"hhhh"}

2.map->json (结果和1一样)
{"username":"zzz","password":"hhhh"}

3.复杂对象->json
{"nickname":"a chu","age":26,"birDay":{"time":1230096693386,"minutes":31,"seconds":33,"hours":13,"month":11,"year":108,"timezoneOffset":-480,"day":3,"date":24},"user":{"username":"zzz","password":"hhhh"}}

4.json->复杂对象 (可以debug看对象内容)
26

5.简单对象数组->json
[{"username":"zzz1","password":"hhhh1"},{"username":"zzz2","password":"hhhh2"}]


可以看出Java处理json非常简单,转换自如,相对xml描述精简很多,尤其在网络传输具有非常大的优势。java json包也非常轻量级,尤其是org.json(很遗憾该例没有使用该包)共有15个类,没有依赖,可以把源码放入工程。

注:不太喜欢org.json的实现,json-lib更面向对象,符合程序员的编码习惯(从上边的例子可以看出),缺点是依赖其它包。

2008年12月9日星期二

用maven构建一个小型的复杂项目

若你还对maven2不太了解,快速获得一些信息。
http://maven.apache.org/guides/getting-started/index.html

本来想写个多个工程的项目,写了一点发现和“getting-started”的“How do I build more than one project at once?”类似(只不过是10来个工程的项目),所以作罢。


--------------------------------------------------------------------------
在某个目录下创建工程
:
mvn archetype:create -DgroupId=com.kiwiyard.shamrock -DartifactId=shamrock-clientSDK-java
工程名:
shamrock-clientSDK-java
创建好工程,创建一个目录同工程名,
并自动创建好java的package:com.kiwiyard.shamrock;
在package下还有一个测试类,基本没用直接删除。



构建多个项目可以参考下面的文章,还可以实现不同的环境:

使用maven2构建多环境