<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    stone2083

    #

    讓log4j支持占位符

    目標:讓log4j.xml配置文件中允許使用占位符(${key}).

    使用場景:
    在運行期決定一些動態的配置內容.
    比如在我們項目中,希望一臺物理機同一個應用跑多個實例.
    因為多進程操作同一份log文件存在并發問題(打印,DailyRolling等),所以我希望配置如下:${loggingRoot}/${instance}/project.log
    在運行腳本中,通過加入-Dinstance=instance1參數,來動態指定實例名.讓同一份應用在不同的運行實例下,日志打印到不同的路徑

    Log4j分析:
    我以為,Log4j天生就支持占位符的.請見:org.apache.log4j.helpers.OptionConverter.substVars(String val, Properties props)就有對占位符的處理.
    org.apache.log4j.PropertyConfigurator (log4j.properties文件解析).默認就支持對占位符的處理.
    org.apache.log4j.xml.DOMConfigurator挺怪異的.明明也有對占位符的處理.但是我們就是無法對其屬性props進行賦值.
    (當然,有可能是我誤解了其props的用法--還沒有完整讀過他的源碼)

    處理方案:
    繼承org.apache.log4j.xml.DOMConfigurator,實現自己的DOMConfigurator.
    public class PlaceHolderDOMConfigurator extends org.apache.log4j.xml.DOMConfigurator {

        
    private Properties props;

        
    public PlaceHolderDOMConfigurator(Properties props){
            
    this.props = props;
        }

        
    public static void configure(String filename, Properties props) {
            
    new PlaceHolderDOMConfigurator(props).doConfigure(filename, LogManager.getLoggerRepository());
        }

        //主要是覆寫這個方案.傳入properties對象
        
    protected String subst(String value) {
            
    try {
                
    return OptionConverter.substVars(value, props);
            } 
    catch (IllegalArgumentException e) {
                LogLog.warn(
    "Could not perform variable substitution.", e);
                
    return value;
            }
        }
    }

    測試代碼:
    log4j.xml片段:
    <appender name="PROJECT" class="org.apache.log4j.DailyRollingFileAppender">
            
    <param name="file" value="${loggingRoot}/${instance}/project.log"/>
            
    <param name="append" value="false"/>
            
    <param name="encoding" value="GB2312"/>
            
    <param name="threshold" value="info"/>
            
    <layout class="org.apache.log4j.PatternLayout">
                
    <param name="ConversionPattern" value="%d [%X{requestURIWithQueryString}] %-5p %c{2} - %m%n"/>
            
    </layout>
        
    </appender>
    Run.java:
    public static void main(String[] args) {
            Properties props 
    = new Properties();
            props.setProperty(
    "loggingRoot""d:/tmp");
            props.setProperty(
    "instance""instance1");

            PlaceHolderDOMConfigurator.configure(LOG4J_PATH, props);
            Logger rootLogger 
    = LogManager.getRootLogger();
            FileAppender fileAppender 
    = (FileAppender) rootLogger.getAppender("PROJECT");
            System.out.println(fileAppender.getFile());
        }

    輸出結果:
    d:/tmp/instance1/project.log

    當然,你也可以通過在啟動參數中加 -DloggingRoot=xxxx  -Dinstance=yyyy動態指定內容.


    特別說明:
    本文:log4j版本為1.2.14
    log4j 1.2.15測試不通過,原因見:https://issues.apache.org/bugzilla/show_bug.cgi?id=43325

    posted @ 2010-07-01 08:52 stone2083 閱讀(12674) | 評論 (2)編輯 收藏

    單元測試下簡易性能測試工具

    背景:
    1.團隊成員對質量意識逐漸提升;單元測試意識提升;
    2.性能意識不足,往往到最后提交性能測試的時候,才發現性能問題;在開發階段忽視對性能的考慮.
    尤其在做對外服務的需求中,危害特別明顯.

    基于這兩個原因,希望有一個在單元測試下的性能測試工具.提供最簡單的性能指標報表.在開發階段讓開發對性能情況有個感性的認識.

    設計思路:


    概念說明:
     類名 方法
    說明
    Statistics 
    說明:性能統計信息
    tps() 提供tps
      average() 提供平均響應時間,單位毫秒
      total() 提供總耗時,單位毫秒
    Job 
    說明:測試單元邏輯
    execute() 性能測試邏輯
    Warn 
    說明:性能未達標警告


    PerformanceTester (核心)
    說明:性能測試工具,根據制定的并發數和單個并發循環次數,進行性能測試;根據提供的平均響應時間,分析是否達標
    test(Job job) 性能測試,打印性能報表,分析是否達標
    JTesterxPerformance 
    說明:基于JTester的性能測試基類,統一執行性能測試計劃
    備注:
    JTester是我們公司同事編寫的一套單元測試框架.我們同樣可以提供基于JUnit的實現,比如JUnitPerformance
    performance() 根據提供的性能策略,指標 和 測試邏輯,進行性能測試

    job() 需要子類覆寫,提供測試邏輯

    testers() 需要子類覆寫,提供性能測試策略和指標要求


    User Guide:
    • Step1:
        創建一個性能測試類,繼承com.alibaba.tpsc.common.test.jtesterx.JTesterxPerformance
        在類名標注@Test (org.testng.annotations.Test),表明需要進行TestNG的單元測試
        備注:如果是在其他單元測試框架下,請自行擴展類似JUnitPerformacne實現
    • Step2:
        覆寫public Job job()方法.提供 性能測試名 和 性能測試邏輯
         

    @Override
    public Job job() {
        
    return new Job("SampleService.hello") {
            @Override
            
    public void execute() {
                SampleService.hello();
            }
        };
    }
    • Step3:
        覆寫public Collection<PerformanceTester> testers().提供一組性能測試策略(并發數,單個并發循環次數) 和 性  能測試指標(平均響應時間)
        性能測試工具會根據提供策略和指標,依次進行性能測試.
    public Collection<PerformanceTester> testers() {
        Collection
    <PerformanceTester> testers = new ArrayList<PerformanceTester>();
        
    // 20個并發,單個并發循環1000次,平均響應時間閥值10ms
        testers.add(new PerformanceTester(20100010));
        
    // 10個并發,單個并發循環1000次,平均響應時間閥值5ms
        testers.add(new PerformanceTester(1010005));
        
    return testers;
    }
    • Step4:
        右鍵點擊Eclipse->Run As->TestNG Test.
        如果測試通過,則顯示Green Bar
        如果測試未通過,則在Red Bar中顯示:java.lang.AssertionError: performance expected is 1ms,but actual is 2.938ms.


    工具代碼和演示代碼如下:
    Demo下載

    posted @ 2010-06-10 09:13 stone2083 閱讀(2491) | 評論 (4)編輯 收藏

    編程方式實現SpringBean LazyInit

    Tags:
    Spring  LazyInit DocumentDefaultsDefinition ReaderEventListener AbstractXmlApplicationContext

    背景:
    工程單元測試希望和生產環境應用共用一份Spring配置文件.
    生產環境應用為了客戶體驗使用非LazyInit模式,但是單元測試下為了當前測試提高響應時間,希望設置LazyInit.

    分析源代碼,得知,Spring在解析XML時,會將Bean默認配置,放入到DocumentDefaultsDefinition對象中,其中包含lazyInit.
    DocumentDefaultsDefinition注釋如下:
    Simple JavaBean that holds the defaults specified at the <beans> level in a standard Spring XML bean definition document: default-lazy-init, default-autowire, etc

    Spring是否提供了入口點,進行DocumentDefaultsDefinition的修改呢?
    詳見:ReaderEventListener,注釋如下:
    Interface that receives callbacks for component, alias and import registrations during a bean definition reading process
    在BeanDefinition分析過程中,對component,alias,import registrations,defaults registrations提供一組callbacks.

    接口代碼如下:
     1 public interface ReaderEventListener extends EventListener {
     2 
     3     /**
     4      * Notification that the given defaults has been registered.
     5      * @param defaultsDefinition a descriptor for the defaults
     6      * @see org.springframework.beans.factory.xml.DocumentDefaultsDefinition
     7      */
     8     void defaultsRegistered(DefaultsDefinition defaultsDefinition);
     9 
    10     /**
    11      * Notification that the given component has been registered.
    12      * @param componentDefinition a descriptor for the new component
    13      * @see BeanComponentDefinition
    14      */
    15     void componentRegistered(ComponentDefinition componentDefinition);
    16 
    17     /**
    18      * Notification that the given alias has been registered.
    19      * @param aliasDefinition a descriptor for the new alias
    20      */
    21     void aliasRegistered(AliasDefinition aliasDefinition);
    22 
    23     /**
    24      * Notification that the given import has been processed.
    25      * @param importDefinition a descriptor for the import
    26      */
    27     void importProcessed(ImportDefinition importDefinition);
    28     
    29 }

    接下去分析,ReaderEventListener是在哪個入口點,提供了回調.答案是XmlBeanDefinitionReader.
    在AbstractXmlApplicationContext,創建了XmlBeanDefinitionReader對象,見:
     1 public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
     2     
     3     /**
     4      * Loads the bean definitions via an XmlBeanDefinitionReader.
     5      * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
     6      * @see #initBeanDefinitionReader
     7      * @see #loadBeanDefinitions
     8      */
     9     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
    10         // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    11         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    12 
    13         // Configure the bean definition reader with this context's
    14         // resource loading environment.
    15         beanDefinitionReader.setResourceLoader(this);
    16         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    17 
    18         // Allow a subclass to provide custom initialization of the reader,
    19         // then proceed with actually loading the bean definitions.
    20         initBeanDefinitionReader(beanDefinitionReader);
    21         loadBeanDefinitions(beanDefinitionReader);
    22     }
    23 
    24 }

    我們只需要去復寫這個方法,在創建XmlBeanDefinitionReader的時候,去注入EventListener即可.

    擴展代碼如下:
    LazyInitListener.java (不管配置文件如何配置,設置默認的LazyInit為true)
    public class LazyInitListener implements ReaderEventListener {

        
    private static final String LAZY_INIT = "true";

        @Override
        
    public void defaultsRegistered(DefaultsDefinition defaultsDefinition) {
            
    //set lazy init true
            if (defaultsDefinition instanceof DocumentDefaultsDefinition) {
                DocumentDefaultsDefinition defaults 
    = (DocumentDefaultsDefinition) defaultsDefinition;
                defaults.setLazyInit(LAZY_INIT);
            }
        }

        @Override
        
    public void aliasRegistered(AliasDefinition aliasDefinition) {
            
    //no-op
        }

        @Override
        
    public void componentRegistered(ComponentDefinition componentDefinition) {
            
    //no-op
        }

        @Override
        
    public void importProcessed(ImportDefinition importDefinition) {
            
    //no-op
        }

    }

    LazyInitClasspathXmlApplicationContext.java (復寫AbstractXmlApplicationContext,創建XmlBeanDefinitionReader的時候注入LazyInitListener)
     1 public class LazyInitClasspathXmlApplicationContext extends ClassPathXmlApplicationContext {
     2 
     3     public LazyInitClasspathXmlApplicationContext(String location) {
     4         super(location);
     5     }
     6 
     7     @Override
     8     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
     9         // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    10         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    11 
    12         // Configure the bean definition reader with this context's
    13         // resource loading environment.
    14         beanDefinitionReader.setResourceLoader(this);
    15         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    16 
    17         // 添加的代碼,設置LazyInitListener
    18         beanDefinitionReader.setEventListener(new LazyInitListener());
    19 
    20         // Allow a subclass to provide custom initialization of the reader,
    21         // then proceed with actually loading the bean definitions.
    22         initBeanDefinitionReader(beanDefinitionReader);
    23         loadBeanDefinitions(beanDefinitionReader);
    24     }
    25 
    26 }

    演示代碼如下:
    TestBean:一個Spring Bean對象
    public class TestBean {

        
    public void init() {
            
    try {
                Thread.sleep(
    5000);
            } 
    catch (InterruptedException e) {
                
    //ignore
                System.out.println(e);
            }
            System.out.println(
    "TestBean Init");
        }
    }

    Spring配置文件:
    1 <?xml version="1.0" encoding="UTF-8"?>
    2 <beans xmlns="http://www.springframework.org/schema/beans"
    3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    5 
    6     <bean id="testBean" class="com.alibaba.javalab.spring.lazy.TestBean" init-method="init" />
    7 </beans>

    測試代碼:
     1 public class Run {
     2 
     3     private static final String CONFIG = "classpath:spring/bean.xml";
     4 
     5     public static void main(String[] args) {
     6         testInit();
     7         System.out.println("===============================");
     8         testLazyInit();
     9     }
    10 
    11     public static void testInit() {
    12         new ClassPathXmlApplicationContext(CONFIG);
    13     }
    14 
    15     public static void testLazyInit() {
    16         new LazyInitClasspathXmlApplicationContext(CONFIG);
    17     }
    18 }


    大功告成.收工. :)

    posted @ 2010-06-03 12:46 stone2083 閱讀(3163) | 評論 (2)編輯 收藏

    Tomcat(6.0.14) Session創建機制簡介

    背景:
    公司的一個web應用,提交給測試部門做壓力測試(由于不是我負責的,不清楚如何做的壓力測試,以及測試指標),結果沒壓多久,就出現OutOfMemory.
    接手協助原因查找,通過監控工具,發現StandardSession(org.apache.catalina.session.StandardSession)對象不斷增長,毫無疑問,肯定是在不斷創建Session對象.
    備注:一般做壓力測試,每次請求都不會指定JESSESIONID值,導致Web容器認為每次請求都是新的請求,于是創建Session對象.
    同事負責代碼Review,發現應用沒有任何一個地方存放Session內容.困惑之...

    問題:Tomcat容器何時創建Session對象?
    想當然認為,只有動態存放Session內容的時候,才會創建Session對象.但是事實真得如此嗎?

    先看Servlet協議描述:
    請看:
    getSession(boolean create)方法:
    javax.servlet.http.HttpServletRequest.getSession(boolean create)

    Returns the current HttpSession associated with this request or, if if there is no current session and create is true, returns a new session. 

    If create is false and the request has no valid HttpSession, this method returns null. 

    To make sure the session is properly maintained, you must call this method before the response is committed.

    簡單地說:當create變量為true時,如果當前Session不存在,創建一個新的Session并且返回.

    getSession()方法:
    javax.servlet.http.HttpSession getSession();

    Returns the current session associated with this request, or if the request does not have a session, creates one.
    簡單的說:當當前Session不存在,創建并且返回.


    所以說,協議規定,在調用getSession方法的時候,就會創建Session對象.



    既然協議這么定了,我們再來看看Tomcat是如何實現的:(下面的描述,是基于Tomcat6.0.14版本源碼)
    先看一張簡單的類圖:


    ApplicationContext:Servlet規范中ServletContext的實現
    StandardContext:Tomcat定義的Context默認實現.維護了一份SessionManager對象,管理Session對象.所有的Session對象都存放在Manager定義的Map<String,Session>容器中.
    StanardManager:標準的Session管理,將Session存放在內容,Web容器關閉的時候,持久化到本地文件
    PersistentManager:持久化實現的Session管理,默認有兩種實現方式:
    --持久化到本地文件
    --持久化到數據庫

    了解了大概的概念后,回頭再來看看org.apache.catalina.connector.Request.getSession()是如何實現的.
    最終調用的是doGetSession(boolean create)方法,請看:
    protected Session doGetSession(boolean create) {

            
    // There cannot be a session if no context has been assigned yet
            if (context == null)
                
    return (null);

            
    // Return the current session if it exists and is valid
            if ((session != null&& !session.isValid())
                session 
    = null;
            
    if (session != null)
                
    return (session);

            
    // Return the requested session if it exists and is valid
            Manager manager = null;
            
    if (context != null)
                manager 
    = context.getManager();
            
    if (manager == null)
                
    return (null);      // Sessions are not supported
            if (requestedSessionId != null) {
                
    try {
                    session 
    = manager.findSession(requestedSessionId);
                } 
    catch (IOException e) {
                    session 
    = null;
                }
                
    if ((session != null&& !session.isValid())
                    session 
    = null;
                
    if (session != null) {
                    session.access();
                    
    return (session);
                }
            }

            
    // Create a new session if requested and the response is not committed
            if (!create)
                
    return (null);
            
    if ((context != null&& (response != null&&
                context.getCookies() 
    &&
                response.getResponse().isCommitted()) {
                
    throw new IllegalStateException
                  (sm.getString(
    "coyoteRequest.sessionCreateCommitted"));
            }

            
    // Attempt to reuse session id if one was submitted in a cookie
            
    // Do not reuse the session id if it is from a URL, to prevent possible
            
    // phishing attacks
            if (connector.getEmptySessionPath() 
                    
    && isRequestedSessionIdFromCookie()) {
                session 
    = manager.createSession(getRequestedSessionId());
            } 
    else {
                session 
    = manager.createSession(null);
            }

            
    // Creating a new session cookie based on that session
            if ((session != null&& (getContext() != null)
                   
    && getContext().getCookies()) {
                Cookie cookie 
    = new Cookie(Globals.SESSION_COOKIE_NAME,
                                           session.getIdInternal());
                configureSessionCookie(cookie);
                response.addCookieInternal(cookie, context.getUseHttpOnly());
            }

            
    if (session != null) {
                session.access();
                
    return (session);
            } 
    else {
                
    return (null);
            }

        }


    至此,簡單地描述了Tomcat Session創建的機制,有興趣的同學要深入了解,不妨看看Tomcat源碼實現.



    補充說明,順便提一下Session的過期策略.
    過期方法在:
    org.apache.catalina.session.ManagerBase(StandardManager基類) processExpires方法:
    public void processExpires() {

            
    long timeNow = System.currentTimeMillis();
            Session sessions[] 
    = findSessions();
            
    int expireHere = 0 ;
            
            
    if(log.isDebugEnabled())
                log.debug(
    "Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);
            
    for (int i = 0; i < sessions.length; i++) {
                
    if (sessions[i]!=null && !sessions[i].isValid()) {
                    expireHere
    ++;
                }
            }
            
    long timeEnd = System.currentTimeMillis();
            
    if(log.isDebugEnabled())
                 log.debug(
    "End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere);
            processingTime 
    += ( timeEnd - timeNow );

        }

    其中,Session.isValid()方法會做Session的清除工作.


    在org.apache.catalina.core.ContainerBase中,會啟動一個后臺線程,跑一些后臺任務,Session過期任務是其中之一:
    protected void threadStart() {

            
    if (thread != null)
                
    return;
            
    if (backgroundProcessorDelay <= 0)
                
    return;

            threadDone 
    = false;
            String threadName 
    = "ContainerBackgroundProcessor[" + toString() + "]";
            thread 
    = new Thread(new ContainerBackgroundProcessor(), threadName);
            thread.setDaemon(
    true);
            thread.start();

        }


    OVER :)

    posted @ 2010-02-26 16:12 stone2083 閱讀(7057) | 評論 (4)編輯 收藏

    布隆過濾器(BloomFilter)

    資料:
    wikipedia--bloom filter

    使用場景,原理簡介之中文資料:
    數學之美系列二十一 - 布隆過濾器(Bloom Filter)

    核心內容(摘自Google黑板報文章內容):


    BloomFilter簡易實現:
    public class SimpleBloomFilter {

        
    private static final int   DEFAULT_SIZE = 2 << 24;
        
    private static final int[] seeds        = new int[] { 71113313761, };

        
    private BitSet             bits         = new BitSet(DEFAULT_SIZE);
        
    private SimpleHash[]       func         = new SimpleHash[seeds.length];

        
    public static void main(String[] args) {
            String value 
    = "stone2083@yahoo.cn";
            SimpleBloomFilter filter 
    = new SimpleBloomFilter();
            System.out.println(filter.contains(value));
            filter.add(value);
            System.out.println(filter.contains(value));
        }

        
    public SimpleBloomFilter() {
            
    for (int i = 0; i < seeds.length; i++) {
                func[i] 
    = new SimpleHash(DEFAULT_SIZE, seeds[i]);
            }
        }

        
    public void add(String value) {
            
    for (SimpleHash f : func) {
                bits.set(f.hash(value), 
    true);
            }
        }

        
    public boolean contains(String value) {
            
    if (value == null) {
                
    return false;
            }
            
    boolean ret = true;
            
    for (SimpleHash f : func) {
                ret 
    = ret && bits.get(f.hash(value));
            }
            
    return ret;
        }

        
    public static class SimpleHash {

            
    private int cap;
            
    private int seed;

            
    public SimpleHash(int cap, int seed) {
                
    this.cap = cap;
                
    this.seed = seed;
            }

            
    public int hash(String value) {
                
    int result = 0;
                
    int len = value.length();
                
    for (int i = 0; i < len; i++) {
                    result 
    = seed * result + value.charAt(i);
                }
                
    return (cap - 1& result;
            }

        }

    }



    posted @ 2010-01-30 15:00 stone2083 閱讀(2585) | 評論 (0)編輯 收藏

    httpclient ssl support

    手頭上一些工作,需要經常訪問公司內網的數據,為了減少重復的勞動力,就考慮程序幫忙爬取信息數據。
    apache下httpclient是一個很好的工具,不過公司內網是使用HTTPS協議的,于是乎,就需要考慮如何讓httpclient支持https。
    支持ssl的關鍵,在于如何創建一個SSLSocket,幸好,httpclient提供了支持。

    請看:
    org.apache.commons.httpclient.protocol.ProtocolSocketFactory
    javadocs:A factory for creating Sockets.

    實現一個簡單的EasySSLProtocolSocketFactory,詳見代碼:
    public class EasySSLProtocolSocketFactory implements ProtocolSocketFactory {

        
    private SSLContext sslcontext = null;

        
    private String     ksfile;
        
    private String     tksfile;
        
    private String     kspwd;
        
    private String     tkspwd;

        
    public EasySSLProtocolSocketFactory(String ks, String kspwd, String tks, String tkspwd){
            
    this.ksfile = ks;
            
    this.kspwd = kspwd;
            
    this.tksfile = tks;
            
    this.tkspwd = tkspwd;
        }

        
    public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException,
                                                                                                 UnknownHostException {
            
    return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
        }

        
    public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort,
                                   
    final HttpConnectionParams params) throws IOException, UnknownHostException,
                                                                     ConnectTimeoutException {
            
    if (params == null) {
                
    throw new IllegalArgumentException("Parameters may not be null");
            }
            
    int timeout = params.getConnectionTimeout();
            SocketFactory socketfactory 
    = getSSLContext().getSocketFactory();
            
    if (timeout == 0) {
                
    return socketfactory.createSocket(host, port, localAddress, localPort);
            } 
    else {
                Socket socket 
    = socketfactory.createSocket();
                SocketAddress localaddr 
    = new InetSocketAddress(localAddress, localPort);
                SocketAddress remoteaddr 
    = new InetSocketAddress(host, port);
                socket.bind(localaddr);
                socket.connect(remoteaddr, timeout);
                
    return socket;
            }
        }

        
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
            
    return getSSLContext().getSocketFactory().createSocket(host, port);
        }

        
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException,
                                                                                           UnknownHostException {
            
    return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
        }

        
    private SSLContext createEasySSLContext() {
            
    try {
                SSLContext context 
    = SSLContext.getInstance("SSL");

                KeyManagerFactory kmf 
    = KeyManagerFactory.getInstance("SunX509");
                TrustManagerFactory tmf 
    = TrustManagerFactory.getInstance("SunX509");

                KeyStore ks 
    = KeyStore.getInstance("JKS");
                KeyStore tks 
    = KeyStore.getInstance("JKS");

                ks.load(
    new FileInputStream(ksfile), kspwd.toCharArray());
                tks.load(
    new FileInputStream(tksfile), tkspwd.toCharArray());

                kmf.init(ks, kspwd.toCharArray());
                tmf.init(tks);

                context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), 
    null);
                
    return context;
            } 
    catch (Exception e) {
                
    throw new HttpClientError(e.toString());
            }
        }

        
    private SSLContext getSSLContext() {
            
    if (this.sslcontext == null) {
                
    this.sslcontext = createEasySSLContext();
            }
            
    return this.sslcontext;
        }

    }

    備注:
    至于ssl相關的知識,和如何創建java key store,可見:
    SSL雙向認證java實現


    如何使用,也簡單:
    Protocol.registerProtocol("https", new Protocol("https", new EasySSLProtocolSocketFactory(KS_FILE, KS_PWD, TKS_FILE, TKS_PWD), 443))

    官方資料:
    http://hc.apache.org/httpclient-3.x/sslguide.html

    演示代碼:
    httpclient-ssl.zip

    posted @ 2010-01-30 14:39 stone2083 閱讀(2912) | 評論 (0)編輯 收藏

    ubuntu下安裝Python Imaging Library (PIL)

    easy_install is not so easy。
    這是我最近在學習python的一絲體會,好多lib都無法通過easy_install安裝,比如:Python Imaging Library (PIL)

    只能通過手工安裝方式安裝:
    *download the pil_1.1.6
    *tar xvf Imaging-1.1.6.tar.gz & chmox +x setup.py
    *python setup.py build

    結果,居然:
     _imagingtk.c -o build/temp.linux-i686-2.6/_imagingtk.o
    _imagingtk.c:
    20:16: error: tk.h: No such file or directory
    _imagingtk.c:
    23: error: expected ‘)’ before ‘*’ token
    _imagingtk.c:
    31: error: expected specifier-qualifier-list before ‘Tcl_Interp’
    _imagingtk.c: In function ‘_tkinit’:
    _imagingtk.c:
    37: error: ‘Tcl_Interp’ undeclared (first use in this function)
    _imagingtk.c:
    37: error: (Each undeclared identifier is reported only once
    _imagingtk.c:
    37: error: for each function it appears in.)
    _imagingtk.c:
    37: error: ‘interp’ undeclared (first use in this function)
    _imagingtk.c:
    45: error: expected expression before ‘)’ token
    _imagingtk.c:
    51: error: ‘TkappObject’ has no member named ‘interp’
    _imagingtk.c:
    55: warning: implicit declaration of function ‘TkImaging_Init’
    error: command 'gcc' failed with exit status 
    1

    tk.h No such file or directory

    事實上,tk-dev包我已經安裝了,查看setup.py代碼,發現:
    # Library pointers.
    #
    #
     Use None to look for the libraries in well-known library locations.
    #
     Use a string to specify a single directory, for both the library and
    #
     the include files.  Use a tuple to specify separate directories:
    #
     (libpath, includepath).  Examples:
    #
    #
     JPEG_ROOT = "/home/libraries/jpeg-6b"
    #
     TIFF_ROOT = "/opt/tiff/lib", "/opt/tiff/include"
    #
    #
     If you have "lib" and "include" directories under a common parent,
    #
     you can use the "libinclude" helper:
    #
    #
     TIFF_ROOT = libinclude("/opt/tiff")

    FREETYPE_ROOT 
    = None
    JPEG_ROOT 
    = None
    TIFF_ROOT 
    = None
    ZLIB_ROOT 
    = None
    TCL_ROOT 
    = None

    將TCL_ROOT = None 修改成:TCL_ROOT = '/usr/include/tk',即可

    python setup.py build
    python setup.py install

    成功 :)

    posted @ 2009-10-27 17:25 stone2083 閱讀(3105) | 評論 (1)編輯 收藏

    Struts2.1.6--想用通配符,不容易

    初次使用Struts2,老老實實為每個action method配置url mapping文件。
    時間長了,難為覺得繁瑣,為何不使用COC的方式呢?終于,想到了使用通配符。
    查看Struts2 Docs,找到相關配置方法:

    <package name="alliance" namespace="/alliance" extends="struts-default">
            
    <action name="*/*" class="cn.zeroall.cow.web.alliance.action.{1}Action" method="{2}">
                
    <result name="target" type="velocity">/templates/alliance/{1}/${target}.vm</result>
                
    <result name="success" type="velocity">/templates/alliance/{1}/{2}.vm</result>
                
    <result name="input" type="velocity">/templates/alliance/{1}/{2}.vm</result>
                
    <result name="fail" type="velocity">/templates/common/error.vm</result>
            
    </action>
    </package>

    恩,非常方便,可是啟動jetty,發現滿足正則的url,就是找不到Action。
    無奈,debug代碼,找到原因,需要在struts.properties中,配置:
    struts.enable.SlashesInActionNames = true
    見注釋:
    ### Set this to true if you wish to allow slashes in your action names.  If false,
    ### Actions names cannot have slashes, and will be accessible via any directory
    ### prefix.  This is the traditional behavior expected of WebWork applications.
    ### Setting to true is useful when you want to use wildcards and store values
    ### in the URL, to be extracted by wildcard patterns, such as 
    ### 
    <action name="*/*" method="{2}" class="actions.{1}"> to match "/foo/edit" or 
    ### "/foo/save".

    啟動,COC終于成功。

    但是(又冒出一個但是),針對*/*正則的url mapping,如何做validation呢?
    按照struts2的約定,是通過:
    [package/]ActionName-${配置中的action name=""中的名字}-validation.xml

    如何把"/"這個符號放入到${配置中的action name=""中的名字}呢?
    "/"可不是一個合法的文件名。

    比如,我要為AlliedMemberAction/doRegister做validation,那么約定的校驗文件名應該是:
    cn/zeroall/cow/web/alliance/action/AlliedMemberAction-AlliedMember/doRegister-validation.xml
    這個特殊符號,可難剎我也。

    無奈,繼續debug,發現在代碼:
    xwork框架中的,AnnotationActionValidatorManager:
    private  List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
            String fileName = aClass.getName().replace('.', '/') + "-" + context + VALIDATION_CONFIG_SUFFIX;

            return loadFile(fileName, aClass, checkFile);
    }
    這個context就是action name=""中的url表達式。

    思想斗爭后,由于我不喜歡使用*-*的pattern,更喜歡使用*/*pattern,只好修改了源碼:
    private  List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
            String fileName = aClass.getName().replace('.', '/') + "-" + context.replace("/", "-") + VALIDATION_CONFIG_SUFFIX;

            return loadFile(fileName, aClass, checkFile);
    }
    將context中的“/”變成"-"解決這個問題。

    不清楚struts2官方怎么看待這個問題。

    大家是否有更好的方案,請指教


    posted @ 2009-09-26 14:06 stone2083 閱讀(3651) | 評論 (5)編輯 收藏

    支付寶接口demo代碼讀后感

    最近在幫朋友做一個支付功能,用到了支付寶。
    從支付寶管理界面,下載到商戶合作文檔,看了demo程序后,心是拔涼拔涼的。
    說說review代碼后的問題吧:
    CheckURL.java
    public static String check(String urlvalue ) {


          String inputLine
    ="";

            
    try{
                    URL url 
    = new URL(urlvalue);

                    HttpURLConnection urlConnection  
    = (HttpURLConnection)url.openConnection();

                    BufferedReader in  
    = new BufferedReader(
                            
    new InputStreamReader(
                                    urlConnection.getInputStream()));

                    inputLine 
    = in.readLine().toString();
                }
    catch(Exception e){
                    e.printStackTrace();
                }
                
    //System.out.println(inputLine);  系統打印出抓取得驗證結果

            
    return inputLine;
      }
    *Inputstream不需要close?
    *知道e.printStackTrace()的性能代價?

    Md5Encrypt.java
    *是采用什么編碼的?我下載的是UTF8編碼版本的,請問Md5Encrypt.java是什么編碼?

    Payment.java
    public static String CreateUrl(String paygateway,String service,String sign_type,String out_trade_no,

                      String input_charset,String partner,String key,String seller_email,

                      String body,String subject,String price,String quantity,String show_url,String payment_type,

                      String discount,String logistics_type,String logistics_fee,String logistics_payment,

                      String return_url) {

                       
    //String notify_url,需要的請把參數加入以上的createurl

            Map params 
    = new HashMap();

            params.put(
    "service", service);

            params.put(
    "out_trade_no", out_trade_no);

            params.put(
    "show_url", show_url);

            params.put(
    "quantity", quantity);

            params.put(
    "partner", partner);

            params.put(
    "payment_type", payment_type);

            params.put(
    "discount", discount);

            params.put(
    "body", body);

           
    // params.put("notify_url", notify_url);

            params.put(
    "price", price);

            params.put(
    "return_url", return_url);

            params.put(
    "seller_email", seller_email);

            params.put(
    "logistics_type", logistics_type);

            params.put(
    "logistics_fee", logistics_fee);

            params.put(
    "logistics_payment", logistics_payment);

            params.put(
    "subject", subject);

            params.put(
    "_input_charset", input_charset);

            String prestr 
    = "";



            prestr 
    = prestr + key;

            
    //System.out.println("prestr=" + prestr);



            String sign 
    = com.alipay.util.Md5Encrypt.md5(getContent(params, key));



            String parameter 
    = "";

            parameter 
    = parameter + paygateway;

            
    //System.out.println("prestr="  + parameter);

            List keys 
    = new ArrayList(params.keySet());

            
    for (int i = 0; i < keys.size(); i++) {

                  String value 
    =(String) params.get(keys.get(i));

                
    if(value == null || value.trim().length() ==0){

                    
    continue;

                }

                
    try {

                    parameter 
    = parameter + keys.get(i) + "="

                        
    + URLEncoder.encode(value, input_charset) + "&";

                } 
    catch (UnsupportedEncodingException e) {



                    e.printStackTrace();

                }

            }



            parameter 
    = parameter + "sign=" + sign + "&sign_type=" + sign_type;



            
    return sign;



        }
    *多少個參數???超過3,4個參數,都不使用ParameterClass嗎?方便client調用嗎?
    *這個方法做什么?createUrl?得到url??墒聦嵣夏??return sign。sign是什么?是參數的加密竄。
    方法中的parameter不知道要來干嗎用?
    *又看到
    e.printStackTrace();

    SignatureHelper.java
    哇,總算看到一個過得去的代碼,可以eclipse上,發現一個warning:import java.io.UnsupportedEncodingException;
    有用到UnsupportedEncodingException這個嗎?

    SignatureHelper_return.java
    *看看這個類名,符合java類名的規范嗎?
    *和SignatureHelper.java有什么區別?

    SetCharacterEncodingFilter.java
    哇塞,總算看到非常標準的代碼了??墒牵?#64;author Craig McClanahan,原來是copy過來的。嗚呼。

    并且整個demo工程,是用myeclipse的。哎。。。

    看不下去了,實在看不下去了。
    我不清楚支付寶公司提供的demo程序的目的是什么?
    --提供的java文件是允許打成lib包使用的?
    --僅僅提供學習的?

    就算是提供學習的,寫得標準些,行不?

    最后,我真希望,是我自己下錯了demo程序--這個demo程序不是支付寶官方的demo。希望如此吧,阿門~

    備注:
    除了demo,那份接口文檔,寫得還是非常規范的。

    posted @ 2009-09-18 13:10 stone2083 閱讀(6309) | 評論 (10)編輯 收藏

    科比現身阿里濱江園區

    公園2009年09月11日下午13點30分,科比現身阿里巴巴濱江園區,難得有一次機會,近觀科比。
    最大的感受:高,黑,精(肉夠精)
    附上同學拍攝的紀實照片:





















    posted @ 2009-09-11 21:03 stone2083 閱讀(420) | 評論 (0)編輯 收藏

    僅列出標題
    共10頁: First 上一頁 2 3 4 5 6 7 8 9 10 下一頁 
    主站蜘蛛池模板: 久久综合亚洲色一区二区三区| 国产高潮久久免费观看| 亚洲国产国产综合一区首页| 卡1卡2卡3卡4卡5免费视频| 久9热免费精品视频在线观看| 一级视频在线免费观看| 亚洲hairy多毛pics大全| 亚洲国产亚洲片在线观看播放| 亚洲国产精品福利片在线观看| 亚洲A∨午夜成人片精品网站| www.999精品视频观看免费| 4399影视免费观看高清直播| 国产在线精品观看免费观看| 免费精品视频在线| 美女羞羞免费视频网站| 亚洲精品V天堂中文字幕| 亚洲影视自拍揄拍愉拍| 亚洲激情视频网站| 久久精品国产亚洲精品2020| 国产成人亚洲精品青草天美| 亚洲色偷偷偷鲁综合| 久久精品亚洲福利| 亚洲精品A在线观看| 亚洲成年人啊啊aa在线观看| 色吊丝永久在线观看最新免费| 毛片免费观看视频| 毛片a级毛片免费观看免下载| 久久精品免费全国观看国产| 欧美在线看片A免费观看| 成年女人毛片免费视频| 成人奭片免费观看| 女人与禽交视频免费看| 成年午夜视频免费观看视频| 青青草国产免费久久久下载| 色播在线永久免费视频| 国产伦精品一区二区三区免费迷 | 国产成人麻豆亚洲综合无码精品 | 亚洲一本到无码av中文字幕| 中文有码亚洲制服av片| 亚洲av日韩av永久无码电影| 特级毛片全部免费播放|