Denny 的个人资料Java Dreams日志列表 工具 帮助

Denny Denny

职业
兴趣
No impossible

Java Dreams

Java Dreams
12月22日

Tapestry之Cache组件

有许多页面的一部分或者这个页面是很少更新的,他们通常是由外部文件来生成这个部分。所以我们可以把这部分内容cache住,当有新的请求时,我们就response cache,这样可以减少服务器的负担,还可以提高性能。其中oscache已经可以实现页面的cache和页面部分cache。oscache使用jsp tags来实现局部cache的。拿到Tapestry中肯定是行不通的。在同事的提醒下,想到写这个Tapestry的cache组件来达到重用的目的。

 

说干就干,先在头脑中想好要怎样使用cache(页面上的布局)。ok。 我想好了。

<span jwcid="@Cache" cacheProvider="ognl:cacheProvider" updateCondition="ognl:needUpdate">

    //Cache body, which is the content you want to cache.

</span>

这里有2个参数,updateCondition 当为true时,我们就绕过cache, cacheProvider 我把他定义为一个接口,这样用户可以把cache存在任何地方。而且提供这样的一个接口,用户可以更好的操作cache。先看看jwc对cache组件的定义。

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE component-specification PUBLIC
"-//Apache Software Foundation//Tapestry Specification 4.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd">

<component-specification allow-body="yes" allow-informal-parameters="no" class="com.live.spaces.dengyin2000.tapestry.tfancomponents.components.Cache">
   <description>
   Cache component, this component can inclue any content as its body, and cache its body.
    This is useful in rarely updated content.
    </description>
<parameter name="updateCondition" required="no" default-value="false">
    <description>
     The flag that need to refresh cache, it would casue tapestry render not use the cache.
    </description>
</parameter>
<parameter name="cacheProvider" required="yes">
     <description>
     You need to provider an cache provider to store its body content. for some simply use.
    Please see     @com.live.spaces.dengyin2000.tapestry.tfancomponents.components.SimpleHtmlSourceCacheProvider
    </description>
</parameter>
</component-specification>

下面的是ICacheProvider接口

 

public interface ICacheProvider {
/**
*
* @param cacheKey
* @param cacheContent
*/
public void storeCache(String cacheKey, String cacheContent);
/**
*
* @param cacheKey
* @return
*/
public String getCacheContent(String cacheKey);
/**
* This method provider to user, so that user can controll cache manaully.
* @param cacheKey
*/
public void removeCache(String cacheKey);
/**
* This method provider to user, so that user can controll cache manaully.
* Clear all caches
*
*/
public void reset();
}

ok。 再来看看Cache组件的代码。

 

public abstract class Cache extends AbstractComponent {
protected static final Log logger = LogFactory.getLog(Cache.class);
public abstract boolean getUpdateCondition();
public abstract ICacheProvider getCacheProvider();
@Override
protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) {
if (getUpdateCondition()){
renderComponentWithCache(writer, cycle);
}else{
if (getCacheProvider().getCacheContent(this.getIdPath()) != null){
//response cache html content.
writer.printRaw(getCacheProvider().getCacheContent(this.getIdPath()));
}else{
renderComponentWithCache(writer, cycle);
}
}

}
private void renderComponentWithCache(IMarkupWriter writer, IRequestCycle cycle) {

logger.debug("We need to refresh cache now.");
CacheWriterWrapper cacheWrapper = new CacheWriterWrapper();
super.renderBody(buildCacheMarkupWriter(cacheWrapper.getPrintWriter(), writer), cycle);
String cacheContent = cacheWrapper.getCacheContent();
logger.debug("fetched cache content, ready to put it into cache.");

getCacheProvider().storeCache(this.getIdPath(), cacheContent);
// response html content.
writer.printRaw(cacheContent);
}
private IMarkupWriter buildCacheMarkupWriter(PrintWriter writer, IMarkupWriter sourceWriter){
return this.getPage().getRequestCycle().getInfrastructure().getMarkupWriterSource().newMarkupWriter(writer, new ContentType(sourceWriter.getContentType()));
}
class CacheWriterWrapper{
private StringWriter sw;
private PrintWriter pw;
public CacheWriterWrapper() {
sw = new StringWriter();
pw = new PrintWriter(sw);
}

public String getCacheContent(){
return sw.getBuffer().toString();
}
public PrintWriter getPrintWriter(){
return pw;
}
}
}

主要是得到cache组件body的内容,然后把body的内容cache住,下次的话就response Cache的内容。 其实也是满简单的。

我自己还写了一个简单CacheProvider。

 

public class SimpleHtmlSourceCacheProvider implements ICacheProvider {
private Map<String, String> cache = new HashMap<String, String>();
public String getCacheContent(String cacheKey) {
return cache.get(cacheKey);
}

public void storeCache(String cacheKey, String cacheContent) {
cache.put(cacheKey, cacheContent);
}

public void removeCache(String cacheKey) {
cache.remove(cacheKey);
}

public void reset() {
cache.clear();
}
}

在使用中你可以把CacheProvider放到Global或者Visit对象中。注意要使用同一个CacheProvider。

我在google code host上面建了一个probject地址是http://code.google.com/p/tfancomponents/ 有兴趣的同学可以看看, 这是一个maven项目。

11月29日

提高eclipse在windows下的性能

在windows中,假如程序处理一定的状态下,会把memory放到paging里面。这样就降低了性能,但提高了memory的利用率。

打开windows注册表, 找到 HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\DisablePagingExecutive  值, 然后把这个值1。 这样应用程序就会一直使用物理内存了。

详细请看:http://technet2.microsoft.com/WindowsServer/en/library/3d3b3c16-c901-46de-8485-166a819af3ad1033.mspx?mfr=true
11月24日

让GWT的FlexTable支持双击(double click)

Each event has a set of browser events that fires it.  For example take

a look at the source of the HTMLTable Class in the gwt-user jar.  It
has the following constructor:

  public HTMLTable() {
    tableElem = DOM.createTable();
    bodyElem = DOM.createTBody();
    DOM.appendChild(tableElem, bodyElem);
    setElement(tableElem);
    sinkEvents(Event.ONCLICK);
  }

Basically, it is "sinking" only the ONCLICK event, and will ignore all
other events.   To 'sink' and event just means to react to any firings
of that event.  A full list of the various events are listed here:

http://code.google.com/webtoolkit/documentation/com.google.gwt.user.c...

So, for example, if you wanted the table to respond to ONDBLCLICK
events, all you need to do is extend the class of the table you want to

use (let's say FlexTable), so you would make the class:

public class HappyTable extends FlexTable {
        public HappyTable(){
                super();
                addTableListener(this);
                sinkEvents(Event.ONCLICK | Event.ONDBLCLICK |
Event.ONKEYDOWN);
        }
...

}

What this code is doing, is telling the table to react to click, double

click, and key press events.  In the same code you would create an
onBrowser event method which would define what you want to do:

public void onBrowserEvent(Event event) {
                // Find out which cell was actually clicked.
                Element td = getEventTargetCell(event);
                if (td == null) {
                        return;
                }
                Element tr = DOM.getParent(td);
                Element body = DOM.getParent(tr);
                int row = DOM.getChildIndex(body, tr);
                int column = DOM.getChildIndex(tr, td);
                switch (DOM.eventGetType(event)) {
                case Event.ONCLICK: {
                        if (this.tableListeners != null) {
                                // Fire the event.
                                tableListeners.fireCellClicked(this,
row, column);
                        }
                        break;

                }
                case Event.ONDBLCLICK: {
                        if (this.tableListeners != null) {
                                // Fire the event.
                                tableListeners.fireCellDblClicked(this,
row, column);
                        }
                        break;
                }
                case Event.ONKEYDOWN: {
                        if (DOM.eventGetKeyCode(event) == KEY_UP) {
                                // Fire the event.
                                tableListeners.fireUpKey(this);
                        }
                        else if (DOM.eventGetKeyCode(event) ==
KEY_DOWN) {
                                // Fire the event.
                                tableListeners.fireDownKey(this);
                        }
                        break;
                }
                default: {
                        // Do nothing
                }

                }
        }

Because I was doing a variety of things with the table I created my own

listener interface, and listener collection which included double
clicks, but this is not necessary for simpler things.  Here it is
anyway (note that this implements all of clicks, dblclicks, and up/down

keys):

public interface HappyTableListener extends TableListener{

        public void onCellDblClicked(SourcesTableEvents sender, int
row, int
cell);
        public void onUpKey(SourcesTableEvents sender);
        public void onDownKey(SourcesTableEvents sender);

}

public class MyTableListenerCollection extends Vector {

        public void fireCellClicked(SourcesTableEvents sender, int row,
int
cell) {
                for (Iterator it = iterator(); it.hasNext();) {
                        MyTableListener listener = (MyTableListener)
it.next();
                        listener.onCellClicked(sender, row, cell);
                }
        }

        public void fireCellDblClicked(SourcesTableEvents sender, int
row, int
cell) {
                for (Iterator it = iterator(); it.hasNext();) {
                        MyTableListener listener = (MyTableListener)
it.next();
                        listener.onCellDblClicked(sender, row, cell);
                }
        }

        public void fireUpKey(SourcesTableEvents sender) {
                for (Iterator it = iterator(); it.hasNext();) {
                        MyTableListener listener = (MyTableListener)
it.next();
                        listener.onUpKey(sender);
                }
        }
        public void fireDownKey(SourcesTableEvents sender) {
                for (Iterator it = iterator(); it.hasNext();) {
                        MyTableListener listener = (MyTableListener)
it.next();
                        listener.onDownKey(sender);
                }
        }

详细请看:http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/861b1cb22c8423c3/fcaf84884d8d3d13?lnk=gst&q=table+row+double+click&rnum=1#fcaf84884d8d3d13

11月15日

Dojo TreeV3使用详解

Dojo TreeV3使用详解

                                              作者:邓胤(deng.yin@gmail.com)

    工作中使用了TreeV3这个dojo的组件,感觉基本上已经是把dojo的treeV3使用到了极点(自定义树的外观,树节点的拖拉,把其他Drag Source拖拉到树中),公司也要求我作个treeV3演说。所以特意把这篇文章写下来。

    首先先对treeV3做一个简单的介绍。TreeV3.js, TreeNodeV3.js, .TreeBasicControllerV3.js, TreeContextMenuV3.js,TreeDndControllerV3.js,TreeEditor.js, 主要的就是这些js一起协作。有兴趣的可以看看源码, 其实dojo的代码看起来还算是瞒简单的。下面我就用几个例子来说明怎样扩展或自定义TreeV3。本篇文章是增对有一定dojo知识的读者,如果你现在还不了解dojo,请看
http://dojotoolkit.org/.

    1.  为叶子节点增加图标。
       IconTree.html
      
<div dojoType="TreeV3" listeners="controller">
    <div dojoType="TreeNodeV3" title="Item 1" widgetId="1">
        <div dojoType="TreeNodeV3" childIconSrc="images/page.gif" title="Item 1.1" widgetId="Item 1.1"></div>
        <div dojoType="TreeNodeV3" childIconSrc="images/page.gif" title="Item 1.2" widgetId="Item 1.2"></div>
    </div>

    <div dojoType="TreeNodeV3" title="Item 2" widgetId="Item 2">
        <div dojoType="TreeNodeV3" childIconSrc="images/page.gif" title="Item 2.1" widgetId="Item 2.1"></div>
    </div>

    <div dojoType="TreeNodeV3" title="Empty Folder" isFolder="true" widgetId="Item 3"></div>
    <div dojoType="TreeNodeV3" childIconSrc="images/page.gif" title="Item 4" widgetId="Item 4"></div>
</div>
    我们在每个TreeNode上加上childIconSrc属性。请看icontree.js怎样让这个属性生效的。
    icontree.js
dojo.lang.extend(dojo.widget.TreeNodeV3, {
    childIconsrc: "",
    childIcon: null,
    iconNode:null,
    titleNode:null,
    postCreate: function(args) {
    //build icon
        if (args["childIconsrc"]) {
            this.childIconsrc = args["childIconsrc"];
        }
        this.childIcon = document.createElement("img");
        if (this.childIconsrc != "") {
            this.childIcon.src = this.childIconsrc;
            this.childIcon.setAttribute("width", "16");
            this.childIcon.setAttribute("height", "16");
        }
        this.iconNode = this.tree.labelNodeTemplate.cloneNode(true);
        this.titleNode = this.tree.labelNodeTemplate.cloneNode(true);
        if (!this.isFolder){
            //clear treenode, if it is not folder node
            this.labelNode.innerHTML="";
        }
        this.titleNode.innerHTML=this.title;
        this.iconNode.appendChild(this.childIcon);
        // add icon first, then add title
        this.labelNode.appendChild(this.iconNode);
        this.labelNode.appendChild(this.titleNode);       
    }
});
我们扩展了TreeNodeV3这个组件的postCreate方法(dojo组件都有这个方法,他相当于构造方法吧,我们可以在这里做必要的初始化),这里做的事应该比较清楚,我们通过childIconsrc这个属性创建了iconNode, 然后清空TreeNodeV3的labelNode。再增加iconNode和titleNode

2. 拖动物体到tree中
dragother.html
<div dojoType="TreeV3" DndMode="between;onto" DndAcceptTypes="firstTree;feed" widgetId="firstTree" listeners="basicController;dndcontroller;selector;disableWrap" toggle="fade" showGrid="false" showRootGrid="true">
    <div dojoType="TreeNodeV3" title="Item 1" widgetId="1">
        <div dojoType="TreeNodeV3"  title="Item 1.1" widgetId="Item 1.1"></div>
        <div dojoType="TreeNodeV3" title="Item 1.2" widgetId="Item 1.2"></div>
    </div>

    <div dojoType="TreeNodeV3" title="Item 2" widgetId="Item 2">
        <div dojoType="TreeNodeV3" title="Item 2.1" widgetId="Item 2.1"></div>
    </div>

    <div dojoType="TreeNodeV3" title="Empty Folder" isFolder="true" widgetId="Item 3"></div>
    <div dojoType="TreeNodeV3" title="Item 4" widgetId="Item 4"></div>
</div>

<br/>

<span id="feed_0">Drag me0 to tree.</span><br/>
<span id="feed_1">Drag me1 to tree.</span><br/>
<span id="feed_2">Drag me2 to tree.</span><br/>
<span>You can't drag me.</span>
注意黑体的DndAcceptTypes属性。说明这个树接受dragSource type为firstTree或者feed的(也就是自己,还有就是下面的span, 我们需要把span变成可以拖动的物体 dragSource)。再看dragother.js。
    dojo.addOnLoad(function(){
           var num = 0;
           while(dojo.byId("feed_" + num) != null){
               var dropSource = new dojo.dnd.HtmlDragSource(dojo.byId("feed_" + num), "feed");
               num++;
        }   
    });

dojo.dnd.TreeDropTargetV3.prototype.oldGetDropHandler = dojo.dnd.TreeDropTargetV3.prototype.getDropHandler;
dojo.lang.extend(dojo.dnd.TreeDropTargetV3, {
   
    getDropHandler: function(e, source, targetParent, targetIndex) {
        if (source.type == "feed"){
            var sourceNode = source.domNode;
            if (dojo.widget.byId(sourceNode.id) != null){
                alert("this widget has already been here.");
                return function(){
                    return false;
                }
            }            
            var node = createTreeNode(sourceNode.id, sourceNode.innerHTML);
            targetParent.addChild(node, targetIndex);
            return function(){
                return true;
            }
        }
        return this.oldGetDropHandler(e, source, targetParent, targetIndex);
    }   
});   

function createTreeNode(widgetId, title) {
    return dojo.widget.createWidget("TreeNodeV3",
    {
        widgetId: widgetId,                                                   
        title: title,
        isFolder: false,
        tree: 'firstTree'
    });
}

第一段是让那些span成为dragsource。第二段我们hack了dojo.dnd.TreeDropTargetV3的 getDropHandler方法(具体可以看看TreeDndControllerV3.js)。当我们接收到dragSources时我们获取那些 span中的一些信息,然后create 一个treeNode,在把这个treeNode加到树中。

3.   ...................................................
4.   ...................................................
5.   ...................................................

还有一些hack treeV3的东西实在不愿写。 dojo的treeV3真的是蛮强大的(仔细看看那几个**Controller和TreeV3 TreeNodeV3的源码), 而且dojo的widget源码还是蛮容易理解的。有兴趣的朋友可以看看dojo的源码。还有就是dojo包里面tests目录下面的例子。大多数应用的话看看里面的例子也就行了。

例 子的源码可以在http://deng.yin.googlepages.com/TreeV3Sample.zip下载。本例子中没有包括 dojo0.4源码,用户可以到http://download.dojotoolkit.org/release-0.4.0/dojo-0.4.0- ajax.zip下载源码, 然后解压到例子中的dojo目录。

格式不好的话可以看http://docs.google.com/View?docid=ajgd3tgv958v_3gz374h

HttpURLConnection 访问页面时发生403问题。

工作中发现一个问题。当我用HttpURLConnection去连接读取一个网站时,老是会发生这个403错误。这个引起了IOException,但是我用firefox访问这个网站时就没问题。 google后知道了答案。原来如果用java代码HttpURLConnection去连的话 http header 中的User-Agent就为空,解决方法就是在连接之前先设置这个属性。
URL myUrl = new URL(searchURL);
URLConnection myConn = (HttpURLConnection)myUrl.openConnection();
myConn.setRequestProperty("User-agent","Mozilla/4.0");
BufferedReader br = new BufferedReader(new InputStreamReader(myConn.getInputStream()));

那台Server上要这么做, 可能是要组织一些网络机器人的访问(不过感觉不是很有用,用上面的方法就能破了)。 其实实现感觉也很简单, 加上一个Filter,判断如果request.getHeader("User-agent")为空的话,然后再response一个403 status就行。