/*
* Copyright (c) 2012 NTT DATA Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jp.terasoluna.fw.beans.jxpath;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import org.apache.commons.jxpath.JXPathIntrospector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* commons-JXPathのバグ(JXPATH-152)用パッチをアクティベートするクラス。
* <p>
* クラスロードのタイミングで、 JXPathIntrospectorの、問題を抱えているprivate staticフィールド(byClass, byInterface)内のHashMapを パッチオブジェクト(
* {@link HashMapForJXPathIntrospector})に差し替える。<br>
* </p>
* <p>
* このアクティベータを使用するには、アプリケーション起動時に、このクラスをロードする(ロードされていないときにロードする)必要がある。<br>
* applicationContext.xmlに、以下の記述を追加することで、可能である。 <fieldset> <legend>applicationContext.xml</legend> <bean
* id="jxpathPatchActivator" class="jp.terasoluna.fw.beans.jxpath.JXPATH152PatchActivator"/> </fieldset>
* </p>
* <p>
* 特記事項:<br>
* このクラスは、JXPathIntrospectorのprivate staticフィールドにアクセスするため、 セキュリティマネージャを設定している場合には、
* privateフィールドへのアクセス権(java.lang.reflect.ReflectPermissionのsuppressAccessChecks)を付与する必要がある。<br>
* なお、privateフィールドアクセスは特権モードで実行する(呼び出し元のクラスの権限が低い場合でも、このクラスに与えられた権限で実行する)ため、 アクセス権の付与対象を絞りたい場合は、以下の例のように、
* このクラスを含むjarファイルのみに限定してprivateフィールドへのアクセス権を付与すればよい。 <fieldset> <legend>セキュリティポリシーファイルの設定例(Tomcatの例)</legend>
*
* <pre>
* grant codeBase "file:${catalina.home}/webapps/<ContextRoot>/WEB-INF/lib/<このクラスを含むjarファイル名>" {
* permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
* };
* </pre>
*
* </fieldset> (アクセス権の付与対象を絞る必要が無い場合は、「codeBase "file:~"」の部分を省略する。)<br>
* セキュリティマネージャが有効かつ権限不足でパッチをアクティベートできなかった場合や、 commons-JXPath-1.3とはJXPathIntrospectorの実装が異なるバージョンのcommons-JXPathを使用するなどして
* パッチをアクティベートできなかった場合には、FATALログを出力する。<br>
* ただし、Errorが発生しない限り、パッチをアクティベートできなくても、例外はスローしない。<br>
* したがって、Errorが発生する場合を除き、パッチをアクティベートできなかった場合、パッチが当たっていない状態でアプリケーションを継続する。
* </p>
* @see HashMapForJXPathIntrospector
*/
public class JXPATH152PatchActivator {
/**
* ログクラス。
*/
private static final Log log = LogFactory.getLog(
JXPATH152PatchActivator.class);
static {
// 特権モードで実行する。
// (呼び出し元のクラスの権限が低い場合でも、このクラスに与えられた権限で実行する。)
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
activate();
return null;
}
});
}
/**
* パッチをアクティベートする。
*/
private static void activate() {
try {
// もともと使用されているMapオブジェクトを取得
Field byClassField = JXPathIntrospector.class.getDeclaredField(
"byClass");
byClassField.setAccessible(true);
Map<?, ?> byClass = (Map<?, ?>) byClassField.get(null);
Field byInterfaceField = JXPathIntrospector.class.getDeclaredField(
"byInterface");
byInterfaceField.setAccessible(true);
Map<?, ?> byInterface = (Map<?, ?>) byInterfaceField.get(null);
// Mapオブジェクトを差し替える
byClassField.set(null,
new HashMapForJXPathIntrospector<Object, Object>(byClass));
byInterfaceField.set(null,
new HashMapForJXPathIntrospector<Object, Object>(byInterface));
log.info("JXPATH-152 Patch activation succeeded.");
} catch (Exception e) {
// セキュリティマネージャの制限による例外や、
// 想定していないバージョンのcommons-JXPathを使用したことによる例外
log.fatal("JXPATH-152 Patch activation failed.", e);
}
}
}