• HttpURLConnectionのタイムアウト

    2012年03月31日 12時09分
    JavaでHTTP接続が必要になったので、HttpURLConnectionを使おうと思ったのですが、
    connect timeoutとread timeoutの意味がよくわからなかったので調べました。

    調べた環境はjdk1.7.0_02 (Windows 64bit)です。

    connect timeout

    connect timeoutは接続にかかった時間で、setConnectTimeoutで設定します。

    引数はミリ秒で指定し、0を指定すると無限になります。
    デフォルト値はJavadocに書いてないのですが、0になっています。


    connect timeoutが発生するのはconnectメソッド内です。
    接続にかかった時間がtimeout値を超えるとSocketTimeoutExceptionが発生します。


    read timeout

    read timeoutはデータ取得にかかった時間で、setReadTimeoutで設定します。
    (リクエストを投げてレスポンスが帰ってくるまでの時間)

    引数はミリ秒で指定し、0を指定すると無限になります。
    デフォルト値はJavadocに書いてないのですが、0になっています。


    read timeoutが発生するのは、レスポンスデータを参照するメソッドです。
    • getContent
    • getHeaderFields
    • getInputStream
    • getResponseCode
    • getResponseMessage
    • getContentEncoding
    • getContentLength
    • getContentLengthLong
    • getContentType
    • getDate
    • getExpiration
    • getHeaderField
    • getHeaderFieldDate
    • getHeaderFieldInt
    • getHeaderFieldKey
    • getLastModified

    ただ、上記の内、SocketTimeoutExceptionが発生するのは次のメソッドだけです。
    • getContent
    • getHeaderFields
    • getInputStream
    • getResponseCode
    • getResponseMessage

    他のメソッドはnullとか0とかエラー値を返すだけです。
    SocketTimeoutExceptionが発生しないメソッドだけを使うと、
    timeoutが起こったことがわからないので注意が必要です。

    どのメソッドが例外を投げるかはJavadocに書いてないので実際の動作からしか読み取れません。
    (getInputStreamだけは書いてあったりしてますますよくわかりません。)
    実際に使用する環境でも念のため調べておいた方が無難でしょう。
  • SAStrutsのAction

    2012年03月29日 21時52分
    前回の続きでもう1パターン考えてみました。

    やっぱり一々getter作るのは、書くのも読むのも面倒だよね。
    ということで、publicフィールドで行く方向で考えます。

    要するにリクエストパラメータのセットから特定のフィールドを除外してやれば良いわけです。
    「特定のフィールド」を指定するにはやはりアノテーションが良いでしょう。
    以下のアノテーションを作成します。
    1@Target(ElementType.FIELD)
    2@Retention(RetentionPolicy.RUNTIME)
    3public @interface Readonly
    4{
    5}



    リクエストパラメータのセットはS2RequestProcessorで行っているので、これを改造してしまいます。
    1public class RequestProcessor extends S2RequestProcessor
    2{
    3    @SuppressWarnings({"unchecked""rawtypes"})
    4    @Override
    5    protected void setSimpleProperty(Object bean, String name, Object value)
    6    {
    7        if (bean instanceof Map)
    8        {
    9            setMapProperty((Map)bean, name, value);
    10            return;
    11        }
    12
    13        BeanDesc beanDesc = BeanDescFactory.getBeanDesc(bean.getClass());
    14        if (!beanDesc.hasPropertyDesc(name))
    15            return;
    16
    17        PropertyDesc pd = beanDesc.getPropertyDesc(name);
    18        if (!pd.isWritable())
    19            return;
    20
    21        // @Readonlyアノテーションが有ったらセットしない
    22        Field f = pd.getField();
    23        if (f != null && f.getAnnotation(Readonly.class) != null)
    24            return;
    25        
    26        if (pd.getPropertyType().isArray())
    27            pd.setValue(bean, value);
    28        else if (List.class.isAssignableFrom(pd.getPropertyType()))
    29        {
    30            List<String> list = ModifierUtil.isAbstract(pd.getPropertyType()) ? new ArrayList<String>() : (List<String>)ClassUtil.newInstance(pd.getPropertyType());
    31            list.addAll(Arrays.asList((String[]) value));
    32            pd.setValue(bean, list);
    33        }
    34        else if (value == null)
    35            pd.setValue(bean, null);
    36        else if (value instanceof String[])
    37        {
    38            String[] values = (String[]) value;
    39            pd.setValue(bean, values.length > 0 ? values[0] : null);
    40        }
    41        else
    42        pd.setValue(bean, value);
    43    }
    44}


    struts-config.xmlを書き換えてセットします。
    <controller
            maxFileSize="1024K"
            bufferSize="1024"
            processorClass="jp.ku6.seasar.struts.action.RequestProcessor"
            multipartClass="org.seasar.struts.upload.S2MultipartRequestHandler"/>



    使い方は、publicなフィールドに@Readonlyを付けるだけです。
    1@Readonly
    2public List<Homu> homus;



    あと、publicにするとAutoBindingの対象になるので、一応ActionのAutoBindingDefを変更してみました。
    <component name="actionCreator" class="org.seasar.framework.container.creator.ActionCreator">
    		<initMethod name="setAutoBindingDef">
    			<arg>@org.seasar.framework.container.assembler.AutoBindingDefFactory@SEMIAUTO</arg>
    		</initMethod>
    	</component>


    別に対象になっても実際に設定されることはまず無いとは思うのですが、
    どうせ設定したい物には明示的に@Resourceを付けるので一応やっておくかなと。


    何かだめなところが見つかるまではこれで行ってみます。
  • SAStrutsのActionのトランザクション

    2012年03月28日 22時31分
    SAStruts + Mayaaで開発しています。
    まだ実装方法に迷いながらやっているので、考えたことなどをメモっておこうと思います。


    表示用データを作ってHTMLに出力する流れを次のようにしていました。


    まず、Actionに表示用データのフィールドを持ちます。
    リクエストパラメータの設定対象とならないようprivateにしています。
    1private List<Homu> homus;


    privateだとMayaaから参照出来ないため、getterを作成します。
    1public List<Homu> getHomus()
    2{
    3    return homus;
    4}


    そしてMayaaから上記getter経由でデータを読み出します。
    <m:forEach id="homus" items="${homus}" var="homu"/>


    この時、SAStrutsのActionがデフォルトでTxAttributeCustomizerを設定しているため、
    getHomus()メソッドにトランザクションがかかってしまいます。

    このトランザクションは特に必要な物ではないため、消してみようかと思いました。



    解決策1

    動くんだから気にしない。


    解決策2

    publicにしてgetterを使わない。

    チュートリアルなんか見ていると、SAStruts的な正解はpublicにするような気もします。
    1public List<Homu> homus;


    ただ、リクエストパラメータで同じ名前を指定されるとデータが入るのが気になります。
    パラメータ名は外からは推測しか出来ませんし、Actionできちんと上書きすれば問題ないか。


    解決策3

    TxAttributeCustomizerを拡張して、トランザクションが不要なメソッドを対象から外す。

    具体的にはTxAttributeCustomizerを継承し、
    @Executeアノテーションが付いたメソッド以外は除外するクラスを作る。
    1public class ActionTxAttributeCustomizer extends TxAttributeCustomizer
    2{
    3    @Override
    4    protected void doCustomize(final ComponentDef componentDef)
    5    {
    6        final Class<?> componentClass = componentDef.getComponentClass();
    7        final TransactionAttribute classAttribute = componentClass.getAnnotation(TransactionAttribute.class);
    8        final TransactionAttributeType classAttributeType = classAttribute != null ? classAttribute.value() : defaultAttributeType;
    9
    10        for (final Method method : componentClass.getMethods())
    11        {
    12            if (method.isSynthetic() || method.isBridge())
    13                continue;
    14
    15            if (method.getDeclaringClass() == Object.class)
    16                continue;
    17
    18            // @Executeアノテーションが付いていない物は除外する
    19            if (method.getAnnotation(Execute.class) == null)
    20                continue;
    21
    22            final TransactionAttribute methodAttribute = method.getAnnotation(TransactionAttribute.class);
    23            final TransactionAttributeType methodAttributeType = methodAttribute != null ? methodAttribute.value() : classAttributeType;
    24            final String interceptorName = txInterceptors.get(methodAttributeType);
    25            if (!StringUtil.isEmpty(interceptorName))
    26                componentDef.addAspectDef(AspectDefFactory.createAspectDef(interceptorName, method));
    27        }
    28    }
    29}

    「get」から始まるメソッドは除外。とかでもいいかもしれません。



    そこはかとなくこれでいい気がしないことも無くも無いのでこれで行ってみようと思います。