青岛啤酒这几年和华润一样,在全国各地大量收购中小啤酒企业。所以,你在当地喝的青岛啤酒不一定是青岛原产的(上海松江就有青岛啤酒分公司),即使是工艺一样,原料一样,那么水还不一样呢,口感当然有差别。最近常去联华买这种青岛原产的青岛啤酒,比上海松江产的青岛清爽啤酒贵一点儿,但口感好多了。Classic Tsingtao beer:D
作者:Andy(http://blog.xintiantang.com/andy),版权所有,转载请先联系我
这篇文章于2005/08/30 20:00开始,计划在一个月内完成,所以将会人工置顶。内容有点儿乱,先把想的和做的记下来,然后再慢慢整理。同时欢迎提出你的看法。更新记录:2005/08/30 20:00,2005/08/31 14:45,2005/08/31 21:21,2005/09/01 16:24,2005/09/01 21:53,2005/09/02 22:13
对于普通用户来说,使用传统的桌面应用程序(可能采用不同语言和工具开发的)可能存在这样那样的限制,比如界面风格千变万化、操作习惯不统一、学习难度大、无法或很难和外部应用通信等。而传统的服务器端应用程序或网站也存在许多限制,比如网络连通可能出现问题、响应速度慢、不能保证服务器端始终可用、担心数据安全和个人隐私问题、不够灵活、使用不方便等。
而基于浏览器的桌面应用程序正是综合桌面应用程序和服务器端应用程序的功能产生的。它包含以下优点:
基于浏览器的桌面应用程序(Browser-based desktop application)的定义:一个包含嵌入式网站服务器(Embedded web server)的能够在浏览器内的客户端的独立运行(Stand alone)的应用程序。
我把基于浏览器的桌面应用程序(Browser-based desktop application)简称为BBDA。其实BBDA和AJAX技术一样,也是一个buzzword,而不是什么新技术,只不过把几种通用的软件或技术组合在一起而已。和传统的重量级的Portal系统不同的是,BBDA是轻量级网络应用(Light weight web application),XML(Feed / RSS / XML-RPC / SOAP)是联系各个服务或应用的纽带。
其实,BBDA很符合目前热炒的WebOS(基于网络浏览器的操作系统)的特性,这方面Google做了个最好的Demo:Google Desktop, 在GD中,一切内容的表现都是网页(虽然隐藏了浏览器),并且很容易和Google网站进行通信(比如同时搜索本地和搜索Web,利用Feed(RSS)或XML-PRC或SOAP之类的协议读新闻、邮件、天气预报、股票行情等,微软该称RSS为Feed好像很有道理吧?)。现在GDS(Google Desktop Search)只是GD的一个模块而已,百度的桌面搜索只是单纯地模仿了GDS,却没有猜到原来Google是要做GD,好傻的百度。Google这几年的收购和挖人活动(Google Net, Google Browser等),看来一切都是为了WebOS而努力。这里顺便说一下Yahoo刚收购的Konfabulator,Konfabulator和GD有很大不同,我觉得它只是包含许多小软件或和网站交互的工具,只是个玩具箱而已,要做WebOS靠这个可以吗?看来Yahoo的眼光似乎始终不如Google。找到这篇文章:GoogleOS? YahooOS? MozillaOS? WebOS? 居然和我的想法如此相似(写这个之前我绝对没看过这篇文章)!
由于我主要做Java开发,所以只列出可能涉及的Java软件包:Browser(IE/FireFox), JDK, Tomcat/Jetty, HSQLDB/Apache Derby, Hibernate, Spring, Groovy, DOM4J,Apache Http Client, Lucene, Struts/WebWork/JSF/Tapestry/Wicket, DWR(AJAX), Apache Web Services(SOAP/XML-RPC), Apache POI(Word/Excel/ PDF), MP3 ID TAG 3 lib ,JPEG EXIF lib...
我的目的是组合几种通用的JAVA软件,用来进行快速的BBDA开发。对于开发者而言,只要选好合适的软件,并合理组合起来,完全可以达到或超过Ruby on rails的功能和开发效率。同时,对于普通用户而言,只要打开浏览器输入http://localhost就会进入Dashboard(ProtoPage是个很好的Prototype^_^,Start也不错),然后就可以管理日记(blog)、日历、通讯录、照片(flickr?)、音乐(itunes?)、文件、网摘(delicious?)、阅读订阅的RSS(bloglines?)、查看邮件(web mail)、新闻之类等,并可以进行搜索、共享、发布、与外部应用或网站同步等,构成一个完整的个人门户系统(Personal portal),XML(Feed / RSS / XML-RPC / SOAP)是联系各个服务或应用的纽带。
画了个草图(持续完善中):
接下来我会发布一个开发BBDA的基础软件包(将会包含一个简单的相册的BBDA),下面是我对基础软件包的说明。
关于嵌入式网站服务器(Embedded web server):
我这几天试用了Jetty和Tomcat。总以为Jetty很小呢,最适合做BBDA呢,结果发现软件包也很大了,另外很多功能不如Tomcat,Tomcat Embed 5.5由于使用Ecipse JDT,现在已经很小了(3M左右),而且标准,所以选了Tomcat Embed(这里下载)。下面的代码是Tomcat Embed的启动程序。
Start.java:
package com.terac.tomcat;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Embedded;public class Start {
public static void main(String[] args) {
try {
String path = System.getProperty("SERVER_HOME");
if(path!=null&&!"".equals(path)){
int port = Integer.parseInt(System.getProperty("SERVER_PORT","8080"));
startTomcat(path,port);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void startTomcat(String path,int port) throws UnknownHostException,
LifecycleException {
System.setProperty("catalina.home", path);
Embedded embedded = new Embedded();
Engine engine = embedded.createEngine();
engine.setName("Engine");
Host host = embedded.createHost("localhost", "web");
Context rootContext = embedded.createContext("", "");
rootContext.setPrivileged(true);
host.addChild(rootContext);
engine.addChild(host);
engine.setDefaultHost("localhost");
embedded.addEngine(engine);
Connector connector = embedded.createConnector((InetAddress) null,port, false);
embedded.addConnector(connector);
embedded.start();
}
}start.bat:
@set classpath=./lib/所有的jar文件
@java -DSERVER_HOME=%cd% -DSERVER_PORT=80 -classpath %classpath% com.terac.tomcat.Start
关于嵌入式数据库(Embeded database):
Apache Derby虽然比HSQLDB强大,但还是太大了,尤其是当用到IBM的JDBC Driver时。所以只好选HSQLDB。下面的代码是利用ServerContextListener在Web应用程序启动时启动HSQLDB服务器。
HSQLDBServletContextListener.java:
package com.terac.hsqldb;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;public class HSQLDBServletContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent evt) {
try {
ServletContext context = evt.getServletContext();
String params = "port=9001;silent=true;trace=false;database.0=" +
context.getRealPath("/") + "/WEB-INF/hsqldb/terac";
org.hsqldb.Server server = new org.hsqldb.Server();
server.putPropertiesFromString(params);
server.setLogWriter(null);
server.setErrWriter(null);
server.start();
} catch (Exception e) {
System.err.println(e);
}
}
public void contextDestroyed(ServletContextEvent evt) {
Connection conn = null;
try {
Class.forName("org.hsqldb.jdbcDriver");
conn = DriverManager.getConnection("jdbc:hsqldb:hsql://localhost:9001",
"sa", "123456");
Statement stmt = conn.createStatement();
stmt.executeUpdate("SHUTDOWN;");
} catch (Exception e) {
//
}
}
}
web.xml中需要加入listener:
com.terac.hsqldb.HSQLDBServletContextListener
关于全文检索引擎(Full text search engine):
Apache Lucene应用广泛,也足够强大,高亮显示(Highlighting)需要自己实现,自动摘要(autosummarize)需要Classifier4j 。下面是常用类的封装代码:
Indexer.java:
还未整理好
Searcher.java:
还未整理好
关于Shell:
有些时候,用户不满足已经有的服务,需要更灵活的、或者定制的功能,一个简单的、易学的、解释性的、交互式的Shell就显得很必要,比如Windows提供的cmd或Linux里的bash。研究了一下,发现Groovy正好能满足需要,“for (ch in "Hello world!") {println ch}”,这个稍微复杂点儿的Hello world很好懂吧?
关于时间服务(Time Service):
Windows里有计划任务(Schedule task),Linux里有Cron job,这个对于WebOS也是必须的,比如定时检查邮件,定时抓新闻等等都需要定时器(Timer),Spring framework封装了好几种Time service,足够了。
关于对象关系映射工具(OR mapping tools):
Hibernate是最常用最强大的工具。下面是Hibernate Session的封装代码(也可以利用Spring framework来管理):
HibernateUtil.java:
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Session;
import net.sf.hibernate.cfg.Configuration;
import org.apache.log4j.Logger;public class HibernateUtil {
private static Log log = LogFactory.getLog(HibernateUtil.class);
public static final ThreadLocal threadSession = new ThreadLocal();
private static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// We have to catch Throwable, otherwise we will miss
// NoClassDefFoundError and other subclasses of Error
log.error("Building SessionFactory failed.", ex);
throw new ExceptionInInitializerError(ex);
}
}
public static Session currentSession() throws HibernateException {
Session s = (Session) threadSession.get();
if (s == null) {
s = sessionFactory.openSession();
threadSession.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
session.set(null);
if (s != null)
s.close();
}
}
关于模型-视图-控制框架(MVC framework):
现在的MVC framework太多了,传统的有Struts/WebWork,新的有JSF/Tapestry/Wicket/Flex&Laszlo(Flash RIA) ,Wicket网站列出了55种之多!我试过JSF,始终不能另我满意,正在研究Wicket和RIFE(这两个都支持AJAX)。
待续。。。
Google's location-based search service now has a Mainland China version that supports Chinese language searches. It's at bendi.google.com. The service allows you to search for businesses and services in specific places, such as wireless Internet café in Chaoyang District, Beijing (无线上网 咖啡厅 北京朝阳区).
2. A 1 year old hermaphrodite with three legs to become female after the operation;
3. The universities which can not provide enough loans to students to be limited for enrollments;
4. Regular consumers' complaints to be handled within nine days;
5. The pork under standards sold in Carrefour Guozhan shop.
They dropped it to $4.95 a couple months back before it returned to $9.95.
After I posted about Philipp Lenssen’s poll of Blogspot blog’s, I thought: the guy who has to come up with a new “Blogs of Note” for Blogger’s dashboard every day must have the most frustrating job in the whole of Google. Half of the blogs he has to survey (and I reckon he must go through hundreds each day) is spam. Poor bastard.