星期三, 十月 11, 2006
HTML Comments
HTML中的注释,定义很简单,不过却比较搞。因为末尾的"--"和">"之间允许有空格,而开头的"<"和"--"之间却不可以。这就暗示了,注释的内容中,如果出现连续的"-",那么,解析将有可能出错。
Firefox表叫严格地遵循W3C标准,所以,碰到这种情形(html注释内容中包含"--"字样),有时会将部分注释显示出来。不过,IE不会,似乎IE对这些不符合规范的格式兼容得很好
星期六, 九月 23, 2006
HTML, XML & XHTML
Understanding HTML, XML and XHTML
如果你现在正在尝试设计符合XHTML标准的HTML网页,那么,这篇文章值得一看。
可能已经碰到的问题:
- 为什么<script>标签必须显式地以</script>闭合(<script src="blah..." type="text/javascript" />这种self-closed形式的语句不能工作)
- 为什么<img src="blah..." />可以正常显示?
答案很简单,现在所有浏览器的HTML Parser实际上是不支持“自闭”格式的tag的。象<img src="blah..." />这样格式的tag描述语句能够工作,其实是html parser误以为"/"是此标签的一个非法的attribution。对于非法的attribution的处理的不同,就造成了以上两个不同的结果。
XMLHttpRequest对象实现的安全性考虑:为什么不能跨域访问
AJAX通过XMLHttpRequest对象发送http请求,但是只能访问本站点内的资源,站点A的页面通过此对象访问站点B上的资源的行为是被禁止的。
原因,参考:http://www.w3.org/TR/XMLHttpRequest/#security
The restrictions are there to stop untrusted users of the API from using the implementation to retrieve sensitive data. Specifically, an implementation in a web browser will often want to restrict pages from a website A to retrieve data from website B. The reason for this is that website B could reside inside a corporate firewall. If data could be retrieved from website B then website A could use the browser effectively circumvent the firewall.
假设站点B是一个公司的内网(在防火墙后面),如果允许站点A的某个网页上的XMLHttpRequest对象访问站点B的资源的话,那么,当站点B所在公司的员工访问此页面时,站点A就可以通过脚本获得内网资源,使得防火墙形同虚设。
如果站点A和站点B是一个域名下的两个站点(子域),Abe Fettig’s Weblog介绍了一个方法(iframe+ajax)让XMLHttpRequest对象跨站点访问:How to make XmlHttpRequest calls to another server in your domain
星期六, 七月 22, 2006
javascript中的变量声明和赋值与否
如果变量a未声明,对变量a的调用会触发浏览器js engin报错:对象不存在
- 判别变量是否声明的操作:typeof(a) == "undefined";
- 如果变量a已被声明但尚未被初始化(赋值),那么此变量的值为undefined:
var a;
a == undefined; //return true
- 可以使用eval来动态创建变量:
eval("var a");
星期三, 四月 12, 2006
javascript测试工具JsUnit
测试浏览器端页面上的javascript。方法类似测试Java class的JUnit。他们都是XUnit的一部分
see http://www.edwardh.com/jsunit/
XUnit包含的单元测试包除了JUnit(java), JsUnit(js), 还有CppUnit(C++), PHPUnit(php), UTPlsql(oracle PL/SQL)等。
What is an XUnit framework?
- A standardized set of language-independent concepts and constructs for writing and running unit tests
- There is an XUnit framework for virtually every language
- Traditionally open-source
- All support certain constructs:
- TestCase and TestSuite are well-defined entities
- The assertion syntax follows rough standards, e.g. assertEquals(comment, expected, actual)
- All provide a TestRunner program to run tests
Listed at http://xprogramming.com/software.htm
星期六, 四月 01, 2006
Ruby在线交互教程
在线教程海了去,不过以这种交互方式进行的实在新鲜,a hands-on tutorial。用了一下,确实方便直观。
very hot! In delicious,this url has been saved by 3389 people
星期日, 三月 12, 2006
正确使用链接模拟浏览器的Go Back功能
正确方式(e.g.):
<a href="back_url.html" onClick="javascript:history.back();return false;">返回</a>
1)这种写法(<a href="javascript:history.back();">返回</a>)为什么不对?
如果浏览器不支持js,那么,浏览器会去直接要求打开href指向的页面
2)onClick中,如果忽略了"return false;"会如何?
return false,在这里,是告诉浏览器,取消加载href指向的url;如果在onClick内,忽略了return false();那么,浏览器将在history.back()之后,会再次要求打开href指向的页面,结果造成页面的重新加载,在某些情形下,会导致页面出错
星期一, 二月 20, 2006
再谈tomcat 5.x环境下的字符编码问题
@see "Tomcat环境下,字符编码的filter"
确实比较晕,tomcat server让多字节编码的国家吃尽苦头
原先以为,加这么个filter就一了百了了,实际上要复杂的多
原因是,这个filter的方法对tomcat 4.x版本是起作用的,但是在tomcat 5以后,就不完全适合了
为什么呢?
因为在http method是GET的时候,tomcat server 5.x会根据server.xml配置中<Connector>元素的URIEncoding属性设置,自己处理字符编码(此处讨论,不考虑主要是为了和tomcat 4兼容的useBodyEncodingForURI属性)
这也就意味着,如果你使用tomcat 5.x,你必须在doFilter的时候,区分http请求的方法是GET还是POST,如果是POST,调用request.setCharacterEncoding(encoding)来处理字符编码;如果是GET,则应当将这转换编码的工作交给tomcat server。为了让tomcat能正常处理你的字符编码,必须注意要设置正确的URIEncoding属性。譬如,网页编码是GBK的,则应当设置URIEncoding="GBK",特别的,如果需要生成URL的,则URL中的中文字符必须转码成符合GBK编码的%xx的格式。
对于使用GBK编码,使用tomcat 5.x server的网站,如下的配置和设计是正确的:
1)<Connector ... URIEncoding="GBK" ... />
2)使用filter,encoding=GBK
3)filter中区分http请求,代码如下:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
// Conditionally select and set the character encoding to be used
if (ignore || (request.getCharacterEncoding() == null)) {
String encoding = selectEncoding(request);
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if (encoding != null && httpServletRequest.getMethod().equalsIgnoreCase("POST"))
request.setCharacterEncoding(encoding);
}
// Pass control on to the next filter
chain.doFilter(request, response);
}
4)直接使用URL的,如果URL中的参数值包含中文,则必须根据GBK的编码方式将每个中文字符转成%xx%xx的格式,如:<A href="/search.shtml?action=search&key=%CC%AB%D1%F4%C4%DC">太阳能</A>
星期四, 一月 12, 2006
drw, tomcat and xalan exception
访问dwr的动态js时,抛出异常:
- StandardWrapper.Throwable
javax.xml.transform.TransformerFactoryConfigurationError: Provider org.apache.xalan.processor.TransformerFactoryImpl not found
at javax.xml.transform.TransformerFactory.newInstance(Unknown Source)
本地测试是好的,提交到服务器上,报错。
在lib下增加xalan.jar包,正常了
或者:http://www.robsanheim.com/2005/07/24/dwr-tomcat-55-and-xalan-classpath-error/
查看$catalina_home/common/endorse目录,果然发现xml-apis.jar文件。删除,同时删除新增的xalan.jar包,重启tomcat,正常了。就是这个endorse/xml-apis.jar和项目内的lib/xml-apis.jar冲突了,导致这个异常
查看全文星期三, 一月 04, 2006
Hibernate Lucene Integration
http://www.hibernate.org/hib_docs/annotations/reference/en/html/lucene.html
Chapter 4. Hibernate Lucene Integration
Lucene is a high-performance Java
search engine library available from the Apache Software Foundation. Hibernate
Annotations includes a package of annotations that allows you to mark any domain
model object as indexable and have Hibernate maintain a Lucene index of any
instances persisted via Hibernate.
4.1. Using Lucene to index your entities
4.1.1. Annotating your domain
model
First, we must declare a persistent class as @Indexed:
@Entity
@Indexed(index="indexes/essays")
public class Essay {
...
}
The index attribute tells Hibernate where the Lucene index is
located (a directory on your file system).
Lucene indexes contain four kinds of fields: keyword fields, text fields, unstored fields and unindexed fields. Hibernate Annotations provides annotations to mark a property of an entity as one of the first three kinds of indexed fields.
@Entity
@Indexed(index="indexes/essays")
public class Essay {
...
@Id
@Keyword(id=true)
public Long getId() { return id;
}
@Text(name="Abstract")
public String getSummary() {
return summary; }
@Lob
@Unstored
public String
getText() { return text; }
}
These annotations define an index
with three fields: Id, Abstract and Text.
Note: you must specify @Keyword(id=true) on the identifier property of your entity class.
4.1.2. Enabling automatic indexing
Finally, we enable the
LuceneEventListener for the three Hibernate events that occur after changes are
committed to the database.
<hibernate-configuration>
...
<event
type="post-commit-update"
<listener
class="org.hibernate.lucene.event.LuceneEventListener"/>
</event>
<event type="post-commit-insert"
<listener
class="org.hibernate.lucene.event.LuceneEventListener"/>
</event>
<event type="post-commit-delete"
<listener
class="org.hibernate.lucene.event.LuceneEventListener"/>
</event>
</hibernate-configuration>
基于apache Lucene的mp3搜索器
辗转摘抄,出处不明
前些日子找机器上的一首老歌时,费了些周折,后想到既然这些mp3有自己的标签信息,为何不利用起来呢?笔者就尝试用Lucene实现,分两部分,Mp3Indexer.java是创建索引的,mp3search.jsp是搜索mp3的页面。
下面是Mp3Indexer.java的代码。
package
mp3indexer;
import java.io.*;
import java.text.*;
import
java.util.*;
import org.apache.lucene.analysis.cjk.*;
import
org.apache.lucene.document.*;
import
org.apache.lucene.index.*;
public class Mp3Indexer
{
public
final static String mp3Path="d:mp3";//mp3所在目录
public final static String
indexPath="c:mp3Indexer";//索引存放目录
public static void main(String[] args)
throws ClassNotFoundException, IOException{
try {
IndexWriter writer =
new IndexWriter(indexPath, new CJKAnalyzer(), true);
indexMp3s(writer, new
File(mp3Path));
System.out.println("优化中....");
writer.optimize();
writer.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public static void
indexMp3s(IndexWriter writer, File file) throws Exception {
if
(file.isDirectory()) {
String[] files = file.list();
for (int i = 0; i
< files.length; i++) {
indexMp3s(writer, new File(file,
files[i]));
}
}
else if (file.getPath().endsWith(".mp3")) { //只对 MP3
文件做索引
System.out.print("正在处理文件:" + file + " ....");
// Add mp3 file
....
Document doc = new Document();
doc.add(Field.Text("name",
file.getName())); //索引文件名
doc.add(Field.UnIndexed("modified",
DateFormat.getDateTimeInstance().format(new Date(file.lastModified()))));
//索引最后修改时间
doc.add(Field.Text("size",""+NumberFormat.getNumberInstance().format(file.length()/1048576.0)+"MB"));
//索引最后修改时间
FileReader fReader = new
FileReader(file);
java.io.RandomAccessFile r=new
RandomAccessFile(file,"r");
r.seek(file.length()-128);
byte[] bt=new
byte[127];
r.read(bt);
String labelInfo=new
String(bt,"GB2312");
System.out.println(labelInfo);
if
(labelInfo.startsWith("TAG")) {
doc.add(Field.Text("comment",
labelInfo));
}
System.out.println("[处理完成]");
r.close();
fReader.close();
writer.addDocument(doc);
}
//end else if
}
} //end class
为blog增加全文搜索-Lucene
辗转摘抄,来处不明
全文搜索能大大方便用户快速找到他们希望的文章,为blog增加一个全文搜索功能是非常必要的。然而,全文搜索不等于SQL的LIKE语句,因为关系数据库的设计并不是为全文搜索设计的,数据库索引对全文搜索无效,在一个几百万条记录中检索LIKE '%A%'可能会耗时几分钟,这是不可接受的。幸运的是,我们能使用免费并且开源的纯Java实现的Lucene全文搜索引擎,Lucene可以非常容易地集成到我们的blog中。
Lucene不提供直接对文件,数据库的索引,只提供一个高性能的引擎,但接口却出人意料地简单。我们只需要关心以下几个简单的接口:
- Document:代表Lucene数据库的一条记录,也代表搜索的一条结果。
- Field:一个Document包含一个或多个Field,类似关系数据库的字段。
- IndexWriter:用于创建新的索引,也就是向数据库添加新的可搜索的大段字符串。
- Analyzer:将字符串拆分成单词(Token),不同的文本对应不同的Analyzer,如HtmlAnalyzer,PDFAnalyzer。
- Query:封装一个查询,用于解析用户输入。例如,将“bea blog”解析为“同时包含bea和blog的文章”。
- Searcher:搜索一个Query,结果将以Hits返回。
- Hits:封装一个搜索结果,包含Document集合,能非常容易地输出结果。
下一步,我们需要为Article表的content字段建立全文索引。
首先为Lucene新建一个数据库,请注意这个数据库是Lucene专用的,我们不能也不必知道它的内部结构。Lucene的每个数据库对应一个目录,只需要指定目录即可:
String indexDir = "C:/search/blog";
IndexWriter indexWriter = new
IndexWriter(indexDir, new StandardAnalyzer(), true);
indexWriter.close();
然后添加文章,让Lucene对其索引:
String title = "文章标题" // 从数据库读取 String content = "文章内容" //
从数据库读取
// 打开索引:
IndexWriter indexWriter = new IndexWriter(indexDir, new
StandardAnalyzer(), false); // 添加一个新记录: Document doc = new Document();
doc.add(Field.Keyword("title", title)); doc.add(Field.Text("content", content));
// 建立索引:
indexWriter.addDocument(doc); // 关闭: indexWriter.close();
要搜索文章非常简单:
Searcher searcher = new IndexSearcher(dir);
Query query =
QueryParser.parse(keyword, "content", new StandardAnalyzer());
Hits hits =
searcher.search(query);
if(hits != null){for(int i = 0;i < hits.length();
i++){ Document doc = hits.doc(i); System.out.println("found in " +
doc.get("title")); System.out.println(doc.get("content")); } }
searcher.close();
我们设计一个LuceneSearcher类封装全文搜索功能,由于必须锁定数据库所在目录,我们把数据库设定在/WEB-INF/search/下,确保用户不能访问,并且在配置文件中初始化目录:
<bean id="luceneSearcher" class="org.crystalblog.search.LuceneSearcher">
<property name="directory"> <value>/WEB-INF/search/</value>
</property>
</bean>
星期三, 十二月 21, 2005
Hibernate,left join fetch,重复对象(记录)的问题
Hibernate 3.0中,mapping文件缺省配置的one-to-many的子集合是lazy declarations的,fetch的模式是join,当应用需要访问子表(1:M)中的数据时,hibernate框架会主动发起一条查询的sql。当我们需要在获取主记录的同时得到子记录的数据时,我们可以使用HSQL,“left join fetch”。但是当子记录存在时,主对象记录会有重复。
解决方法:http://forum.hibernate.org/viewtopic.php?t=938705&highlight=onetomany+query
fetch return distinct root instances
join has the same meaning as SQL join, so just use
Set results = new HashSet(query.list()) if you don't want duplicate results.
星期二, 十二月 13, 2005
FileUpload, session还是request
使用struts.upload模块处理文件上传的时候,会用到org.apache.commons.fileupload包。
如果包含文件上传的Action的scope="session",那么当前会话下,临时的上传文件始终有效,除非主动删除它。此时,重新启动Tomcat,在load org.apache.commons.fileupload.DefferredFileOutputStream时,系统会抛掷异常:
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: org.apache.commons
.fileupload.DeferredFileOutputStream
常理来看,没必要创建会话级的上传文件的Formbean,因为太占内存了。但是Struts配置中,缺省的scope是session,所以要特别注意。