slim3のtester周りのコードを少し眺めてたら
本日も自宅で陽気にプログラミングの時間です。体調悪いですが、目はさえています。
以下はAppEngineTesterのprepareLocalServices()メソッドですが
@SuppressWarnings("unchecked") protected static void prepareLocalServices(ClassLoader loader) { try { Class<?> apiProxyLocalImplClass = loader.loadClass(API_PROXY_LOCAL_IMPL_CLASS_NAME); Class<?> localServerEnvironmentClass = loader.loadClass(LOCAL_SERVER_ENVIRONMENT_CLASS_NAME); Constructor<?> con = apiProxyLocalImplClass .getDeclaredConstructor(localServerEnvironmentClass); con.setAccessible(true); InvocationHandler ih = new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("getAppDir")) { return new File("build/test-classes"); } if (method.getName().equals("getPort")) { return 0; } return null; } }; Object localServerEnvironment = Proxy.newProxyInstance( loader, new Class<?>[] { localServerEnvironmentClass }, ih); apiProxyLocalImpl = (Delegate<Environment>) con.newInstance(localServerEnvironment); } catch (Throwable cause) { ThrowableUtil.wrapAndThrow(cause); } }
気になっちゃったものなー
con.setAccessible(true);
↑ここ。
ならばこのコードに沿って、前のApiProxyLocalImplをFactoryから生成してたやつを勉強がてら書き直してみましょうか。意味はないです。勉強です。Proxyクラスとかの使い方の勉強になりました。
前のコードは、
public void setUp() { ApiProxy.setEnvironmentForCurrentThread(new TestEnviroment()); ApiProxyLocal proxy = new ApiProxyLocalFactory() .create(new LocalServerEnvironment() { @Override public void waitForServerToStart() throws InterruptedException { } @Override public int getPort() { return 0; } @Override public File getAppDir() { return new File("nekonekoponpon"); } @Override public String getAddress() { return null; } }); proxy.setProperty(LocalDatastoreService.NO_STORAGE_PROPERTY , Boolean.TRUE.toString()); ApiProxy.setDelegate(proxy); }
であって、このApiProxyLocalFactory().createの引数に渡しているLocalServerEnvironmentは、LocalServerEnvironmentのインスタンスそのものなのであろう。
で、↓slim3コード参考にさせてもらいこのようにしてもできるのですが、
public void setUp() throws Exception { ApiProxy.setEnvironmentForCurrentThread(new TestEnviroment()); ClassLoader loader = LocalBaseTestCase.class.getClassLoader(); Class<?> localServerEnvironmentClass = loader.loadClass("com.google.appengine.tools.development.LocalServerEnvironment"); Class<?> apiProxyLocalImplClass = loader.loadClass("com.google.appengine.tools.development.ApiProxyLocalImpl"); Constructor<?> con = apiProxyLocalImplClass.getDeclaredConstructor(localServerEnvironmentClass); con.setAccessible(true); InvocationHandler ih = new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("getAppDir")) { return new File("nekoneko"); } if (method.getName().equals("getPort")) { return 0; } return null; } }; Object localServerEnvironment = Proxy.newProxyInstance(loader,new Class[] { localServerEnvironmentClass },ih); ApiProxyLocal proxy = (ApiProxyLocal) con.newInstance(localServerEnvironment); proxy.setProperty(LocalDatastoreService.NO_STORAGE_PROPERTY,Boolean.TRUE.toString()); ApiProxy.setDelegate(proxy); }
前回との違いはcon.newInstance(localServerEnvironment)の引数のlocalServerEnvironmentは、Proxyクラスから作られたProxyオブジェクトであって、Proxy生成対象となるクラスのメソッドが呼ばれた場合には、Proxy.newProxyInstance()の第3引数のInvocationHandlerのinvoke()を呼びますということらしい。それがProxyというものらしい。
イメージ的には、LocalServerEnvironmentのメソッドを全てオーバライドして、全てのメソッドでinvoke()に処理を委譲し、とかいうイメージになるのでしょうか? それでinvoke()の引数として実際のメソッドをMethodクラスのオブジェクトとして与えている。AOPの感じですかね。
今回の場合は、
package com.google.appengine.tools.development; import java.io.File; public interface LocalServerEnvironment { public abstract File getAppDir(); public abstract String getAddress(); public abstract int getPort(); public abstract void waitForServerToStart() throws InterruptedException; }
のgetAppDir()とgetPort()が呼ばれたときに「new File("nekoneko")」と「0」をそれぞれreturnしていて、それ以外はnullを返しているのであるな。
con.setAccessible(true);
↑これやらないと、
ApiProxyLocal proxy = (ApiProxyLocal) con.newInstance(localServerEnvironment);
↑ここでアクセスできないって怒られるね。
あーメモった。メモッた。間違えていたら、すいません。いろいろ知らないこと多すぎ。slim3のコードを読むのは勉強になるので、頑張って少しずつ読んでいきましょう。
con.setAccessible(true);
こんなのぜんぜん知らなかったもの。だめだだめだ。俺はだめだ。
あとslim3だと、そもそもtest用にこのjarやあのjarをパスに追加しろとかもないっぽいですね。依存してない。よいですねー。