デフォルトのcatalina.policyを使うとexamplesでcatalina.logにwarningが出る

2011/01/21 10:50:26 org.apache.catalina.startup.HostConfig deployDirectory
INFO: Webアプリケーションディレクトリ examples を配備します
2011/01/21 10:50:26 org.apache.catalina.loader.WebappClassLoader findClass
WARNING: WebappClassLoader.findClassInternal(chat.ChatServlet) security exception: access denied (java.lang.RuntimePermission accessClassInPackage.org.apache.catalina)
java.security.AccessControlException: access denied (java.lang.RuntimePermission accessClassInPackage.org.apache.catalina)

logが汚れて邪魔なので、examplesにpermissionを付与する。

// ========== EXAMPLE CODE PERMISSIONS =======================================


// These permissions apply to example
grant codeBase "file:${catalina.home}/webapps/examples/WEB-INF/classes/-" {
        permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina";
        // permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager";
        // permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager.util";
};

Tomcatで動かすWebアプリをプラグインで機能拡張可能にするためにやったこと

ここら辺を参考に。

プラグインのインターフェースを決める

とりあえず適当に決める。
priority付けてるのはプラグインが複数あったときに実行順を決めたかっただけ。

package com.example;

public interface FooPlugin {
	public int getPriority();
	public Bar execute(Bar bar);
}

プラグイン読み込み管理クラスをつくる

最初URLClassLoaderで試してたんだけど、うまくいかないので、RMIClassLoaderに変更。

package com.example;

import java.io.File;
import java.io.IOException;
import java.rmi.RMISecurityManager;
import java.rmi.server.RMIClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.logging.Logger;

public class FooPluginManager {
	private static final Logger logger = Logger.getLogger(FooPluginManager.class.getName());
	private ArrayList<FooPlugin> plugins = new ArrayList<FooPlugin>();

	public FooPluginManager(String pluginPath) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
		logger.info(pluginPath);
		if (System.getSecurityManager() == null) {
			System.setSecurityManager(new RMISecurityManager());
		}
		File dir = new File(pluginPath);
		String[] files = dir.list();
		for (int i = 0; i < files.length; i++) {
			if (files[i].toLowerCase().endsWith(".jar")) {
				File jarFile = new File(pluginPath + File.separator + files[i]);
				JarFile jar = new JarFile(jarFile);
				Manifest manifest = jar.getManifest();
				Attributes attribs = manifest.getMainAttributes();
				String className = attribs.getValue("Plugin-Class");
				if (className != null) {
					className = className.trim();
					Class<?> clazz = RMIClassLoader.loadClass(jarFile.toURL(), className);
					logger.info("load... " + className);
					Object obj = clazz.newInstance();
					plugins.add((FooPlugin) obj);
				}
			}
		}
	}

	public ArrayList<FooPlugin> sort() {
		Collections.sort(plugins, new Comparator<FooPlugin>() {
			public int compare(FooPlugin a, FooPlugin b) {
				if (a.getPriority() < b.getPriority()) return -1;
				if (a.getPriority() > b.getPriority()) return  1;
				return a.getClass().getCanonicalName().compareTo(b.getClass().getCanonicalName());
			}
		});
		for (Iterator<FooPlugin> iter = plugins.iterator(); iter.hasNext();) {
			logger.info("sort... " + iter.next().getClass().getCanonicalName());
		}
		return plugins;
	}
}

プラグインを書く

2個くらい書いてみる。

package com.example;

import com.example.Bar;
import com.example.FooPlugin;

public class FooPluginImpl1 implements FooPlugin {
	private static final int priority = 2;

	public int getPriority() {
		return priority;
	}

	public Bar execute(Bar bar) {
		return bar;
	}
}
package com.example;

import com.example.Bar;
import com.example.FooPlugin;

public class FooPluginImpl2 implements FooPlugin {
	private static final int priority = 1;

	public int getPriority() {
		return priority;
	}

	public Bar execute(Bar bar) {
		return bar;
	}
}

マニフェストを書いてjarファイルをつくる

EclipseのExport機能を使ってjarファイルをつくる。(任意のマニフェストを指定できる。)

p1.mf
Manifest-Version: 1.0
Plugin-Class: com.example.FooPluginImpl1
p2.mf
Manifest-Version: 1.0
Plugin-Class: com.example.FooPluginImpl2

つくった二つのjarファイルをどこか適当に配置する。
「C:\jakarta-tomcat-6.0\webapps\foo\WEB-INF\plugins」に置いた。

メインプログラム

とりあえず読み込んでソートだけしてみる。

		FooPluginManager pm = new FooPluginManager("C:\\jakarta-tomcat-6.0\\webapps\\foo\\WEB-INF\\plugins");
		ArrayList<FooPlugin> plugins = pm.sort();

jakarta-tomcat-6.0/conf/catalina.policyの変更

とりあえずゆるゆるで。

// ========== FOO CODE PERMISSIONS =======================================


// These permissions apply to foo
grant codeBase "file:${catalina.home}/webapps/foo/WEB-INF/classes/-" {
        permission java.security.AllPermission;
};

// These permissions apply to plugin
grant codeBase "file:${catalina.home}/webapps/foo/WEB-INF/plugins/-" {
        permission java.security.AllPermission;
};

Tomcat起動引数を追加

デフォルトだとcatalina.policyは使われず、JREのlib/security/java.policyが使われる。-securityオプションを付けるとcatalina.policyを使うようになるとTomcatのドキュメントにあったんだけど、実際に試すとそんなコマンドはないとか言われて困り果てたところ、RE: Simple Security Manager how to ? というのを見つけた。
Configure -> [Java] タブ -> Java Options

-Djava.security.manager
-Djava.security.policy=C:\jakarta-tomcat-6.0\conf\catalina.policy

おわり

ログで動作確認。

2011/01/19 10:54:17 com.example.FooPluginManager <init>
INFO: C:\jakarta-tomcat-6.0\webapps\foo\WEB-INF\plugins
2011/01/19 10:54:17 com.example.FooPluginManager <init>
INFO: load... com.example.FooPluginImpl1
2011/01/19 10:54:17 com.example.FooPluginManager <init>
INFO: load... com.example.FooPluginImpl2
2011/01/19 10:54:17 com.example.FooPluginManager sort
INFO: sort... com.example.FooPluginImpl2
2011/01/19 10:54:17 com.example.FooPluginManager sort
INFO: sort... com.example.FooPluginImpl1

放置していたtumblrを再動させつつあるので、やっつけbookmarklet書いた。

選択範囲からそれっぽい引用HTMLを生成

javascript:(function(d,t,s,r,f){t.appendChild(d.createTextNode(['<blockquote%20title="'+document.title+'"%20cite="'+location.href+'"><dl>','<dt>'+f(s.shift())+'</dt>',(s.length>0?'<dd><pre>'+s.map(function(v){v=f(v);v=v.replace(r,'<a%20href="$1;">$1;</a>');return%20v}).join('\n')+'</pre></dd>\n':'')+'</dl></blockquote>'].join('\n')));with(t.style){position='fixed';bottom=left=0;zIndex=999;width='90%';height='9em'}d.body.appendChild(t);t.addEventListener('blur',function(){this.parentNode.removeChild(this)},false);t.select()})(document,document.createElement('textarea'),getSelection().toString().split(/\n/).filter(function(v)v),/(https?:\/\/[\-_.!~*\'()\w;\/?:\@&=+\$,%#]+)/g,function(s)s.replace(/^\s+|\s+$/,''))

cite属性値からanchor要素を生成

昔からあるやつです。僕はFirefoxを使っていて、CSSの擬似要素でcontent:attr(cite)とかされてても嬉しくないので。

javascript:(function(d,q){Array.prototype.slice.apply(q('blockquote,q')).forEach(function(b){b.appendChild(d.createTextNode(b.cite))})})(document,function(q)document.querySelectorAll(q))

僕の Ado

ado.wsc

こういうのを書いて ado.wsc で保存して、右クリックで COM 登録して使ってる。
GUID は GUIDの生成 - Ci.nsIZIGOROu - Mozilla 拡張機能勉強会 でやると便利ですよ。
saveFile メソッドは utf-8n 用。 ADODB.Streamオブジェクトを使ってBOMなしUTF-8のファイルを作成する方法 - 大人になったら肺呼吸

ENTITY使ってますか

全国のXMLerの皆さん、ENTITY使ってますか。DTDをそらで書ける皆さんなら当然使ってますよね。たとえば、Androidの設定ファイルなんかでも、

main.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE LinearLayout [
	<!ENTITY padding '10dp'>
	<!ENTITY view.text '
		<TextView
			android:text="copy"
			android:layout_width="fill_parent"
			android:layout_height="wrap_content"
			android:padding="&padding;" />
	'>
]>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">

	<ListView android:id="@+id/list_view_select"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:padding="&padding;" />

	<Spinner
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:padding="&padding;" />

	<RatingBar
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:padding="&padding;" />

	<EditText
		android:hint="コメント"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:padding="&padding;" />

	&view.text;
	&view.text;
	&view.text;
	&view.text;

</LinearLayout>

paddingの共通値に使ってみたり、Viewを複製してみたり、

strings.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
    <!ENTITY serv 'http://localhost/'>
]>
<resources>
    <string name="app_name">Layout</string>
    <string name="url_hoge">&serv;hoge</string>
    <string name="url_piyo">&serv;piyo</string>
    <string name="url_fuga">&serv;fuga</string>
    <string name="text_view_camera">カメラ</string>
    <string name="text_view_camera_role">カメラロール</string>
</resources>

文字列の共通部分とかにも! 簡単でしょ?

Linuxでtitanium起動

rm ~/.titanium/runtime/linux/1.0.0/libgobject-2.0.*
rm ~/.titanium/runtime/linux/1.0.0/libglib-2.0.*
rm ~/.titanium/runtime/linux/1.0.0/libgio-2.0.*
rm ~/.titanium/runtime/linux/1.0.0/libgthread-2.0.*
http://developer.appcelerator.com/question/14471/symbol-lookup-error-usrliblibgdk-x11-20so0-undefined-symbol-gmallocn-solved

つうか、消していいんですかね……