<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>刘小天的Sense</title>
    <description>黄昏时看到的朦胧的红色的天空
让一切不回家的人们如此的激动
红色的阳光照着人们走来走去
远处传来模模糊糊的歌</description>
    <link>http://eai.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>软件研发的四个技术方向</title>
        <author>tian_cookie</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://eai.javaeye.com">tian_cookie</a>&nbsp;
          链接：<a href="http://eai.javaeye.com/blog/58033" style="color:red;">http://eai.javaeye.com/blog/58033</a>&nbsp;
          发表时间: 2007年03月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>1、以消息中间件为基础的数据集成平台。企业应用集成（EAI）是企业软件发展的必经阶段，一直以来很多公司都在以原始的、分散的、偶然的方式做着业务系统之间的集成工作，随着企业应用数量的增加、技术的多样化和复杂化，加之EAI理念不断深化和EAI产品的大力推广，大中型企业必将采取一种先进的、全面的、持续不断站在整个公司战略和流程层面考虑的应用整合平台。数据集成做为EAI的第一层和基础层是进行应用整合必不可少的工作。</p>
<p>2、以Ajax＋SOA构建的新型业务系统。系统后台采用SOA理念对业务系统的功能和流程进行抽象，形成不同层次能完成特定任务的标准服务提供出来。前台展现层采用Ajax技术直接调用、访问后台的服务，使B/S结构重新回归到类似于Delphi、PB时代的三层C/S结构。（后台借用SOA的思想来抽象出服务，但从性能的角度考虑，不一定采用SOAP和Web Service的标准协议做为前后台的传输协议）</p>
<p>3、以开源Portal服务器和Portlet技术为主线的用户界面集成。单一入口、通用外观、单点登陆、个性化定制、公共安全服务外加一个内容管理系统，这些特性对于企业用户来说还是很有吸引力，使得Portal本身具有良好的市场前景，而且对于企业或者政府领导来说这些都是实实在在看得见的业绩，跟一味鼓吹后台应用整合，架构调整（SOA）相比，用Portal做为切入点，再进行应用整合是一种很好的方式。</p>
<p>4、动态语言和敏捷开发。以Ruby on Rails和Django为代表的敏捷开发框架以及随之而来的极限编程理念必将引起整个软件行业的一次变革，并且这场变革现在已经初露端倪，如何把握这一契机，迎接变革，抓住机会，抢占新的市场是摆在每个软件公司面前的问题。动态语言和敏捷开发最大的好处在于大大的缩短软件开发周期和降低需求变更对项目造成的影响，让软件开发者能够根据客户的要求&ldquo;随需而变&rdquo;，但就现阶段来说要普及和推广还有比较大的难度，在开发工具、协作方式、服务支持上还有一些问题，如何与传统软件工程相结合，如何适应企业应用的要求都是需要探索的。目前，我们要保持一定的技术能力，在小型业务系统进行积极实践和创新，引导客户，不断积累，一旦市场成熟就能够全面出击。</p>
          <br/>
          <span style="color:red;">
            <a href="http://eai.javaeye.com/blog/58033#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 07 Mar 2007 18:53:00 +0800</pubDate>
        <link>http://eai.javaeye.com/blog/58033</link>
        <guid>http://eai.javaeye.com/blog/58033</guid>
      </item>
      <item>
        <title>做顾问的四句话</title>
        <author>tian_cookie</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://eai.javaeye.com">tian_cookie</a>&nbsp;
          链接：<a href="http://eai.javaeye.com/blog/37277" style="color:red;">http://eai.javaeye.com/blog/37277</a>&nbsp;
          发表时间: 2007年01月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><strong>顾问的首要工作：满足客户的求知欲</strong></p>
<p>如果你花了高价钱请了个家庭厨师，当然他要能烧出合你口味的菜，但是如果他不能解答你关于烧菜的各种古怪的问题，满足你关于其他口味菜肴的好奇心，那么你一样会怀疑高价花的是否值。</p>
<p><strong>所有人都知道答案，但没有人知道问题</strong></p>
<p>顾问通过合适的方法找到问题，答案往往是客户自己想出来的。</p>
<p><strong>如果你不能让客户信任你，那么项目就失败了</strong></p>
<p>只有相信你才会相信你的成果，而让客户产生信任感的往往不是你的专业知识。顾问的交付品是文档和Presentation，信就一字千金，不信就一文不值。</p>
<p><strong>你想到的客户未必没想到，关键是怎么表达</strong></p>
<p>形式比内容重要，表达比想法重要，在靠嘴为生的行业这是条铁律。</p>
          <br/>
          <span style="color:red;">
            <a href="http://eai.javaeye.com/blog/37277#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 10 Jan 2007 11:38:00 +0800</pubDate>
        <link>http://eai.javaeye.com/blog/37277</link>
        <guid>http://eai.javaeye.com/blog/37277</guid>
      </item>
      <item>
        <title>售前的长相</title>
        <author>tian_cookie</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://eai.javaeye.com">tian_cookie</a>&nbsp;
          链接：<a href="http://eai.javaeye.com/blog/34407" style="color:red;">http://eai.javaeye.com/blog/34407</a>&nbsp;
          发表时间: 2006年11月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          　　如果说一个IT公司除了前台对长相有要求外还有别的职位，那么就是售前了，第一次因为一个人长的太异外而拒绝录用，心里有种说不出来的感觉，TMD什么世道。后来也就习惯了，看人先看长相，据说售前的长相代表了公司的形象和实力，直接影响售前做Presentation的效果。<br />　　当然也不是完全的以貌取人，只要五官端正，四肢健全都有机会，只是在同等条件下长的帅可以加分。如果是销售就完全不同了，特别是男销售，长的太帅的一律不要，其貌不扬的优先考虑，最好是看上去忠厚老实又其貌不扬同时又能说会道。<br />　　凡事不能绝对，但说到以貌取人，中国是有传统的，看看《三国演义》的描写，刘备就是个有气质的大帅哥，每到一个地方，别人一看他的长相和仪表就相信他说的自己是“帝室之胄”的话。刘备卖了这么些年的草鞋，特别是在年轻的时候，举止习惯正在形成，说他仪表如何如何像个人物我是不太相信的，那么只有一种解释了，长的帅，干什么人家都看着顺眼。四十几岁的老男人了（古代人平均寿岁大概只有五十岁，到了三十几就自称老夫了），还能拐跑孙权那个颇有男子气概的妹妹，为什么？刘备有才，还是有财？好像都不是，如果一定要说理由，长的帅估计是很大的原因了。
          <br/>
          <span style="color:red;">
            <a href="http://eai.javaeye.com/blog/34407#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 15 Nov 2006 17:55:20 +0800</pubDate>
        <link>http://eai.javaeye.com/blog/34407</link>
        <guid>http://eai.javaeye.com/blog/34407</guid>
      </item>
      <item>
        <title>webMethods IS源代码分析——启动类</title>
        <author>tian_cookie</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://eai.javaeye.com">tian_cookie</a>&nbsp;
          链接：<a href="http://eai.javaeye.com/blog/33147" style="color:red;">http://eai.javaeye.com/blog/33147</a>&nbsp;
          发表时间: 2006年11月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          webMethods IS中负责服务器启动的类有四个：执行入口类com.wm.app.b2b.server.Main、服务器初始化类com.wm.app.b2b.server.Server、以及两个辅助类com.wm.util.UniqueApp、com.wm.util.AppRegistry。<br /><br />让我们先来看看Main的main以及注释：<br /><pre name="code" class="java">
public static void main(String argv[])
    {
        int port = 0;
        String serviceName = null;
        //处理在启动脚本中传入的几个参数
        for(int i = 0; i &lt; argv.length; i++)
        {
            if(argv[i].equals("-port") && i &lt; argv.length - 1)
                port = Integer.valueOf(argv[1 + i++]).intValue();
            if(argv[i].equals("-home") && i &lt; argv.length - 1)
                Config.setProperty("watt.server.homeDir", argv[1 + i++]);
            if(argv[i].equals("-service") && i &lt; argv.length - 1)
                serviceName = argv[1 + i++];
        }
        
        if(port != 0)
            Config.setProperty("watt.server.port", Integer.toString(port));
        UniqueApp ua = null;
        int uaport = 4321;
        //获得一个叫uaport的端口参数，默认是4321
　　  String uapstr = System.getProperty("watt.server.uaport");
        if(uapstr != null)
            try
            {
                uaport = Integer.parseInt(uapstr);
            }
            catch(NumberFormatException _ex) { }

        //用这个uaport创建一个app
        ua = new UniqueApp(uaport);
        try
        {
            ua.start();
            //调用Server这个类的start方法初始化IS
            Server.start(argv);
            ua.quit();
            if(Server.restart())
            {
                try
                {
                    if(serviceName != null)
                    {
                        String homepath = System.getProperty("watt.server.homeDir", ".");
                        String cmd = homepath + File.separator + "bin" + File.separator + "RestartService.exe " + serviceName;
                        Runtime.getRuntime().exec(cmd);
                    }
                }
                catch(Exception e)
                {
                    e.printStackTrace();
                }
                System.exit(42);
            } else
            {
                System.exit(0);
            }
        }
        catch(com.wm.util.UniqueApp.DeniedException _ex)
        {
            System.out.println("Server cannot execute. A server with this configuration is already running.");
            System.exit(0);
        }
    }
</pre><br />看到这里大家可能会产生疑问，这个UniqueApp的实例的ua到底有什么用呢，为什么要在Server初始化之前先start它，初始化之后又quit呢？要解答这些问题让我们先看看这个UniqueApp究竟做了什么事：<br /><br />UniqueApp类的start方法、send方法和runRegistry方法：<br /><pre name="code" class="java">
public void start()
        throws DeniedException
    {
        if(port_ > lastPort_)
            debugPrint("UNQ  ERROR: port_ (" + port_ + ") > lastPort_ (" + lastPort_ + ")");
        for(; port_ &lt;= lastPort_; port_++)
        {
            debugPrint("UNQ  attempting connection on port " + port_);
            try
            {
                debugPrint("UNQ  " + iden_ + " sending initial message");
                String response = send("init");
                debugPrint("UNQ  " + iden_ + " got response " + response);
                if(response == null)
                {
                    debugPrint("UNQ  connected on port " + port_ + ", but no response");
                    continue;
                }
                if(response.equals("DENIED"))
                    throw new DeniedException();
                if(response.equals("OK"))
                {
                    runHeartbeat();
                    break;
                }
                debugPrint("UNQ  connected on port " + port_ + ", but incomprehensible response");
                continue;
            }
            catch(IOException ioe)
            {
                if(DEBUG)
                    ioe.printStackTrace(System.out);
                runRegistry();
            }
            break;
        }

    }

public String send(String type)
        throws IOException
    {
        Socket socket = new Socket("localhost", port_);
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        String msg = type + ": " + iden_;
        out.println(msg);
        out.flush();
        return in.readLine();
    }

public void runRegistry()
    {
        debugPrint("UNQ  " + iden_ + " starting registry on port " + port_);
        registry_ = new AppRegistry(iden_, port_);
    }

</pre><br />这里可以很清楚的看到，UniqueApp是一个socket的client端，它的作用就是用一些特定的原语连接localhost上的uaport端口，如果这个端口没有响应或者响应的不对，它就“new AppRegistry(iden_, port_)”。<br />AppRegistry是一个实现了Runnable接口的类，它又做了什么呢？请看：<br /><br />AppRegistry类的构造方法、run方法、openSocket方法以及解析原语的processLineReceived方法：<br /><br /><pre name="code" class="java">
public AppRegistry(String iden, int port)
    {
        info_ = new HashMap();
        out_ = null;
        socket_ = null;
        info_.put(iden, new Long(0x7fffffffffffffffL));
        port_ = port;
        Thread t = new Thread(this);
        t.setDaemon(true);
        t.start();
    }

public void run()
    {
        debugPrint("REG  running");
        openSocket();
        if(socket_ == null)
            debugPrint("REG  no socket");
        else
            while(socket_ != null) 
                processConnection();
        debugPrint("REG  stopped");
    }

protected void openSocket()
    {
        try
        {
            debugPrint("REG  running socket server on port " + port_);
            socket_ = new ServerSocket(port_);
            socket_.setSoTimeout(3000);
        }
        catch(IOException ioe)
        {
            if(DEBUG)
                ioe.printStackTrace(System.out);
            debugPrint("REG  failed to open socket");
            socket_ = null;
        }
    }

protected void processLineReceived(String line)
    {
        debugPrint("REG  processLineReceived(" + line + ")");
        int pos = line != null ? line.indexOf(": ") : -1;
        if(pos != -1)
        {
            String type = line.substring(0, pos);
            String identifier = line.substring(pos + 2);
            if(type.equals("init"))
                handleInit(identifier);
            else
            if(type.equals("update"))
                update(identifier);
            else
            if(type.equals("quit"))
                quit(identifier);
            else
                debugPrint("REG  unknown type: '" + type + "' in line: '" + line + "'");
        }
    }

</pre><br />由此看出AppRegistry是一个单线程的Socket Server。虽然到了这里我们都知道了UniqueApp和AppRegistry的作用，但是疑惑仍然没有解答，为什么要在服务器启动之前先连接或者启动一个Socket Server呢？<br /><br />要得到答案，让我们来看看Server初化始化的时候究竟做了些什么：<br /><br />Server类的start方法：<br /><br /><pre name="code" class="java">
public static void start(String args[])
    {
        gServer = new Server(args);
        gServer.start();
        try
        {
            gServer.join();
        }
        catch(Exception _ex) { }
    }

</pre><br />这里要说明的是Server类是Thread类的子类，它的run方法如下：<br /><pre name="code" class="java">
public void run()
    {
        try
        {
            long start = System.currentTimeMillis();
            com.wm.util.Config.setProperty("watt.server", "true");
            gRestart = false;
            gInShutdown = false;
            gListeners = new Values();
            gResources = new Resources(System.getProperty("watt.server.homeDir", "."), true);
            gConfFile = gResources.getConfigFile("server.cnf");
            gResources.getLogJobsInDir();
            gResources.getLogJobsOutDir();
            gResources.getDatastoreDir();
            loadConfiguration();
            Scheduler.init();
            String tmp = com.wm.util.Config.getProperty("false", "watt.server.control.runMemorySensor");
            if(Boolean.valueOf(tmp).booleanValue())
                MemorySensor.init(ServerController.getInstance());
            ThreadPoolSensor.init(ServerController.getInstance());
            checkProperties();
            com.wm.util.Config.processCmdLine(args);
            setupLogging();
            JournalLog.init(args);
            JournalLogger.init(JournalLog.newProducer(), JournalLog.getHandler());
            JournalLogger.logCritical(1, 25, Build.getVersion(), Build.getBuild());
            if(!LicenseManager.init())
            {
                JournalLogger.logCritical(1, 14);
                return;
            }
            RepositoryManager.init();
            JDBCConnectionManager.init();
            AuditLogManager.init();
            setupIPRules();
            UserManager.init();
            ACLManager.init();
            ThreadManager.init();
            StateManager.init();
            ListenerAdmin.init();
            ServiceManager.init();
            InvokeManager.init();
            Statistics.init();
            NetURLConnection.init();
            ContentManager.init();
            CacheManager.init();
            EventManager.init();
            boolean dispatcherCorrectlyInit = false;
            try
            {
                DispatchFacade.init();
                dispatcherCorrectlyInit = true;
            }
            catch(Exception e)
            {
                JournalLogger.logCritical(31, 25, e);
            }
            WebContainer.init();
            ISMonEvtMgr.create();
            PackageManager.init();
            DependencyManager dm = NSDependencyManager.current();
            if(dm == null || !dm.isEnabled())
                JournalLogger.logDebugPlus(3, 15, 25);
            else
                JournalLogger.logDebugPlus(3, 14, 25);
            try
            {
                if(dispatcherCorrectlyInit)
                    DispatchFacade.start();
            }
            catch(Exception ce)
            {
                JournalLogger.logError(35, 25, ce);
            }
            PortManager.init();
            MimeTypes.init();
            HTTPDispatch.init();
            ProxyHTTPDispatch.init();
            LBHTTPDispatch.init();
            CacheManager.startSweeper();
            Document.setHostServices(new ServerXmlHostServices());
            try
            {
                ClusterManager.init();
            }
            catch(Exception e)
            {
                JournalLogger.logDebug(103, 33, e.getMessage());
            }
            try
            {
                String jobDir = com.wm.util.Config.getProperty("logs/jobsout", "watt.tx.jobdir");
                TContext.init(jobDir);
            }
            catch(ServiceException e)
            {
                JournalLogger.logDebugPlus(3, 9998, 36, e);
            }
            if(Config.isSSLPresent())
                try
                {
                    Class c = Class.forName("com.wm.app.b2b.server.ServerTrustDeciderManager");
                    TrustDeciderManager tdm = (TrustDeciderManager)c.newInstance();
                    TrustManager.setManager(tdm);
                }
                catch(Throwable _ex) { }
            if(!ListenerAdmin.isReady() && !gCanListen)
            {
                JournalLogger.logCritical(4, 14);
                return;
            }
            Scheduler.scheduleTask("Key Update", new KeyUpdate(), 0L, 0x5265c00L);
            String sc = com.wm.util.Config.getProperty("true", "watt.server.saveConfigFiles");
            if((new Boolean(sc)).booleanValue())
                saveConfigFiles();
            if(Configurator.isBrokerConfigured())
                SyncManager.init();
            gRunning = true;
            long startTimeSeconds = (System.currentTimeMillis() - start) / 1000L;
            JournalLogger.logCritical(2, 14, Long.toString(startTimeSeconds));
            synchronized(this)
            {
                try
                {
                    wait();
                }
                catch(Exception e)
                {
                    e.printStackTrace();
                }
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        JournalLog.unInit();
    }

</pre><br />上面的代码不用多解释，大家可以看到IS启动做了哪些事情，IS分为哪些模块，以及这些模块的加载顺序。<br />IS启动的操作很多，启动时间也比较长，而且有“PortManager.init()”、“saveConfigFiles()”这样需要独占运行的端口占用和文件操作，如果是在同一个JVM上我们可以用Synchronized，如果是不到的JVM呢？答案揭晓了，UniqueApp的作用是使用端口占用的方式在IS启动阶段实现互斥，即对于同一个启动文件夹下的配置一台机器只能有一个IS在启动，否则会报出“Server cannot execute. A server with this configuration is already running.”的错误。
          <br/>
          <span style="color:red;">
            <a href="http://eai.javaeye.com/blog/33147#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 08 Nov 2006 10:17:08 +0800</pubDate>
        <link>http://eai.javaeye.com/blog/33147</link>
        <guid>http://eai.javaeye.com/blog/33147</guid>
      </item>
      <item>
        <title>webMethods IS源代码分析——ClassLoader</title>
        <author>tian_cookie</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://eai.javaeye.com">tian_cookie</a>&nbsp;
          链接：<a href="http://eai.javaeye.com/blog/32878" style="color:red;">http://eai.javaeye.com/blog/32878</a>&nbsp;
          发表时间: 2006年11月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          源起：开发了一个Package，这个Package用到了一个框架，该框架封装了log4J的日志功能，log4J初始化需要加载classpath下的log4j.properties，将这个properties文件到Package的code/classes下，或者config下均无法找到。<br /><br />限制：不能跨越框架的封装直接调用log4J的初始化类来初始化log4J。<br /><br />版本：webMethods IS 6.1, log4J 1.2.8<br /><br />log4J在第一次调用LogManager的时候最始化，代码：<br /><pre name="code" class="java">
if(configurationOptionStr == null) {  
  url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
  if(url == null) {
    url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
  }
      } else {
  try {
    url = new URL(configurationOptionStr);
  } catch (MalformedURLException ex) {
    // so, resource is not a URL:
    // attempt to get the resource from the class path
    url = Loader.getResource(configurationOptionStr); 
  }  
}
</pre><br />这里的Loader是helpers下面的Loader类，getResource方法的代码如下：<br /><pre name="code" class="java">
static public URL getResource(String resource) {
    ClassLoader classLoader = null;
    URL url = null;
    
    try {
    if(!java1) {
      classLoader = getTCL();
      if(classLoader != null) {
        LogLog.debug("Trying to find ["+resource+"] using context classloader "
         +classLoader+".");
        url = classLoader.getResource(resource);      
        if(url != null) {
          return url;
        }
      }
    }
    
    // We could not find resource. Ler us now try with the
    // classloader that loaded this class.
    classLoader = Loader.class.getClassLoader(); 
    if(classLoader != null) {
      LogLog.debug("Trying to find ["+resource+"] using "+classLoader
             +" class loader.");
      url = classLoader.getResource(resource);
      if(url != null) {
        return url;
      }
    }
    } catch(Throwable t) {
    LogLog.warn(TSTR, t);
    }
    
    // Last ditch attempt: get the resource from the class path. It
    // may be the case that clazz was loaded by the Extentsion class
    // loader which the parent of the system class loader. Hence the
    // code below.
    LogLog.debug("Trying to find ["+resource+
         "] using ClassLoader.getSystemResource().");
    return ClassLoader.getSystemResource(resource);
  } 
</pre><br />大家可以看到，classLoader = Loader.class.getClassLoader(); 实际上log4J是调用当前类的ClassLoader来装载这个properties文件的。特别注意的是它使用的是getResource()方法，该方法返回一个URL类型的对象。<br /><br />接下来看webMethods的ClassLoarder，webMethods IS有一个Server的ClassLoader，每一个Package有一个自己的ClassLoader，值得注意的是Package的ClassLoader与整个Server的ClassLoader没有继承关系，它们都是com.wm.app.b2b.server.ServerClassLoader的实例，这个类直接继承自JDK的ClassLoader，但是覆盖了父类的getResourceAsStream方法，代码如下：<br /><pre name="code" class="java">
public InputStream getResourceAsStream(String name)
    {
        for(int i = 0; i &lt; resdirs.size(); i++)
        {
            File rfile = new File((File)resdirs.elementAt(i), name);
            if(rfile.exists())
                try
                {
                    return new FileInputStream(rfile);
                }
                catch(IOException _ex)
                {
                    JournalLogger.log(27, 28, name);
                }
        }

        for(int i = 0; i &lt; jars.size(); i++)
            try
            {
                ZipFile zfile = new ZipFile((File)jars.elementAt(i));
                ZipEntry ze = zfile.getEntry(name);
                if(ze != null)
                {
                    InputStream is = null;
                    is = zfile.getInputStream(ze);
                    return is;
                }
            }
            catch(Exception _ex)
            {
                JournalLogger.log(26, 28, name);
            }

        for(int i = 0; i &lt; cldirs.size(); i++)
        {
            File cfile = new File((File)cldirs.elementAt(i), name);
            if(cfile.exists())
                try
                {
                    return new FileInputStream(cfile);
                }
                catch(IOException _ex)
                {
                    JournalLogger.log(27, 28, name);
                }
        }

        return null;
    }
</pre><br />其中变量“resdirs”，“jars”，“cldirs”都是Vertor，通过下面的方法最始初化：<br /><pre name="code" class="java">
public static void addPackage(String pkgName)
        throws IOException, ZipException
    {
        Resources res = Server.getResources();
        ServerClassLoader scl = new ServerClassLoader(pkgName);
        scl.addJar(new File(res.getPackageCodeDir(pkgName), "classes.zip"));
        scl.addJarDir(res.getPackageJarsDir(pkgName));
        scl.addClassDir(res.getPackageClassDir(pkgName));
        scl.addResourceDir(res.getPackageResourceDir(pkgName));
        scl.addResourceDir(res.getDeprecatedPackageResourceDir(pkgName));
        pkgs.put(pkgName, scl);
    }

private void addResourceDir(File dir)
    {
        if(dir != null || dir.isDirectory())
            resdirs.addElement(dir);
    }
</pre><br />查看Resource类的代码发现放在Package的code/classes下，code/jars下，config下和lib下的文件都可以通getResourceAsStream这个方法取到。不知道什么原因，webMethods实现的ServerClassLoader并没有覆盖getResource方法，调用这个方法实际调用的是其父类即JDK的ClassLoader的方法。<br /><br />问题就出在这里，当我在Package里用log4J的时候，log4J的实现是用getResource方法的，因此放在Package的资源文件下的properties文件无法被找到，只能将这个文件放到整个Server级别的classpath下。<br /><br />影响：整个webMethods IS如果有多个包用自己的log4J，但却只能有一个log4J的配置。即使设置log4j.configuration这个属性，也只能有一个。
          <br/>
          <span style="color:red;">
            <a href="http://eai.javaeye.com/blog/32878#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 07 Nov 2006 10:38:03 +0800</pubDate>
        <link>http://eai.javaeye.com/blog/32878</link>
        <guid>http://eai.javaeye.com/blog/32878</guid>
      </item>
  </channel>
</rss>