[Miles' Blog]

Welcome 2 My Planet

Available categories: [/] [development. ~]

jdk1.5/1.6下xslt时xalan版本问题|xalan version problem in XSLT under jdk1.5(jdk5) & jdk1.6(jdk6) [Permalink]

Sat Aug 11 13:16:38 CST 2007
Category [working on downwap.]

在jdk1.5/1.6下,现有的xslt会失败。

错误类型TransformerConfigurationException,此外还会有org.jdom.IllegalAddException:A DocType cannot be added after the root element
原因:新版jre中的xalan做了更新,产生了兼容性问题。
解决办法:打包时包括jdom1.0/lib下的xalan.jar。

Posted by: miles
Comments [0] |

Tomcat 2 Weblogic Migration | Tomcat到Weblogic移植 [Permalink]

Sat Sep 24 10:41:04 CST 2005
Category [working on downwap.]

出于性能、扩展性等等一大堆理由,downwap工程由tomcat移植到了weblogic81sp2上。

当初就有考虑到会有这一天,所以开始设计的时候就尽量不依赖容器。如今终于尝到了标准化的甜头。 /images/emoticons/grin.gif

不过即使如此,问题也不少。罗列一下:

  • 以前用的jwml=>jsp servlet映射在weblogic中当然不认。因为tomcat有定义jsp这个servlet-name,而weblogic没有。结果没有想到本以为很简单的后缀映射在weblogic中确很不容易实现,找到的资料大多以6.x之前版本为主,那时候还没到config.xml的年代。惹不起躲得起,干脆改回jsp后缀了事。
  • 原来改了jsp远不是问题的结束。对*.jsp做filter映射是没有问题,可是在filter中通过ResponseWrapper锁定的contentType没了作用。没时间研究原因,直接开头加上DI,用@page设置了contentType。感觉宛如吃苍蝇,最后干脆去掉了所有jsp,统统由XSLT转换。还是那个优先级的理念:可维护性>性能。
  • 同理,Wrapper强制200也没起作用,处理同上。
  • weblogic81自带的JRockit1.4.1类库没带SHA256。换JRockit1.4.2解决。

移植完毕以后做了压力测试,性能的确好不少。不过要说原因,估计和Tomcat的JK、JRockit的调整、代码整理都有关系。倒不一定是AppServer在这个级别应用上的差距体现。

Posted by: miles
Comments [0] |

去掉了service和data access层的类中所有的非static成员变量 [Permalink]

Wed Dec 22 19:09:54 CST 2004
Category [working on downwap.]

主要针对pageCount这个property。很多牵扯分页的类都有他。以前通过

String xml=service.getXxxByYyy();
int pageCount=service.getPageCount();
这样方式读取页数,而现在则是通过标准化返回的xml,让他本身包含pageCount这个ExtInfo。

这样一来,就可以大量应用singleton模式了。毕竟对于目前系统,频繁的new是不可忽视的性能瓶颈之一。

Posted by: miles
Comments [0] |

关于servlet的线程模式 [Permalink]

Mon Nov 29 10:25:06 CST 2004
Category [working on downwap.]

昨天做refactoring时候差点犯错误。因为目前的设计,是所有servlet继承公共基类,而基类中进行一些例如错误处理之类的系统级操作(参看这里),所以两者都要用到request/response。以前的做法,是从基类传入request/response,而在子类中调用各个方法时候,也是统统传入。

本来这样做是没什么问题的,可是昨天却怎么看都不舒服。到处都是HttpServletRequest request HttpServletResponse response,看着都烦。结果可想而知:在基类中弄一个private HttpServletRequest requestprivate HttpServletResponse response,一进入主方法先就设置成员变量,以后就再也不用传递request/response了。

看上去完美了很多,可是仔细一想,如果这样能行,为什么容器还要传递这些玩意??一定有问题!!这时候突然想到了SingleThreadModal这个接口(真的是突然阿。。 /images/emoticons/plain.gif )。。恍然大悟。servlet是单实例多线程的,如果这样做,岂不是会有同步冲突??晕死。。联想到我的SessionListener:

public class SessionListener extends BaseListener implements 
HttpSessionListener, ServletRequestListener, Constant {

    private HttpServletRequest request;

    //Notification that a session was created
    public void sessionCreated(HttpSessionEvent se) {
        logger.debug("New session started!");
        setEquipmentInfo(se.getSession());
        clearAllAntiAttackRecs();
    }

    private void setEquipmentInfo(HttpSession session) {
        WebStringUtil webStringUtil=WebStringUtil.getInstance();
        String agent=webStringUtil.getUserAgent(request);

        String info=null;
        EquipmentService service=EquipmentServiceFactory.getService();
        try {
            info=service.getUserEquipmentByAgent(agent);
        } catch( DidyException ex) {
            logger.warn(ex.getMessage());
        }

        UserTracker userTracker=UserTracker.getInstance();
        userTracker.setEquipmentInfo(session, info);
    }

    //Notification that a session was invalidated
    public void sessionDestroyed(HttpSessionEvent se) {
        logger.debug("Session ended!");
    }

    private void clearAllAntiAttackRecs() {
        Map actAntiAttackRecs=BaseDidyServlet.getActAntiAttackRecs();
        Iterator it=actAntiAttackRecs.keySet().iterator();

        while(it.hasNext()) {
            clearAntiAttackRecs(actAntiAttackRecs, (String) it.next());
        }
    }

    private void clearAntiAttackRecs(Map actAntiAttackRecs, String ip) {
        NumberUtil numberUtil=NumberUtil.getInstance();
        Map ipAntiAttackMap=(Map) actAntiAttackRecs.get(ip);

        if(ipAntiAttackMap == null) {
            return;
        }

        Set obsoleteKeys=new HashSet();
        Iterator it=ipAntiAttackMap.keySet().iterator();
        while(it.hasNext()) {
            String key=it.next().toString();
            Map myIpActAntiAttackMap=(Map) ipAntiAttackMap.get(key);

            long lastActTime=
              numberUtil.getLong(myIpActAntiAttackMap.get("TIME"));
            long minInterval=
              numberUtil.getLong(
                AppProp.read(ANTI_ATTACK_PREFIX_INTERVAL + "." + key)
              );

            if(System.currentTimeMillis() - lastActTime >= minInterval) {
                obsoleteKeys.add(key);
            }
        }

        it=obsoleteKeys.iterator();
        while(it.hasNext()) {
            ipAntiAttackMap.remove(it.next());
        }

        if(ipAntiAttackMap.isEmpty()) {
            actAntiAttackRecs.remove(ip);
        }
    }

    /** The request is about to go out of scope of the web application. */
    public void requestDestroyed(ServletRequestEvent sre) {
    }

    /** The request is about to come into scope of the web application. */
    public void requestInitialized(ServletRequestEvent sre) {
        request=(HttpServletRequest) sre.getServletRequest();
    }
}
这里就麻烦大了!上网找了资料,还是没有找到合适的。不行的话只能试试看加一个overall的filter来为session listener取得request了。UGLY啊!!

Posted by: miles
Comments [0] |

xslt实现select distinct [Permalink]

Thu Nov 25 11:51:55 CST 2004
Category [working on downwap.]

今天做显示支持的手机列表时候遇到一个技术问题,就是select distinct。目前状况是,一次选出所有的equipment纪录,每条记录带有自己的brand(品牌)信息。选出纪录可能是这样格式:

<ROW>
  <ID>1</ID>
  <BRAND>NOKIA</BRAND>
  <MODEL>6100</MODEL>
</ROW>
<ROW>
  <ID>2</ID>
  <BRAND>NOKIA</BRAND>
  <MODEL>7250</MODEL>
</ROW>
<ROW>
  <ID>3</ID>
  <BRAND>NOKIA</BRAND>
  <MODEL>8210</MODEL>
</ROW>
<ROW>
  <ID>4</ID>
  <BRAND>MOTO</BRAND>
  <MODEL>E365</MODEL>
</ROW>
<ROW>
  <ID>5</ID>
  <BRAND>NOKIA</BRAND>
  <MODEL>V3</MODEL>
</ROW>
...
这样的数据,我们需要组织成:
  1. 主card只显示brand,并提供连接到每个brand的model列表card;
  2. model列表card有多个,对应不同品牌。每个card显示所有该品牌的model。
问题很明白,就是需要用distinct来分类组织,让数据从2d变3d。用xslt/xpath2.0或者EXSL.NET十分方便,本身就有distinct功能;但是用xslt/xpath1.0就不那么容易了。下面就是最终使用的xsl文件:
<?xml version="1.0" encoding="gb2312"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="wml" omit-xml-declaration="no" indent="no" 
      doctype-public="-//WAPFORUM//DTD WML 1.1//EN" 
      doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml" />

  <xsl:include href="../common/common.func.xsl"/>
  <xsl:include href="../common/header.inc.xsl"/>
  <xsl:include href="../common/footer.inc.xsl"/>

  <xsl:variable name="pn" select="/ROOT/EXTINFO/PAGENO"/>

  <xsl:template match="/">
    <wml>
      <card>
        <xsl:call-template name="inc.header"/>
        <p>
          <xsl:for-each select="/ROOT/DATA/ROW">
            <xsl:variable name="thisEm" select="EM_ID"/>

            <xsl:if test="count(preceding-sibling::ROW)=0">
              <a>
              <xsl:attribute name="href">#<xsl:value-of select="EM_ID"/></xsl:attribute>
                <xsl:value-of select="BRAND"/></a><br/>
            </xsl:if>

            <xsl:if test="preceding-sibling::ROW[1]/EM_ID != $thisEm">
              <a>
              <xsl:attribute name="href">#<xsl:value-of select="EM_ID"/></xsl:attribute>
              <xsl:value-of select="BRAND"/></a><br/>
            </xsl:if>
          </xsl:for-each>
        </p>
        <xsl:call-template name="inc.footer"/>
      </card>

      <xsl:for-each select="/ROOT/DATA/ROW">
        <xsl:variable name="thisEm" select="EM_ID"/>
        <xsl:if test="count(preceding-sibling::ROW)=0">
          <card>
          <xsl:attribute name="id"><xsl:value-of select="EM_ID"/></xsl:attribute>
            <xsl:call-template name="inc.header"/>
            <p align="center">品牌[<xsl:value-of select="BRAND"/>]的支持机型</p>
            <p>
              <xsl:for-each select="../ROW[EM_ID=$thisEm]">
               型号:<xsl:value-of select="MODEL"/><br/>
               描述:<xsl:value-of select="DESCRIPTION"/><br/>
              </xsl:for-each>
            </p>
            <xsl:call-template name="inc.footer"/>
          </card>
        </xsl:if>

        <xsl:if test="preceding-sibling::ROW[1]/EM_ID != $thisEm">
          <card>
            <xsl:attribute name="id"><xsl:value-of select="EM_ID"/></xsl:attribute>
            <xsl:call-template name="inc.header"/>
            <p align="center">品牌[<xsl:value-of select="BRAND"/>]的支持机型</p>
            <p>
              <xsl:for-each select="../ROW[EM_ID=$thisEm]">
               型号:<xsl:value-of select="MODEL"/><br/>
               描述:<xsl:value-of select="DESCRIPTION"/><br/>
              </xsl:for-each>
            </p>
            <xsl:call-template name="inc.footer"/>
          </card>
        </xsl:if>
      </xsl:for-each>
    </wml>
  </xsl:template>

</xsl:stylesheet>
绕着弯子实现简单功能,是xslt一大特点。 enjoy /images/emoticons/plain.gif

Posted by: miles
Comments [0] |

关于指定资源下载的控制 [Permalink]

Thu Nov 25 00:33:32 CST 2004
Category [working on downwap.]

因为kjava要求文件格式的特殊性,最终设计决定采用如下方案:

  1. 所有的权限控制都由filter处理,这样可以最大限度保证灵活性。实现上,所有的资源都会过filter。实际控制上,对于wml页面,用/*.jwml /*.kwml /*.wml来区分不同状态用户所能访问的页面;对于servlet,则通过他们共同父类,来进行精确到动作的访问控制(参看下面那个关于糟糕的'AOP'的文章)。
  2. 对于客户端,kjava游戏需要的两个文件(*.jad和*.jar)将会在逻辑上处于一个目录下。比如:
    http://wap.didy.net/game/1.jad
    http://wap.didy.net/game/1.jar
    实现上,/game/*映射到一个servlet,通过path_info取得后面的/1.jar部分,然后在里面进行资源访问的逻辑细节,包括扣点,记录流水,以及读取指定资源,并用stream写到客户端。path_info是一个不错的东西,利用它可以作出灵活的应用。只要可以自由定制uri mapping的技术就能用它。当然,asp就做不到。

    这里有一个细节,就是为了提供高可维护性、灵活性和扩展性,jad和jar文件(文件名不限)存储时候,只需要放入一个zip文件,而且jad文件的MIDlet-Jar-URL字段也不需要绝对路径。具体下载时候,程序会寻找正确的zip,解压,并且处理后交给客户端。将来对于图片铃声也类似处理。这样控制力很强,可以实现防止盗链、给图片打戳等高级功能。

附上kjava的jad和jar文件contenttype(mimetype):
    private static final String CONTENT_TYPE_DOWN_JAD="text/vnd.sun.j2me.app-descriptor";
    private static final String CONTENT_TYPE_DOWN_JAR="application/java-archive";

Posted by: miles
Comments [0] |

didy|架构 [Permalink]

Thu Nov 25 00:21:24 CST 2004
Category [working on downwap.]

这段时间又开始开发wap.didy.net,一个付费游戏下载的站点。当然,nexus架构还是沿用,只不过改进更多了 /images/emoticons/happy.gif

这两天任务比较紧,架构方面的变动也比较多,有点over-design的嫌疑。。过段时间一定要静心总结一下。

这两天刚好看到了关于IoC,AOP,Dependency Injection的一些文章,有点感觉了。像是对自己某些工作的总结。。。目前的Servlet部分设计,是依靠继承来扩展功能的。比如,BaseServlet:

public final void doGet(...) throws ... {
    //...common functionality (ASPECT?)...
    process();
    //...common functionality (ASPECT?)...
}
子类: NexusBaseServlet:
public class NexusBaseServlet extends BaseServlet {
    public void process() throws Exception {
       //... bl goes here....
    }
}
这样的设计,子类的限制太多,而且实际上已经完全没有了upper cast的可能。所以,可以认为这个继承关系仅仅是多余的提高了耦合度。这里就很有改进的必要,比如用decorator或者是AOP的理念,就可以很清楚的分理出子类,实现解耦。
正如xp理念,这次开发refactoring贯穿始终,而且也初步确定了设计到测试的基本流程。希望有时间细化他,精益求精。

Posted by: miles
Comments [0] |

关于wml中的response status code [Permalink]

Thu Nov 25 00:07:59 CST 2004
Category [working on downwap.]

没错,就是那个200/404/500。对于一般的浏览器,如果返回的status code不是200(HttpServletResponse.SC_OK),完全没有影响,会显示服务器定制的错误页面。比如:
http://2yup.com/blah 这就是我们通常所见的定制http error page了。

可是对于大多wml浏览器来说,可能是为了节省流量,只要分析到status code,就不会在去找页面,而是使用浏览器的默认错误页。这样适用于大多情况,可是,在我的应用中,我是利用500错误(Internal Server Error)来做统一的错误显示页面的。通过这种方法,只需要简单的抛出Exception,就能够方便的显示错误信息。这样就比较麻烦了--我的所有错误信息页面通通完蛋。

还好我们有filter可以用。对于wml应用,我都需要返回我的定制错误,所以,之前加入的WmlMimeFilter刚好满足条件。只需要在WmlMimeServletResponseWrapper里面,重载setStatus方法,就万无一失了:

    public void setStatus(int code) {
        super.setStatus(HttpServletResponse.SC_OK);
    }
结果正如所愿,丢失的错误信息全部回来了 /images/emoticons/grin.gif

附:一个完整的错误返回:
处理前 ==============
HTTP/1.x 500 Internal Server Error
Content-Type: text/vnd.wap.wml
Content-Length: 695
Date: Wed, 24 Nov 2004 16:14:13 GMT
Server: Apache-Coyote/1.1
Connection: close

处理后 ==============
HTTP/1.x 200 OK
Content-Type: text/vnd.wap.wml
Content-Length: 695
Date: Wed, 24 Nov 2004 16:19:02 GMT
Server: Apache-Coyote/1.1

Posted by: miles
Comments [0] |

关于DOCTYPE [Permalink]

Wed Oct 27 09:57:21 CST 2004
Category [working on downwap.]

如果doc类型是xml的话,不要在xslt时候设置doctype,否则可能会因为root element和doctype不匹配导致错误。因为xml本身就是默认的doctype,实在没有必要多此一举。

Posted by: miles
Comments [0] |

wap通讯录 [Permalink]

Wed Sep 29 11:44:00 CST 2004
Category [working on downwap.]

现在在做通讯录。目前的设计,通讯录不但要求像现在的名片式手机电话簿一样有自定义分组,详细联系人资料,多组电话号码这些基本功能,还需要以下的进阶功能:

  1. 允许用户在电脑上直接管理电话簿
  2. 允许用户上传联系人,以及分组头像
  3. 允许用户直接从电话簿拨号(wap1.2支持)
  4. 允许用户直接从电话簿发送email
  5. 允许用户发送联系人名片给其他注册用户

这些功能都是一般的电话簿所没有的。应该会比较有吸引力 /images/emoticons/wink.gif 而且以后再也不用担心手机或手机卡丢失导致重要电话号码一起遗失了。

Posted by: miles
Comments [0] |

?
十二月 2018
Sun? Mon? Tue? Wed? Thu? Fri? Sat?
      1
2345678
9101112131415
16171819202122
23242526272829
3031     
<  Nov???Today??? Jan  >
<< <   1 2 3   > >>

Available categories: [/] [development. ~]

Powered By blojsom?? RSS Feed? RSS2 Feed? RDF Feed

html hits:?39862