xstream是个好东西。对于配置文件的读取很方便。在mybog中我就用到了。不过今天打算用yupoo的api来做相册。发现xstream对于xmlnode的attribute解析支持不是那么的好。
对于这种节点格式的非常的简单
<result>
<page>1</page>
<pages>1</pages>
<perpage>100</perpage>
<total>19</total>
<photos>
<photo>
<id>ff8080810fc8ac78010fd3f158d40a52</id>
<owner>ff8080810f1a387b010f1a83d6530dfc</owner>
<title>Gmail-2</title>
<host>4</host>
<dir>20061230</dir>
<filename>231905_1463411198</filename>
</photo>
</photos>
</result>
简单的alias一下就可以读到值了
File file = new File("src/test/java/com/jdkcn/test/result.xml");
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
XStream stream = new XStream();
stream.alias("result", YupooResult.class);
stream.alias("photo",YupooPhoto.class);
YupooResult result = (YupooResult)stream.fromXML(reader);
可是Yupoo的api返回的xmlrpc的结果是这样的
<result page="1" pages="1" perpage="100" total="19">
<photos>
<photo id="ff8080810fc8ac78010fd3f158d40a52"
owner="ff8080810f1a387b010f1a83d6530dfc" title="Gmail-2" host="4"
dir="20061230" filename="231905_1463411198" />
</photos>
</result>
这样就load不到值了。没法去mailist里面找答案,果然有人问。
Hello,
I am not sure about the subject but here is what I needed help for:
XML:
<field name="value">I am a Field.</field>
I have already tried several structures and nothing seem to work.
Is this possible for XStream? :)
How is the Java class form to support this?
Thanks!
有人回答是看Converter的文档。果然找到答案了。
自己写一个converter就可以了。
下面是我的converter
package com.jdkcn.xstream;
import java.util.ArrayList;
import java.util.List;
import com.jdkcn.yupoo.YupooPhoto;
import com.jdkcn.yupoo.YupooResult;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
/**
* @author <a href="mailto:rory.cn@gmail.com">somebody</a>
* @since Jan 16, 2007 6:12:35 PM
* @version $Id YupooResultConverter.java$
*/
public class YupooResultConverter implements Converter {
/* (non-Javadoc)
* @see com.thoughtworks.xstream.converters.Converter#marshal(java.lang.Object, com.thoughtworks.xstream.io.HierarchicalStreamWriter, com.thoughtworks.xstream.converters.MarshallingContext)
*/
public void marshal(Object obj, HierarchicalStreamWriter writer, MarshallingContext context) {
// FIXME unfinish.
}
/* (non-Javadoc)
* @see com.thoughtworks.xstream.converters.Converter#unmarshal(com.thoughtworks.xstream.io.HierarchicalStreamReader, com.thoughtworks.xstream.converters.UnmarshallingContext)
*/
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
YupooResult result = new YupooResult();
result.setPage(new Integer(reader.getAttribute("page")));
result.setPages(new Integer(reader.getAttribute("pages")));
result.setPerpage(new Integer(reader.getAttribute("perpage")));
result.setTotal(new Integer(reader.getAttribute("total")));
reader.moveDown();
List<YupooPhoto> photos = new ArrayList<YupooPhoto>();
while(reader.hasMoreChildren()) {
reader.moveDown();
YupooPhoto photo = new YupooPhoto();
photo.setDir(reader.getAttribute("dir"));
photo.setFilename(reader.getAttribute("filename"));
photo.setHost(reader.getAttribute("host"));
photo.setId(reader.getAttribute("id"));
photo.setOwner(reader.getAttribute("owner"));
photo.setTitle(reader.getAttribute("title"));
photos.add(photo);
reader.moveUp();
}
result.setPhotos(photos);
return result;
}
/* (non-Javadoc)
* @see com.thoughtworks.xstream.converters.ConverterMatcher#canConvert(java.lang.Class)
*/
public boolean canConvert(Class clazz) {
return clazz.equals(YupooResult.class);
}
}
然后调用的地方修改一下就ok了。
XStream stream = new XStream();
stream.registerConverter(new YupooResultConverter());
stream.alias("result", YupooResult.class);
参考:
http://xstream.codehaus.org/converter-tutorial.html
2007年1月18日更新。
这里感谢网友 的提示。原来新版的xstream可以简单的解决了。在1.2.1的doc里面找到了这个两个方法。
useAttributeFor
public void useAttributeFor(java.lang.String fieldName,
java.lang.Class type)
- Use an XML attribute for a field or a specific type.
- Parameters:
fieldName
- the name of the field
type
- the Class of the type to be rendered as XML attribute
- Throws:
XStream.InitializationException
- if no AttributeMapper
is available
- Since:
- 1.2
useAttributeFor
public void useAttributeFor(java.lang.Class type)
- Use an XML attribute for an arbotrary type.
- Parameters:
type
- the Class of the type to be rendered as XML attribute
- Throws:
XStream.InitializationException
- if no AttributeMapper
is available
- Since:
- 1.2
这两个方法都是从1.2开始支持的。
也不用自己写converter了。这样就可以了
stream.alias("result", YupooResult.class);
stream.useAttributeFor("page", Integer.class);
stream.useAttributeFor("pages", Integer.class);
stream.useAttributeFor("perpage", Integer.class);
stream.useAttributeFor("total", Integer.class);
stream.alias("photo", YupooPhoto.class);
stream.useAttributeFor("id", String.class);
stream.useAttributeFor("owner", String.class);
stream.useAttributeFor("title", String.class);
stream.useAttributeFor("host", String.class);
stream.useAttributeFor("dir", String.class);
stream.useAttributeFor("filename", String.class);
除经特别注明外,本文章版权归
莫多泡泡所有.
署名,非商业用途,保持一致. somebody(莫多)
posted @
2007-01-17 18:24 莫多 阅读(7982) |
评论 (2) |
编辑 收藏
上周更新了一下myblog,添加了一个Filter,做统计访问用。可是后来发现出现乱码问题了。找了很久都没有找到问题。debug的时候看到 CharacterEncodingFilter确实是执行了。不过就是没有效果。执行之前是ISO-8859-1编码的,执行之后还是, CharacterEncodingFilter就没有起到作用。后来终于找到问题的原因了。原来是Filter配置先后顺序的原因。
刚开始的配置是这样的:
<
filter-mapping
>
<
filter-name
>
requestCounterFilter
</
filter-name
>
<
url-pattern
>
*.jhtml
</
url-pattern
>
</
filter-mapping
>
<
filter-mapping
>
<
filter-name
>
encodingFilter
</
filter-name
>
<
url-pattern
>
/dwr/*
</
url-pattern
>
</
filter-mapping
>
<
filter-mapping
>
<
filter-name
>
encodingFilter
</
filter-name
>
<
url-pattern
>
*.jhtml
</
url-pattern
>
</
filter-mapping
>
<
filter-mapping
>
<
filter-name
>
encodingFilter
</
filter-name
>
<
url-pattern
>
*.jsp
</
url-pattern
>
</
filter-mapping
>
先经过那个统计的filter然后再经过编码的filter。这样的话编码的filter就不起作用了。只要吧编码的filter放到最前面就没有问题了。改成这样就好。
<
filter-mapping
>
<
filter-name
>
encodingFilter
</
filter-name
>
<
url-pattern
>
/dwr/*
</
url-pattern
>
</
filter-mapping
>
<
filter-mapping
>
<
filter-name
>
encodingFilter
</
filter-name
>
<
url-pattern
>
*.jhtml
</
url-pattern
>
</
filter-mapping
>
<
filter-mapping
>
<
filter-name
>
encodingFilter
</
filter-name
>
<
url-pattern
>
*.jsp
</
url-pattern
>
</
filter-mapping
>
<
filter-mapping
>
<
filter-name
>
requestCounterFilter
</
filter-name
>
<
url-pattern
>
*.jhtml
</
url-pattern
>
</
filter-mapping
>
以后大家一定要注意啊。顺序问题也是很重要的。
除经特别注明外,本文章版权归莫多泡泡所有.
署名,非商业用途,保持一致. somebody(莫多)
posted @
2006-12-27 10:37 莫多 阅读(2677) |
评论 (3) |
编辑 收藏
昨天晚上配置myblog的rewrite。发现一个奇怪的问题。由于现在使用的这个pjblog,为了让搜索引擎收录的连接有效。我想把原来的asp连接rewrite到我的新程序上面。所以有这样一条规则。
<rule>
<from>^/article.asp\?id=(.*)$</from>
<to type="redirect">/entry/$1.jhtml</to>
</rule>
但是我这样的连接总是匹配不到,只要去掉那个?就可以了。这个正则表达式是没有问题的。/article.asp?id=64是可以匹配的到的。
后来看3.0的manual (http://tuckey.org/urlrewrite/manual/3.0/)才发现原来是这个的问题。
<urlrewrite> element
The top level element.
Attribute | Possible Value | Explanation |
---|
default-match-type (optional) | regex (default) | All rules and thier conditions will be processed using the Java Regular Expression engine (unless match-type is specified on a rule). |
wildcard | All rules and thier conditions will be processed using the Wildcard Expression engine (unless match-type is specified on a rule). |
decode-using (optional) | utf8 (default) | When URL is decoded UTF-8 will be used. |
null | Do not decode. |
[encoding] | Any string representing a supported character encoding eg, ISO-8859-1. See Java Charset Object for more info. |
use-query-string (optional) | false (default) | The query string will not be appended to the url that the "from" element matches against. |
true | The query string will be appended to the url that the "from" element matches against. |
use-context (optional) | false (default) | The context path will not be added to the url that the "from" element matches against. |
true | The context path will be added to the url that the "from" element matches against. |
就是那个use-query-string 的问题,默认的是不使用query-string就是把?后面的都忽略了。所以就不能匹配到了。只要在<urlrewrite>里面加一个属性就可以了。
<urlrewrite use-query-string="true">
</urlrewrite> 除经特别注明外,本文章版权归莫多泡泡所有.
署名,非商业用途,保持一致. somebody(莫多)
posted @
2006-12-12 10:33 莫多 阅读(2332) |
评论 (0) |
编辑 收藏
我们的项目用到了xmlrpc,不过还是用的2.x版本的。由于xmlrpc3.x地推出。提供了NULL,Serializable等的支持,将原来的Hashtable改成了Map,Vector改成了List。都是不错的进步。所以我们决定从xmlrpc2.x升级到xmlrpc3.x.
在spring里面有几个ServiceExporter,org.springframework.remoting.rmi.RmiServiceExporter、org.springframework.remoting.caucho.HessianServiceExporter、org.springframework.remoting.caucho.BurlapServiceExporter。不过没有xmlrpc的serviceExporter,原来我们是自己封装的XmlRpcServer,用servlet提供服务。(eg:http://localhost:8080/community/service/xmlrpc)没有和spring集成虽然用了spring。
考虑到spring的便利以及配置的同意我决定将xmlrpcService放入spring中。xmlrpc3.x和xmlrpc2.x的代码基本上没有一样的。改了很多东西。除了类型变化之外,还添加了对异常的支持。详细信息请参照xmlrpc3.x源代码。
XmlRpcServiceExporter.java
package
com.jdkcn.xmlrpc;
import
javax.servlet.ServletException;
/**
*
@author
<a href="mailto:rory.cn@gmail.com">somebody</a>
*
@since
2006-9-27 03:59:22 pm
*
@version
$Id XmlRpcServiceExporter.java$
*/
public
class
XmlRpcServiceExporter
extends
RemoteExporter
implements
Controller, InitializingBean {
private
XmlRpcServletServer server;
public
String serviceName;
public
Resource configFile;
public
Boolean enabledForExtensions;
public
void
setEnabledForExtensions(Boolean enabledForExtensions) {
this
.enabledForExtensions
=
enabledForExtensions;
}
public
void
setConfigFile(Resource configFile) {
this
.configFile
=
configFile;
}
public
String getServiceName() {
return
serviceName;
}
public
void
setServiceName(String serviceName) {
this
.serviceName
=
serviceName;
}
public
XmlRpcServletServer getXmlRpcServletServer() {
return
server;
}
/*
(non-Javadoc)
* @see org.springframework.web.servlet.mvc.Controller#handleRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
public
ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws
Exception {
if
(
!
WebContentGenerator.METHOD_POST.equals(request.getMethod())) {
throw
new
ServletException(
"
XmlRpcServiceExporter only supports POST requests
"
);
}
server.execute(request, response);
return
null
;
}
/*
(non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public
void
afterPropertiesSet()
throws
Exception {
server
=
new
XmlRpcServletServer();
server.setHandlerMapping(newXmlRpcHandlerMapping());
if
(enabledForExtensions
!=
null
) {
((XmlRpcServerConfigImpl) server.getConfig()).setEnabledForExtensions(enabledForExtensions.booleanValue());
}
}
/**
Creates a new handler mapping. The default implementation loads
* a property file from the resource
*
*/
protected
XmlRpcHandlerMapping newXmlRpcHandlerMapping()
throws
XmlRpcException {
SpringHandlerMapping mapping
=
new
SpringHandlerMapping(getServiceInterface());
mapping.addHandler(getServiceName(), getServiceInterface());
mapping.setTagetObject(getProxyForService());
return
mapping;
}
}
spring配置文件
<bean id="accountService" class="com.jdkcn.service.impl.AccountServiceImpl">
</bean>
<bean name="rpcAccountService" class="com.jdkcn.xmlrpc.XmlRpcServiceExporter">
<property name="service">
<ref bean="accountService"/>
</property>
<property name="serviceName">
<value>jdkcn.accountService</value>
</property>
<property name="enabledForExtensions">
<value>true</value>
</property>
<property name="serviceInterface">
<value>com.jdkcn.service.AccountService</value>
</property>
</bean> 然后映射一个地址就可以通过xmlrpc访问服务了
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/account">rpcAccountService</prop>
</props>
</property>
</bean>
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring/global.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>service</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>service</servlet-name>
<url-pattern>/service/xmlrpc3/*</url-pattern>
</servlet-mapping>
然后我们的service地址就是这样的http://localhost:8080/service/xmlrpc3/account
希望对大家有用,这里提供project下载。包含一个client程序。com.jdkcn.xmlrpc.Client
点击下载完整代码 除经特别注明外,本文章版权归
莫多泡泡所有.
署名,非商业用途,保持一致. somebody(莫多)
posted @
2006-10-22 16:12 莫多 阅读(2168) |
评论 (0) |
编辑 收藏
不知道大家有没有碰到,还是没有这种需求。就是用like来查询,我们没有用Lucene,Compass这种全文索引的方案,我们只是简单的添加%进行like查询。用户搜索的时候就使用*和?来代表任意和一个。所以要对"%"和"_"进行转义,我们使用的是oracle数据库。sql语句看起来可能是这样的。
SELECT * FROM t_user where nickname like '%Goo\_D' escape '\'
这里对_进行转义了。因为用户昵称包含下划线,如果不进行转义就表示一个任意字符。有时候我们可能还需要对%进行转义。同样的方法在%前加\% 但是比起普通的like语句。多了一个声明转义符的语句。所以我们会想到这样的语句
DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
criteria.add(Restrictions.like("nickname", user.getNickname()+"' escape'\"));
但是这样是不管用的。
接下来可能会想到使用Hibernate3的原生sql查询,其实我们不需要这样做。我们还是使用Criteria条件查询。
criteria.add(Restrictions.sqlRestriction("{alias}.nickname like ? escape'/'", StringUtil.escapeSQLLike(user.getNickname()), Hibernate.STRING));
这样Hibernate产生的语句就是我们想要的语句了。
/**
* 转义like语句中的
* <code>'_'</code><code>'%'</code>
* 将<code>'?'</code>转成sql的<code>'/_'</code>
* 将<code>'%'</code>转成sql的<code>'/%'</code>
* <p>
* 例如搜索<code>?aa*bb?c_d%f</code>将转化成<br/>
* <code>_aa%bb_c/_d/%f</code>
* </p>
* @param likeStr
* @return
* @author <a href="http://jdkcn.com">somebody</a>
*/
public static String escapeSQLLike(String likeStr) {
String str = StringUtils.replace(likeStr, "_", "/_");
str = StringUtils.replace(str, "%", "/%");
str = StringUtils.replace(str, "?", "_");
str = StringUtils.replace(str, "*", "%");
return str;
}
除经特别注明外,本文章版权归
莫多泡泡所有.
署名,非商业用途,保持一致. somebody(莫多)
posted @
2006-10-16 23:29 莫多 阅读(2477) |
评论 (1) |
编辑 收藏
jspark 的这篇文章《开发阶段eclipse下面的spring容器的启动优化 》讲到如何加快spring的启动速度。非常感谢jspark. 一下是引用的原文:
最近在负责一个大项目,项目组成员包括项目经理大概10个人左右。项目技术用struts+spring+hibernate实现。项目的规模相对来说是比较大的,总共有10大模块,每个大模块又分为有十几个、甚至几十个小模块。开发工具用eclipse,由于在开发阶段,项目开发成员需要频繁重启服务器。在启动服务器的时候,每次启动时间总是会超过1分钟。记得以前在做另外一个项目时,启动时间不到5秒钟,相差了10倍,而且项目规模是差不多的。
从初步分析来说,应该是hibernate解释hbm.xml时花费时间,或者可能是spring容器启动并解释所有的bean配置文件。诊断了一下,发现1分钟消耗的时间主要分布在hibernate解释hbm.xml花费5秒;spring容器从启动到解释bean配置文件竟然花了58秒,真是太嚣张了。当时非常怀疑spring的效率问题。企图从网上搜索相关资料,看看有什么优化措施。
首先是找到了hibernate的启动优化 http://www.hibernate.org/194.html 里面的主要思想是通过将xml序列花到本地的文件里,每次读取的时候根据情况,从本地文件读取并反序列化,节省了hibernate xml的解析时间。按照这个方式测试了一下,发现hibernate的启动时间从5秒降低到3秒,但是这个优化对于整个启动过程是杯水车薪的,毫无用处。
没办法,又仔细查看了spring的资料,终于发现spring的容器是提供了lazy-load的,即默认的缺省设置是bean没有lazy- load,该属性处于false状态,这样导致spring在启动过程导致在启动时候,会默认加载整个对象实例图,从初始化ACTION配置、到 service配置到dao配置、乃至到数据库连接、事务等等。这么庞大的规模,难怪spring的启动时间要花将近1分钟。尝试了一下,把beans的 default-lazy-init改为true就,再次启动,速度从原来的55秒,降到8秒钟!!Great!虽然是非常小一个改动,但是影响确实非常大。一个项目组10个人,假若每个人一天平均需要在eclipse下启动测试服务器50次。那么一天项目组需要重启500次,每次节省50秒的话,就是 25000秒,将近几个小时,差不多一个工作日,多么可观的数字!
不过在运行期间第一次点页面的时候,由于spring做了lazy-load,现在就需要启动一部分需要的beans,所以稍微慢2-3秒钟,但是明显比等几十秒要快很多,值得一鉴。
以上是针对开发阶段的spring容器启动优化,在部署到实际环境中,倒是没必要设置为lazy-load。毕竟部署到实际环境中不是经常的事,每次启动1分钟倒不是大问题。
我这里要提醒的是不是说有的beans都能设置default-lazy-init成为true.对于scheduler的bean不能用lazy-init
<
beans
default-lazy-init
="true"
>
<
bean
class
="org.springframework.scheduling.quartz.SchedulerFactoryBean"
>
<
property
name
="triggers"
>
<
list
>
<
ref
bean
="buildHtmlTrigger"
/>
<
ref
bean
="askTrigger"
/>
<
ref
bean
="mailSenderTrigger"
/>
<
ref
bean
="topicDetailBuildTrigger"
/>
<
ref
bean
="forumBuildTrigger"
/>
<
ref
bean
="topicBuildTrigger"
/>
</
list
>
</
property
>
</
bean
>
</
beans
>
这样的话。所有的scheduler就都不管用了。所以请大家要注意。
<
beans
>
<
bean
class
="org.springframework.scheduling.quartz.SchedulerFactoryBean"
>
<
property
name
="triggers"
>
<
list
>
<
ref
bean
="buildHtmlTrigger"
/>
<
ref
bean
="askTrigger"
/>
<
ref
bean
="mailSenderTrigger"
/>
<
ref
bean
="topicDetailBuildTrigger"
/>
<
ref
bean
="forumBuildTrigger"
/>
<
ref
bean
="topicBuildTrigger"
/>
</
list
>
</
property
>
</
bean
>
</
beans
>
posted @
2006-08-10 10:59 莫多 阅读(3304) |
评论 (2) |
编辑 收藏
虽然项目全部采用了UTF-8编码,所有的源文件*.java,*.jsc,*.html,*.ftl都采用了UTF-8编码。可是还是出现了乱码问题。很是不爽,后来找到了tomcat,和resin的配置。
- Tomcat的配置。(conf/server.xml)
<!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->
<Connector port="80" maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true" URIEncoding="UTF-8"/>
- Resin的配置。(conf/resin.conf)
character-encoding | Resin 1.1 |
child of: resin, server, host-default, host, web-app-default, web-app
default: The default value is ISO-8859-1.
Specifies the default character encoding for the environment.
<web-app id='/'>
<character-encoding>shift_jis</character-encoding>
...
</web-app>
|
这个是resin doc里面的我是在web-app-default里面加上了encoding的配置
<
web-app-default
>
<
character-encoding
>
UTF-8
</
character-encoding
>
</
web-app-default
>
希望对你的项目有帮助。
除经特别注明外,本文章版权归莫多泡泡所有.
署名,非商业用途,保持一致. somebody(莫多)
posted @
2006-07-22 18:13 莫多 阅读(1809) |
评论 (4) |
编辑 收藏
项目中多个项目需要同时引用一个静态资源,所以就想到配置不同的虚拟目录指到同一个目录。于是有下面的配置:
<!-- configures the default host, matching any host name -->
<host id="" root-directory=".">
<!--
- configures an explicit root web-app matching the
- webapp's ROOT
-->
<web-app id="/" document-directory="webapps/ROOT"/>
<web-app id="/community/jsvm2" document-directory="D:\\projects\\FelooComponents\\jsvm2"/>
<web-app id="/passport/jsvm2" document-directory="D:\\projects\\FelooComponents\\jsvm2"/>
</host>
但是发现这样只有后面一个管用(http://localhost:8080/passport/jsvm2)这个是可以,可是(http://localhost:8080/community/jsvm2)就不行,很是郁闷。只要后面的document-directory不是同一个目录就成。
后来在resin的doc里面看到path的配置
path-mapping
child of: web-app-default, web-app
Maps url patterns to real paths. If using a server like IIS, you may need to match the server's path aliases.
Attribute | Meaning | default |
---|
url-pattern | A pattern matching the url: /foo/*, /foo, or *.foo |
url-regexp | A regular expression matching the url |
real-path | The prefix of the real path. When used with url-regexp, allows substitution variables like $1. |
<web-app id='/'>
<path-mapping url-pattern='/resin/*'
real-path='e:\resin'/>
<path-mapping url-regexp='/~([^/]*)'
real-path='e:\home$1'/>
</web-app>
|
改正这样的配置就ok了。
<web-app id="/community" document-directory="D:\\projects\\FelooCommunityWeb">
<path-mapping url-pattern='jsvm2/*'
real-path='D:\\projects\\FelooComponents\\jsvm2'/>
</web-app>
<web-app id="/passport" document-directory="D:\\projects\\FelooPassportWeb">
<path-mapping url-pattern='jsvm2/*'
real-path='D:\\projects\\FelooComponents\\jsvm2'/>
</web-app>
除经特别注明外,本文章版权归
莫多泡泡所有.
署名,非商业用途,保持一致. somebody(莫多)
posted @
2006-07-18 19:41 莫多 阅读(2887) |
评论 (0) |
编辑 收藏
自从换工作之后就没有研究过DWR了。下载了最新的DWR2.0M2版本。2.0加了很多东西,也有不少变化的地方。最容易看到的变化就是包名的变化了,由 uk.ltd.getahead 变成了 org.directwebremoting 。
换上了新的配置
<
servlet
>
<
servlet-name
>
dwr-invoker
</
servlet-name
>
<
servlet-class
>
org.directwebremoting.servlet.DwrServlet
</
servlet-class
>
<
init-param
>
<
param-name
>
debug
</
param-name
>
<
param-value
>
true
</
param-value
>
</
init-param
>
<
load-on-startup
>
1
</
load-on-startup
>
</
servlet
>
启动服务,抱错了。
java.lang.IllegalArgumentException: DefaultContainer can't find a classes
at org.directwebremoting.impl.DefaultContainer.getBean(DefaultContainer.java:216)
at org.directwebremoting.annotations.AnnotationsConfigurator.configure(AnnotationsConfigurator.java:50)
at org.directwebremoting.servlet.DwrServlet.init(DwrServlet.java:121)
在DWR的Maillist里面搜索了一下,还有答案,原来DWR2.0 加入了JDK5的注释(annotations).DwrServlet初始化的时候会去检查注释的类,找不到就抱错了。如果你不用annotations也可以忽略掉这个错误。不过看起来总是不爽。有人提出了方案。这样就ok了。
<
servlet
>
<
servlet-name
>
dwr-invoker
</
servlet-name
>
<
servlet-class
>
org.directwebremoting.servlet.DwrServlet
</
servlet-class
>
<
init-param
>
<
param-name
>
debug
</
param-name
>
<
param-value
>
true
</
param-value
>
</
init-param
>
<
init-param
>
<
param-name
>
classes
</
param-name
>
<
param-value
>
java.lang.Object
</
param-value
>
</
init-param
>
<
load-on-startup
>
100
</
load-on-startup
>
</
servlet
>
除经特别注明外,本文章版权归莫多泡泡所有.
署名,非商业用途,保持一致. somebody(莫多)
posted @
2006-07-17 02:11 莫多 阅读(2866) |
评论 (0) |
编辑 收藏
首先感谢JScud提供的好文章。《
使用FreeMarker生成Html静态文件(实例)》
在我们的项目中也用到了Freemarker生成静态文件。不过这里我要说的是编码的问题。我们的项目使用的都是UTF-8编码,我直接使用 飞云小侠 提供的方法生成的文件在UTF-8编码下察看是乱码,而GBK正常(后来发现因为我用的中文操作系统所以用GBK查看正常)。
当然我把Freemarker的配置都改成了UTF-8,我的模版文件也是UTF-8编码的。下面是原来的代码
public void setTemplatePath(Resource templatePath) {
this.templatePath = templatePath;
//设置freemarker的参数
freemarkerCfg = new Configuration();
try {
freemarkerCfg.setDirectoryForTemplateLoading(this.templatePath.getFile());
freemarkerCfg.setObjectWrapper(new DefaultObjectWrapper());
freemarkerCfg.setDefaultEncoding("UTF-8");
} catch (IOException ex) {
throw new SystemException("No Directory found,please check you config.");
}
}
/**
* 生成静态文件
* @param templateFileName 模版名称eg:(biz/order.ftl)
* @param propMap 用于处理模板的属性Object映射
* @param htmlFilePath 要生成的静态文件的路径,相对设置中的根路径,例如 "/biz/2006/5/"
* @param htmlFileName 要生成的文件名,例如 "123.htm"
* @return
*/
private boolean buildHtml(String templateFileName,Map propMap, String htmlFilePath,String htmlFileName){
try {
Template template = freemarkerCfg.getTemplate(templateFileName);
template.setEncoding("UTF-8");
//创建生成文件目录
creatDirs(buildPath.getFilename(),htmlFilePath);
File htmlFile = new File(buildPath + htmlFilePath + htmlFileName);
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(htmlFile)));
template.process(propMap,out);
out.flush();
return true;
} catch (TemplateException ex){
log.error("Build Error"+templateFileName,ex);
return false;
} catch (IOException e) {
log.error("Build Error"+templateFileName,e);
return false;
}
}
下面是修改之后的代码
/**
* 生成静态文件
* @param templateFileName 模版名称eg:(biz/order.ftl)
* @param propMap 用于处理模板的属性Object映射
* @param htmlFilePath 要生成的静态文件的路径,相对设置中的根路径,例如 "/biz/2006/5/"
* @param htmlFileName 要生成的文件名,例如 "123.htm"
* @return
*/
private boolean buildHtml(String templateFileName,Map propMap, String htmlFilePath,String htmlFileName){
try {
Template template = freemarkerCfg.getTemplate(templateFileName);
template.setEncoding("UTF-8");
//创建生成文件目录
creatDirs(buildPath.getFilename(),htmlFilePath);
File htmlFile = new File(buildPath + htmlFilePath + htmlFileName);
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(htmlFile),"UTF-8"));
template.process(propMap,out);
out.flush();
return true;
} catch (TemplateException ex){
log.error("Build Error"+templateFileName,ex);
return false;
} catch (IOException e) {
log.error("Build Error"+templateFileName,e);
return false;
}
}
原因就在于OutputStreamWriter的不同构造方法
OutputStreamWriter(OutputStream out)
创建使用默认字符编码的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName)
创建使用指定字符集的 OutputStreamWriter。
这个是中文JDK的文档说明,刚开始我使用默认的构造函数,所以使用了系统默认的编码,GBK,所以在生成静态文件的时候把UTF-8内容用GBK编码写入了,所以在UTF-8下浏览就有问题。
还有关于修改模版文件同样也要注意这个问题。
public String loadTemplate(String templateName) {
StringBuffer sb = new StringBuffer();
try {
File file = new File(templatePath+"/"+templateName);
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8"));
String line = reader.readLine();
while(line != null) {
sb.append(line);
sb.append("\r\n");
line = reader.readLine();
}
reader.close();
} catch (IOException e) {
throw new SystemException("Loading template Error:",e);
}
return sb.toString();
}
public void saveTemplate(String templateName, String templateContent) {
try {
File file = new File(templatePath + "/" + templateName);
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file),"UTF-8"));
out.write(templateContent);
out.flush();
//扔出templatesave事件
TemplateSaveEvent evt = new TemplateSaveEvent();
evt.setTemplateName(templateName);
dispatchTemplateEvent(evt);
} catch (IOException e) {
throw new SystemException("Write template Error",e);
}
}
|
posted @
2006-06-21 10:46 莫多 阅读(2850) |
评论 (0) |
编辑 收藏
在以前的项目中对于一些资源的配置基本上都是通过spring的IOC注入一个目录的地址字符串。而这样的问题是,对于开发中的团队来说还是很有问题的,因为每个可能都配置一个不同的本地目录,而发布到服务器之后又有不同的目录。这样造成每个人提交了配置文件之后其他人都可能需要修改配置文件才能正确启动服务。这确实很令人烦劳。
最近看《Professional Java Development with the Spring Framework》时看到了spring对底层资源的抽象,才找到了完美解决方案。
原来的代码:
private String templatePath;
public void setTemplatePath(String templatePath) {
this.templatePath = templatePath;
}
public void initListener() {
TemplateEventListener templateListener = new TemplateEventListener(){
public void handleTemplateEvent(TemplateEventSupport evt) {
// 添加事件到队列中
queue.offer(evt);
if(log.isDebugEnabled()){
log.debug("Add Template about:" + evt.getTemplateName());
}
}
};
//注册模版监听事件
templateManager.addEventListener(Constants.TEMPLATE_SAVE_EVENT, templateListener,false);
//设置freemarker的参数
freemarkerCfg = new Configuration();
try {
freemarkerCfg.setDirectoryForTemplateLoading(new File(templatePath));
freemarkerCfg.setObjectWrapper(new DefaultObjectWrapper());
freemarkerCfg.setDefaultEncoding("UTF-8");
} catch (IOException ex) {
throw new SystemException("No Directory found,please check you config.");
}
}
配置文件
<bean id="buildHtmlService" class="cn.jdk.leaf.service.impl.BuildHtmlServiceImpl" init-method="initListener">
<property name="templatePath"><value>${templatePath}</value></property>
</bean>templatePath.path=D:/template
使用spring对底层资源的抽象只要把templatePath改成Resource就可以了
private Resource templatePath;
public void setTemplatePath(Resource templatePath) {
this.templatePath = templatePath;
}
public void initListener() {
TemplateEventListener templateListener = new TemplateEventListener(){
public void handleTemplateEvent(TemplateEventSupport evt) {
// 添加事件到队列中
queue.offer(evt);
if(log.isDebugEnabled()){
log.debug("Add Template about:" + evt.getTemplateName());
}
}
};
//注册模版监听事件
templateManager.addEventListener(Constants.TEMPLATE_SAVE_EVENT, templateListener,false);
//设置freemarker的参数
freemarkerCfg = new Configuration();
try {
freemarkerCfg.setDirectoryForTemplateLoading(templatePath.getFile());
freemarkerCfg.setObjectWrapper(new DefaultObjectWrapper());
freemarkerCfg.setDefaultEncoding("UTF-8");
} catch (IOException ex) {
throw new SystemException("No Directory found,please check you config.");
}
}
bean的配置不变,只要修改properties文件就可以了。
<bean id="buildHtmlService" class="cn.jdk.leaf.service.impl.BuildHtmlServiceImpl" init-method="initListener">
<property name="templatePath"><value>${templatePath}</value></property>
</bean>
把properties文件修改成
templatePath.path=template
在webcontext目录下面建立一个template目录就可以了。在部署到服务器的时候需要部署到一个特定的目录只要修改这个配置文件为
templatePath.path=file:/D:/template
这样就可以了。
| 除经特别注明外,本文章版权归莫多泡泡所有. 署名,非商业用途,保持一致. somebody(莫多) |
posted @
2006-06-11 23:01 莫多 阅读(1854) |
评论 (2) |
编辑 收藏
上篇文章:《
今天发现一个hibernate的bug,或者说一个应该注意的地方比较合适 》里面我提到了Hibernate查询需要注意的一个问题。今天发现了一个最好的解决办法。如果大家现在用Hibernate,相信大家都回用到DetachedCriteria.关于DetachedCriteria查询请查看
http://dev.yesky.com/241/2033241.shtml。
DetachedCriteria给我们的Hibernate查询带来了很多方便,但是如果你带上排序信息就会出现我的上一篇文章里面说的那种错误,今天发现一个很好的解决方法,其实也很简单。就是先把传入的带Order信息的DetachedCriteria去掉order信息查询数据总条数,然后再把Order加回来查询满足条件的对象。通过查看Hibernate的源代码发现Criteria的实现CriteriaImpl发现其实addOrder是给private List orderEntries = new ArrayList();这个List加值。这个List里面放的是OrderEntry对象。这个OrderEntry里面放了一个criteria 和 order.
public PaginationSupport findPageByCriteria(final DetachedCriteria detachedCriteria, final int pageSize, final int startIndex) {
return (PaginationSupport) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
CriteriaImpl impl = (CriteriaImpl) criteria;
List orderEntrys = new ArrayList();
try{
Field field = CriteriaImpl.class.getDeclaredField("orderEntries");
//Get orders
orderEntrys = (List) field.get(impl);
//Remove orders
field.set(criteria,new ArrayList());
}catch(Exception ex){
ex.printStackTrace();
//TODO xxxx
}
int totalCount = ((Integer) criteria.setProjection(Projections.rowCount())
.uniqueResult()).intValue();
criteria.setProjection(null);
try{
Field field = CriteriaImpl.class.getDeclaredField("orderEntries");
//Add orders return
for(int i=0; i<orderEntrys.size(); i++){
List innerOrderEntries = (List) field.get(criteria);
innerOrderEntries.add(orderEntrys.get(i));
}
}catch(Exception ex){
ex.printStackTrace();
//TODO cccc
}
List items = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
PaginationSupport ps = new PaginationSupport(items, totalCount, pageSize,
startIndex);
return ps;
}
}, true);
}
希望大家多多交流
posted @
2006-05-29 23:29 莫多 阅读(4998) |
评论 (12) |
编辑 收藏
现在的系统中虽然使用了。Hibernate但是没有使用Hibernate的关联关系来进行数据库操作。所有的管理操作都是单独实现的。所以也不能用Criteria.add()这种方式去查询关联的一方。所以只能用Native SQL去查询结果返回对象了。按照Hibernate3的reference里面说的
对原生SQL查询执行的控制是通过SQLQuery接口进行的,通过执行Session.createSQLQuery()获取这个接口。最简单的情况下,我们可以采用以下形式:
List cats = sess.createSQLQuery("select * from cats")
.addEntity(Cat.class)
.list();
这个查询指定了:
这里,结果集字段名被假设为与映射文件中指明的字段名相同。对于连接了多个表的查询,这就可能造成问题,因为可能在多个表中出现同样名字的字段。下面的方法就可以避免字段名重复的问题:
List cats = sess.createSQLQuery("select {cat.*} from cats cat")
.addEntity("cat", Cat.class)
.list();
这个查询指定了:
addEntity()方法将SQL表的别名和实体类联系起来,并且确定查询结果集的形态。
addJoin()方法可以被用于载入其他的实体和集合的关联.
List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.addEntity("cat", Cat.class)
.addJoin("kitten", "cat.kittens")
.list();
原生的SQL查询可能返回一个简单的标量值或者一个标量和实体的结合体。
Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
.addScalar("maxWeight", Hibernate.DOUBLE);
.uniqueResult();
除此之外,你还可以在你的hbm文件中描述结果集映射信息,在查询中使用。
List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.setResultSetMapping("catAndKitten")
.list();
后来我使用了命名SQL查询方式。
可以在映射文档中定义查询的名字,然后就可以象调用一个命名的HQL查询一样直接调用命名SQL查询.在这种情况下,我们不 需要调用addEntity()方法.
<sql-query name="persons">
<return alias="person" class="eg.Person"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
</sql-query>
List people = sess.getNamedQuery("persons")
.setString("namePattern", namePattern)
.setMaxResults(50)
.list();
我觉得这种发式比较好。这样写出来的sql可以很整齐。我们的数据库使用的是oracle,不过按照这上面的写法发现sql语句有错误。
后来拿到控制台执行也抱错。因为原来都用sqlserver,而sqlserver都是可以的。后来发现是表不能有别名改成这样就好了。
SELECT T_PAY.sys_id as {pay.sysId},
T_PAY.sys_flag as {pay.sysFlag},
T_PAY.sys_domain as {pay.sysDomain},
T_PAY.sys_owner as {pay.sysOwner},
T_PAY.sys_create_date as {pay.sysCreateDate},
T_PAY.sys_update_date as {pay.sysUpdateDate},
T_PAY.pay_id as {pay.payId},
T_PAY.pay_name as {pay.payName},
T_PAY.pay_type_id as {pay.payTypeId},
T_PAY.pay_date as {pay.payDate},
T_PAY.money_type_id as {pay.moneyTypeId},
T_PAY.amount as {pay.amount},
T_PAY.payer_id as {pay.payerId},
T_PAY.payer_name as {pay.payerName},
T_PAY.accept_id as {pay.acceptId},
T_PAY.accept_name as {pay.acceptName},
T_PAY.pay_state_id as {pay.payStateId},
T_PAY.remark as {pay.remark}
FROM T_PAY
JOIN T_BIZ_PAY
ON T_PAY.pay_id = T_BIZ_PAY.pay_id
WHERE T_BIZ_PAY.biz_id = :bizId
这里要特别的提醒一下大家千万不要把主键忘了。刚开始我就忘了主键,后来调试了半天才找出原因来。
这样在sping里面用回调查询一下就ok了。真的很方便.
public List getPaysByBizId(final String bizId) {
return (List)getHibernateTemplate().execute(new HibernateCallback(){
public Object doInHibernate(Session session)throws HibernateException{
Query query = session.getNamedQuery("find.pays.by.bizid");
query.setParameter("bizId",bizId);
return query.list();
}
},true);
}
posted @
2006-05-26 10:33 莫多 阅读(3777) |
评论 (0) |
编辑 收藏
今天把myblog用sql server部署了一下,可是发现分页查询的时候出现错误,看控制台报错说语句有错,由来发现分页的时候先查询总记录数目的那条语句出错了
select count(*) as y0_ from myblog_Blog this_ inner join myblog_Blog_Category categories3_ on this_.id=categories3_.blogId inner join myblog_Category category1_ on categories3_.categoryId=category1_.id where category1_.id=? order by this_.postTime desc
原来开发的时候我是用的mysql,没有任何问题。原因就在最后面的order by 语句,sql server 在select count(*)里面不能用 order by。然后跟踪代码发现:
public PaginationSupport getBlogsByCategoryByPage(final String categoryId, final int startIndex, final int pageSize) {
return (PaginationSupport) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
Criteria criteria = session.createCriteria(Blog.class);
Criteria cateCriteria = criteria.createCriteria("categories");
cateCriteria.add(Expression.eq("id",categoryId));
criteria.addOrder(Order.desc("postTime"));
int totalCount = ((Integer) criteria.setProjection(Projections.rowCount())
.uniqueResult()).intValue();
criteria.setProjection(null);
List items = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
List blogs = new ArrayList();
for(Iterator ite = items.iterator(); ite.hasNext();) {
Object[] objs = (Object[])ite.next();
blogs.add(objs[1]);
}
PaginationSupport ps = new PaginationSupport(blogs, totalCount, pageSize, startIndex);
return ps;
}
}, true);
}
原来问题就在Criteria.addOrder(Order.desc("postTime"));这句话的位置上面,int totalCount = ((Integer) criteria.setProjection(Projections.rowCount())
.uniqueResult()).intValue();
这句话的时候就会生成上面那句话,如果在这之前addOrder就会出现问题,如果你用mysql不会出现问题,如果你用sql server就会报错。解决方法就是把addOrder语句放到totalCount下面就可以了。
public PaginationSupport getBlogsByCategoryByPage(final String categoryId, final int startIndex, final int pageSize) {
return (PaginationSupport) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
Criteria criteria = session.createCriteria(Blog.class);
Criteria cateCriteria = criteria.createCriteria("categories");
cateCriteria.add(Expression.eq("id",categoryId));
int totalCount = ((Integer) criteria.setProjection(Projections.rowCount())
.uniqueResult()).intValue();
criteria.setProjection(null);
/*
* Fix a bug ,Order must add after get the totalCount,
* beacuse SqlServer not support order by in the select count(*).
*/
criteria.addOrder(Order.desc("postTime"));
List items = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
List blogs = new ArrayList();
for(Iterator ite = items.iterator(); ite.hasNext();) {
Object[] objs = (Object[])ite.next();
blogs.add(objs[1]);
}
PaginationSupport ps = new PaginationSupport(blogs, totalCount, pageSize, startIndex);
return ps;
}
}, true);
}
这样生成的sql语句就是这样的。
select count(*) as y0_ from myblog_Blog this_ inner join myblog_Blog_Category categories3_ on this_.id=categories3_.blogId inner join myblog_Category category1_ on categories3_.categoryId=category1_.id where category1_.id=?
以后大家也要注意了。呵呵。
posted @
2006-05-21 22:49 莫多 阅读(6430) |
评论 (4) |
编辑 收藏
我们的项目比较特殊,基本上每天都要发布一次。为了不影响系统的正常使用,我们的做法是在下班之前把打包好的war放到服务器的一个目录下面(eg:d:\bak)。然后用windows的计划任务在晚上12点自动部署系统,也就是net stop tomcat,删除xx.war,删除xx,然后把d:\bak\xx.war拷贝到webapps下面。然后再net start tomcat。这样在第二天来的上班的时候就可以使用升级过的系统了。也不会影响其他人的使用,不需要中断操作。
关于项目的一个配置我的前几篇文章我也提到过,那个upload的问题就不会发生了。还有就是关于项目开发中的一些问题。比如数据库的连接地址,upload directory的配置等,这些在团队中的每个人都是不一样的,那么在发布的时候都要改成服务器上的正确配置,如果一个月部署一次这到没有什么。但是像我们这样每天部署就不是那么轻松了。而且如果一时不注意很容易造成配置的不正确。所以对于很多配置文件我们都有两份。一份是用户自己开发用的,一份是部署到服务器的配置,这样在开发的时候团队中的每个人都不一样,关于服务器的配置都是一样的,在发布的时候是用的服务器的配置而不是用开发用的配置。eg:web.xml(个人的配置)web-dest.xml 而关键的配置也就是下面这段。
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring/*_context.xml,
classpath:spring/global.xml
</param-value>
</context-param>
web-dest.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring/*_context.xml,
classpath:spring/global-dest.xml,
classpath:spring/scheduler.xml
</param-value>
</context-param>
注意到其中不一样的。dest里少了scheduler.xml这个主要是定时执行的一些任务,一般在开发的时候都不需要,去掉也可以提升速度,也不会出现那么多的log。还有就是发布的web.xml用了global-dest.xml这个是关于spring的总的配置。
global.xml
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>net.sourceforge.jtds.jdbc.Driver</value></property>
<property name="url">
<value>jdbc:jtds:sqlserver://192.168.0.240:1433/fivefortunes;SelectMethod=cursor</value>
</property>
<property name="username"><value>sa</value></property>
<property name="password"><value></value></property>
</bean>
<bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>net.sourceforge.jtds.jdbc.Driver</value></property>
<property name="url">
<value>jdbc:jtds:sqlserver://192.168.0.240:1433/outmail;SelectMethod=cursor</value>
</property>
<property name="username"><value>sa</value></property>
<property name="password"><value></value></property>
</bean>
<bean id="customDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>net.sourceforge.jtds.jdbc.Driver</value></property>
<property name="url">
<value>jdbc:jtds:sqlserver://192.168.0.240:1433/custom;SelectMethod=cursor</value>
</property>
<property name="username"><value>sa</value></property>
<property name="password"><value></value></property>
</bean>
global-dest.xml
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>net.sourceforge.jtds.jdbc.Driver</value></property>
<property name="url">
<value>jdbc:jtds:sqlserver://192.168.0.5:1433/fivefortunes;SelectMethod=cursor</value>
</property>
<property name="username"><value>sa</value></property>
<property name="password"><value></value></property>
</bean>
<bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>net.sourceforge.jtds.jdbc.Driver</value></property>
<property name="url">
<value>jdbc:jtds:sqlserver://192.168.0.5:1433/outmail;SelectMethod=cursor</value>
</property>
<property name="username"><value>sa</value></property>
<property name="password"><value></value></property>
</bean>
<bean id="customDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>net.sourceforge.jtds.jdbc.Driver</value></property>
<property name="url">
<value>jdbc:jtds:sqlserver://192.168.0.5:1433/custom;SelectMethod=cursor</value>
</property>
<property name="username"><value>sa</value></property>
<property name="password"><value></value></property>
</bean>
还有关于一些properties文件我们采用了用ant部署的时候改名字来达到目的。
build.xml
<move file="${dist.dir}/mail.properties" tofile="${dist.dir}/mail-dev.properties" />
<copy file="${dist.dir}/mail-dest.properties" tofile="${dist.dir}/mail.properties" />
<move file="${dist.dir}/upload.properties" tofile="${dist.dir}/upload-dev.properties" />
<copy file="${dist.dir}/upload-dest.properties" tofile="${dist.dir}/upload.properties" />
这个是在war之前做的,大家眼看也就明白吧,就是把xx-dest.properties,改名成xx.propeties。在打完包之后
<move file="${dist.dir}/mail-dev.properties" tofile="${dist.dir}/mail.properties" />
<move file="${dist.dir}/upload-dev.properties" tofile="${dist.dir}/upload.properties" />
这样就达到了我们无忧的目的了。只要每天部署的时候保证程序能正常运行,ant war一下就安心部署吧。
以上就是全部内容,如果有什么不正确欢迎砸板砖。
posted @
2006-04-28 22:27 莫多 阅读(2674) |
评论 (3) |
编辑 收藏
前面一篇文章提到通过webwork的interceptor给webwork的action设置初始值,
jdev说到"为什么要放在这里,而不是放到系统配置文件中,然后在系统设置界面中提供设置",后来我就用spring来注入了。现在就把值放入properties文件中了。
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:mail.properties</value>
<value>classpath:upload.properties</value>
</list>
</property>
</bean>
<!-- 收发外部邮箱 author:somebody-->
<bean id="outMailManager" class="cn.com.fivefortunes.mail.manager.impl.OutMailManagerImpl">
<property name="javaMailSender"><ref bean="javaMailSender"/></property>
<property name="attachementDirectory"><value>${mail.attachementDirectory}</value></property>
<property name="uploadFileDirectory"><value>${upload.directory}</value></property>
<property name="outMailHibernateManager"><ref bean="outMailHibernateManager"/></property>
<property name="outMailAccountManager"><ref bean="outMailAccountManager"/></property>
</bean>
这样把uploadFileDirectory放入mananger中去了。在properties里面配置一下就ok了。
upload.directory=D:\\tomcat5\\webapps\\files
mail.attachementDirectory=D:\\tomcat5\\webapps\\files
上次还有网友这样说到"return ServletActionContext.getServletContext().getRealPath("/");
你这样换了部署环境每次都要改
而且也该是
D://tomcat5//webapps//files"
也许很多人都以为这是最好的方式,可是实际情况不是这样,我们的uploadfile目录已经有3G的文件,而基本上每天我们都要发布新的webapp,如果把uploadfile目录放到webapp的一个子目录,每次部署的时候都要先把uploadfile copy出来然后再部署。所以我们这么做就是把upload目录放到其他目录不影响uploadfile目录里面的文件。关于我们项目的部署见我的下一篇文章。谢谢大家。如果您有什么更好的方法请您指教。
posted @
2006-04-22 11:21 莫多 阅读(1176) |
评论 (0) |
编辑 收藏
刚才在tss上看到Joe发布的消息,DWR允许异步调用Javascript了。DWR 2.0 builds on this to allow you to asynchronously call Javascript code from Java. Reverse Ajax makes writing interactive applications much easier.
值得去看看。
posted @
2006-04-12 09:23 莫多 阅读(306) |
评论 (0) |
编辑 收藏
Rss,Atom,基本上是Web2.0的基本特征,今天终于有时间决定实现一下。对于动态的Rss,Atom其实就是选取最新的内容,迭迨一下生成一个固定格式的xml文件就可以了。其实也很简单。
我用webwork2,用freemarker做模版,在网上找了一个标准的Rss2.0格式就生成我的模版文件rss.ftl
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title><![CDATA[我的小站]]></title>
<link>http://leaf.jdk.cn/</link>
<description><![CDATA[泡出好心情,泡出好技术]]></description>
<language>zh-cn</language>
<copyright><![CDATA[Copyright 2006 PaoPao v4.0]]></copyright>
<webMaster><![CDATA[rory.cn@gmail.com (Rory Ye)]]></webMaster>
<generator>PaoPao v4.0</generator>
<image>
<title>我的小站</title>
<url>http://leaf.jdk.cn/logo.gif</url>
<link>http://leaf.jdk.cn</link>
<description>莫多泡泡2006</description>
</image>
<#list blogs as blog>
<item>
<link>http://leaf.jdk.cn/entry.action?entryId=${blog.id}</link>
<title><![CDATA[${blog.title}]]></title>
<author>${blog.authorMail}(${blog.author})</author>
<category><![CDATA[ ${blog.categories} ]]></category>
<pubDate>${blog.pubDate}</pubDate>
<guid>http://leaf.jdk.cn/entry.action?id=${blog.id}</guid>
<description><![CDATA[${blog.content}]]></description>
</item>
</#list>
</channel>
</rss> 我在rss.action里面去取blogs就可以了。
public String execute() throws Exception {
PaginationSupport ps = blogManager.getBlogsByPage(0);
blogs = new ArrayList();
for(Iterator ite = ps.getItems().iterator(); ite.hasNext();){
Blog blog = (Blog)ite.next();
WrapRssBlog wrapBlog = new WrapRssBlog();
wrapBlog.setId(blog.getId());
wrapBlog.setAuthor(blog.getAuthor().getNickname());
wrapBlog.setAuthorMail(blog.getAuthor().getMail());
wrapBlog.setTitle(blog.getTitle());
wrapBlog.setContent(StringUtil.extractText(blog.getContent()));
wrapBlog.setPubDate(DateUtil.formatRssDate(blog.getPostTime()));
StringBuffer sb = new StringBuffer();
for(Iterator ite2 = blog.getCategories().iterator(); ite2.hasNext();){
Category cate = (Category) ite2.next();
sb.append(" ")
.append(cate.getName());
}
wrapBlog.setCategories(sb.toString());
blogs.add(wrapBlog);
}
return super.execute();
}
这里把blog包装了一下,主要是处理了一下日期,内容,还有把分类拼成字符串。关于日期的处理,参考了飞云小侠写的rss日期的研究。实现代码如下。
public static String formatRssDate(Date date){
SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z",Locale.US);
//TODO TimeZone
SimpleTimeZone zone = new SimpleTimeZone(8,"GMT");
sdf.setTimeZone(zone);
return sdf.format(date);
}
这样就得到了这样的日期
Thu, 6 Apr 2006 16:01:36 GMT
做好了这些,配置一下action
<action name="rss" class="cn.jdk.leaf.action.RssAction">
<result name="success" type="freemarker">/WEB-INF/pages/rss.ftl</result>
</action>
不过像这样配置的话,你访问
http://xxx.xxx.com/rss.action看到的不是一个xml
因为返回结果的contentType是text/html.而不是xml,这样是不行的。看了一下webwork源码。原来可以这样简单的解决。改成这样配置就可以了。
<action name="rss" class="cn.jdk.leaf.action.RssAction">
<result name="success" type="freemarker">
<param name="location">/WEB-INF/pages/rss.ftl</param>
<param name="contentType">application/xml</param>
</result>
</action>
简单吧。其实你还可以把这个contentType改成其他类型的。比如excle的。这样用户执行就可以得到一个xls文件。哈哈。
posted @
2006-04-10 22:40 莫多 阅读(1325) |
评论 (2) |
编辑 收藏
以前一直都没有试过,前几天打算把wordpress换成自己写的程序,就想到了数据的导入和导出,首先想到的是用数据库工具来导。可是觉得有些麻烦,我自己的程序是用spring+hibernate的。后来我就试了一下spring的JdbcTemplate,就和HibernateTemplate一样的好用。首先增加一个连接到wp数据库的dataSource
<bean id="dataSource2" class="org.springframework.jdbc.datasource.DriverManagerDataSource" destroy-method="close">
<property name="driverClassName"><value>org.hibernate.dialect.MySQLDialect</value></property>
<property name="url">
<value>jdbc:mysql://192.168.0.240:3306/wordpressωuseUnicode=true&characterEncoding=utf8</value>
</property>
<property name="username"><value>root</value></property>
<property name="password"><value></value></property>
</bean>
然后在转换程序里面get这个dataSource,new 一个JdbcTemplate(dataSource2),这样就ok了。很简单吧。
public void testCopyData() throws Exception{
DataSource ds = (DataSource)applicationContext.getBean("dataSource2");
CategoryManager cateManager = (CategoryManager) applicationContext.getBean("categoryManager");
JdbcTemplate jt = new JdbcTemplate(ds);
System.out.println("Total posts:"+jt.queryForInt("select count(*) from wp_posts"));
assertNotNull(ds);
List cates = jt.queryForList("select * from wp_categories");
int i= 0;
for(Iterator ite = cates.iterator(); ite.hasNext();){
i++;
Map result = (Map) ite.next();
Category cate = new Category();
cate.setName((String)result.get("cat_name"));
cate.setOrder(i);
if(i==1)
cate.setDefaultCategory(true);
cateManager.saveCategory(cate);
System.out.println("cat_name:"+result.get("cat_name")+"\n");
}
}
posted @
2006-04-07 00:03 莫多 阅读(3578) |
评论 (7) |
编辑 收藏
只是自己的想法,不对不要扔鸡蛋哦。
今天突发奇想的实现一个小小的cache。把分类categories放入map中,cache起来。
private void cache() {
if(log.isDebugEnabled()){
log.debug("Starting cache the categories");
}
cacheCategoryMap = new HashMap();
cacheCategoryMap.put("categories",categoryDao.getCategories());
}
然后我想在interceptor里面把categories写到ognlvaluestack里面这样我在ftl里面就可以<#list categories>....</#list>了。因为这个是在每个页面的header.ftl里面的。我也就不需要再每个action里面去get一下了。
刚开始我implements Interceptor
final OgnlValueStack stack = ActionContext.getContext().getValueStack();
stack.setValue("categories" ,categoryManager.getCategories());
return invocation.invoke();
可是这样也不可以。后来我想到是不是action执行完毕之后就把stack中的这个值清空了我又用了。AroundInterceptor 我想在after里面去设置不就可以了。
protected void after(ActionInvocation dispatcher, String result) throws Exception {
final OgnlValueStack stack = ActionContext.getContext().getValueStack();
stack.setValue("categories" ,categoryManager.getCategories());
}
可是这样还是不可以。我晕了。我想是不是要在action里面声明一下categories。
private List categories;
public List getCategories() {
return categories;
}
public void setCategories(List categories) {
this.categories = categories;
}
然后在before里面去get就可以了。
protected void before(ActionInvocation invocation) throws Exception {
final OgnlValueStack stack = ActionContext.getContext().getValueStack();
stack.setValue("categories" ,categoryManager.getCategories());
}
总算实现了。不过还要在每个action里面声明一下categories,这样还是很不好的。刚才有人建议用filter。我在试试吧.
http://leaf.jdk.cn/index.php/archives/91
posted @
2006-03-21 11:37 莫多 阅读(1122) |
评论 (0) |
编辑 收藏
http://wiki.caucho.com
真是不好意思。我去注册了一下。可能是中文昵称的问题吧。
nickname我用了。中文。
它是用的MediaWiki
http://leaf.jdk.cn/index.php/archives/82
posted @
2006-03-21 10:48 莫多 阅读(288) |
评论 (0) |
编辑 收藏
前两天在做javamail的时候发现原来的上传的action把上传文件的路径写在action里面了。那个目录是服务器上的地址,真bad smell。我就想把它改成可以配置的。我给uploadaction添加了一个string 的字段uploadDirectory。在action的配置文件里面给设置了初始值。
<action name="fileupload" class="cn.com.fivefortunes.upload.FileUpload">
<param name="uploadDirectory">D:\tomcat5\webapps\files</param> <!–D:\temp –>
<result name="success" type="freemarker">/upload/upload.ftl</result>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="params"/>
</action>
可是这样我还是不可以debug的时候发现uploadDirectory是null。不知道怎么回事情了。就到
http://wiki.opensymphony.com 来search了一下,没想到真有。
How do I get static parameters into my action这个就是我的问题。原来是要加上一个inteceptor。
<action name="fileupload" class="cn.com.fivefortunes.upload.FileUpload">
<param name="uploadDirectory">D:\tomcat5\webapps\files</param><!– D:\temp –>
<result name="success" type="freemarker">/upload/upload.ftl</result>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="params"/>
<interceptor-ref name="static-params"/>
</action>
这样就可以了。不过还是觉得不怎么爽。发布的时候还是要记得修改那个xwork的配置文件。
posted @
2006-03-16 15:26 莫多 阅读(1019) |
评论 (4) |
编辑 收藏