macにeclipseをインストール

mac os x 10.6 (snow leopard)にeclipseをインストールするときの手順を書きます。

1. eclipseダウンロード

http://www.eclipse.org/downloads/からEclipse IDE for Java EE DevelopersのMac Cocoa 64bitをダウンロードします。

2.eclipseインストール

ダウンロードしたファイルを解凍し、「eclipseディレクトリを/Applicationsディレクトリに格納します。

3.日本語化

http://sourceforge.jp/projects/mergedoc/からPleiadesをダウンロードします。
解凍した後、pleiadesフォルダの下にある「plugins」「features」フォルダを/Applications/eclipseの下にある「plugins」「features」フォルダに上書き統合します。
次に、/Applications/eclipse/Eclipse.app/Contents/MacOS/eclipse.iniを開いて、以下の一行を追加します。

javaagent:/Applications/eclipse/plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar

ActionScriptにおけるMapみたいなオブジェクト 結合配列/連想配列

ActionScript上で連想配列を使うには、ObjectかDctionaryを使います。
ActionScriptでは連想配列とは言わず、結合配列と呼ぶとか。

ArrayってObjectを継承しているから、ArrayとObjectの違いは何だ?と思ってリファレンスを調べたら、Arrayも結合配列として使えるけど、その場合、Arrayのいくつかの関数が使えなくなるからObjectを使ってね、ってことらしい。

Objectクラス

Objectのプロパティを[]で参照して、連想配列として使うことができます。
キーとして指定できるのは文字列のみです。オブジェクトをキーとして使いたい場合は、Dictionaryを使用します。

var employees:Object = new Object();
employees = { e1001:"jhon", e1002:"steven", e1003:"brown" }
trace(employees["e1001"]);

// 値の追加
employees.e1004 = "scott";
employees["e1005"] = "daniel";

// 値の取得
trace(employees.e1004);
trace(employees["e1005"]);

// 全ての値を取得
// for...inステートメントで全てのキーを取得できます
for (var n:String in employees)
{
    trace(n); // key
    trace(employees[n]); value
}

Dictionary

キーに文字列以外を指定したい場合は、Dictionaryが使用できます。
Dictionaryはflash.utilsパッケージに含まれています。

var employees:Dictionary = new Dictionary();
var jhon:Person = new Person();
var steven:Person = new Person();

var e1001:Employee = new Employee();
var e1002:Employee = new Employee();

employees[jhon] = e1001;
employees[steven] = e1002;

trace(Employee(employees[jhon]).getName());

EmployeeクラスとPersonクラスの中身はご想像にお任せします。



配列に関しては、ここにまとまっていました
http://www.tom.sfc.keio.ac.jp/~fjedi/wiki/index.php?%C7%DB%CE%F3%A1%A2%CA%B8%BB%FA%CE%F3%A1%A2%C0%B5%B5%AC%C9%BD%B8%BD%28ActionScript3%29#z7f00c97

ActionScriptのスタティックイニシャライザ

AS3.0のスタティックイニシャライザの挙動が、Javaとちょっと違うことに気づいたのでメモ。

Javaの場合

Javaの場合は、スタティックフィールドイニシャライザも、スタティックイニシャライザも、定義された順番に実行される。

public class StaticInitializer { 
    public static String str1 = "hoge"; 
    public static String str2 = "dummy"; 

    static { 
        System.out.println("(StaticInitializer) str2=" + str2); 
        str2 = "Hello, " + str1; 
    }
    public static String str3 = str2; 
    
    public static void main(String[] args) { 
        System.out.println("str1 = " + StaticInitializer.str1); 
        System.out.println("str2 = " + StaticInitializer.str2); 
        System.out.println("str3 = " + StaticInitializer.str3); 
    } 
}

(結果)
(StaticInitializer) str2=dummy
str1 = hoge
str2 = Hello, hoge
str3 = Hello, hoge

ActionScript 3.0の場合

ActionScriptの場合は、先にフィールドのイニシャライザが実行され、その後でスタティックイニシャライザが実行されているみたい。

package
{ 
    public class StaticInitializer 
    {
        public static var str1:String = "hoge";
        public static var str2:String = "dummy";
        
        {
            trace("(StaticInitializer) str2=" + str2); 
            str2 = "Hello, " + str1; 
        }
        public static var str3:String = str2;

        public function StaticInitializer() {} 
    }
}
<?xml version="1.0" encoding="utf-8"?> 
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> 
    <mx:Script> 
    <![CDATA[ 
        public function dump():void 
        {
            trace('str1 = ' + StaticInitializer.str1); 
            trace('str2 = ' + StaticInitializer.str2); 
            trace('str3 = ' + StaticInitializer.str3); 
        } 
    ]]>
    </mx:Script> 
    <mx:Button x="10" y="10" label="ボタン" click="dump()"/> 
</mx:Application>

(結果)
(StaticInitializer) str2=dummy
str1 = hoge
str2 = Hello, hoge
str3 = dummy

ActionScript 3.0のデフォルト値とundefined

デフォルト値

変数宣言した際に初期値を指定しなかった場合のデフォルト値をメモ。Number型のデフォルト値はnullじゃなくてNaNになっています。

デフォルト値
Boolean false
String null
int 0
uint 0
Number NaN
Object(任意クラス) null
型宣言なし undefined

型ごとにデフォルト値が決まっているので、型を指定しておけば不定値はないのですが、型を指定しない場合はundefinedという特別な値が設定されます。

undefined

変数の値をいじった後でundefinedを変数に代入するとどうなるか試してみました。
結果は、undefinedを代入するとデフォルト値が設定されます。

ECMA-262仕様によると、undefinedは文字列型にキャストしたときは"undefined"、数値型にキャストしたときはNaNを返すらしいです。このため、てっきりundefinedの代入は、undefinedをキャストした値が入るかと思いきや、デフォルト値が設定される挙動になりました。下記のStringの結果をみると、undefined代入後は文字列"undefined"ではなくデフォルト値nullが入っています。

var b:Boolean = true;
trace(b); // => true
b = undefined;
trace(b); // => false

var s:String = "hoge";
trace(s); // => hoge
s = undefined;
trace(s); // => null  "undefined"ではない

var i:int = 100;
trace(i); // => 100
i = undefined;
trace(i); // => 0

var n:Number = 100;
trace(n);  // => 100
n = undefined;
trace(n);  // => NaN

var o:Object = "hoge";
trace(o); // => hoge
o = undefined;
trace(o); // => null

var undef = "hoge";
trace(undef); // hoge
undef = undefined;
trace(undef); // => undefined


nullとundefinedを==で比較するとtrueが返却されます。どうやらundefinedが比較時にnullに変換されるようです。===で比較した場合は、当然falseが返却されます。

// null is undefinedが表示される
var undef;
if(null == undef)
{
    trace("null is undefined");
}
// コンパイルすると警告を受ける
// 1012: Null 型の変数は undefined にすることができません。値 undefined は Null に型強制された後で比較されます。
if(null == undefined)
{	
}

ちなみに、undefinedをtypeofすると、文字列"undefined"が返却されます。

var undef;
var str:String = typeof undef;
trace(str + ", " + str.length); // => undefined

あと、関数でundefinedの引数や戻り値を指定したい場合は、*を指定します。

public function func(value:*):*
{
    return value;
}

ActionScript スーパークラスのコンストラクタ呼出しタイミング

Javaだと子クラスのコンストラクタの先頭でしか親クラスのコンストラクタが呼べないけど、ActionScriptは子クラスのコンストラクタの任意の場所で親クラスのコンストラクタを呼べるみたいです。

[Java]

public class Parent { 
    public Parent() { 
        System.out.println("Parent constructor"); 
    } 
} 

public class Child extends Parent { 
    public Child() { 
        super(); // ←宣言していなくても勝手にこの位置に挿入される 
        System.out.println("Child constructor");
    } 
}

Child c = new Child(); 

(結果)
>Parent cunstructor
>Child cunstructor


Javaだと↓はNG。ActionScriptだとOK。

public class Child extends Parent {
    public Child() {
        System.out.println("Child cunstructor"); 
        super(); // ←メソッドの先頭でしか呼び出せない 
    }
}

ActionScriptの場合でも、明示的にsuperを呼び出さない場合は、ちゃんと自動的にコンストラクタの先頭でsuperが呼び出される。

superの呼出しタイミングをコンストラクタの先頭以外にするのって、親が生まれる前に子ができる瞬間があるってことですね。

ActionScriptで動的なインスタンス生成を行う

クラス名をもらって、そのクラス名から動的にインスタンスを生成したいときに、flash.utils.getDefinitionByNameが使えます。
引数に指定したクラス名から、クラスの参照を取得することができるので、そこからインスタンスを生成できます。

// 戻り値がObjectなので、Classでキャストしておく
var myClass:Class = flash.utils.getDefinitionByName("Hoge") as Class;
var myInstance:Object = new myClass();


たとえば、インタフェースが統一されているけど、その実装クラスは実行時にしか判明しない場合、以下のようにして動的なインスタンス生成ができます。

public function createCommand(className:String):ICommand
{
    var command:Class = flash.utils.getDefinitionByName(className) as Class;
    return new command as ICommand;
}

BlazeDSでHello World

FlexJavaを連携させるために、BlazeDSを使ってみます。
BlazeDSservletです。どんな実装をしているのか気になるところですが、とりあえず動かしてみます。

準備

出来上がりイメージ

こんな感じで、テキストインプットに入力した文字の先頭に、"Hello, "を付け加えて返却するサーバサービスとFlex画面を連携させます。

サーバ側プロジェクトを作成

eclipse上で、Dynamic Web Projectを作成します。
プロジェクト名:hello_blazeds_server
ターゲットランタイム:blazeds-turnkey付属のtomcat 6
サーブレットバージョン:2.5

lib
プロジェクトを作成したら、WEB-INF/libの下に、blazedsで必要なライブラリを格納します。
blazeds-turnkey-3.2.0.3978/tomcat/webapps/samples/WEB-INF/libの下に入っているので、それを持ってきます。ここでは、以下の4つをコピーしました。

  • backport-util-concurrent.jar
  • flex-messaging-common.jar
  • flex-messaging-core.jar
  • flex-messaging-remoting.jar

web.xml
まず、web.xmlを編集します。リスナーとサーブレットの設定を追加します。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<display-name>hello_blazeds_server</display-name>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.htm</welcome-file>
		<welcome-file>default.jsp</welcome-file>
	</welcome-file-list>

	<!-- ▼▼▼ Listener ▼▼▼ -->
	<listener>
		<listener-class>flex.messaging.HttpFlexSession</listener-class>
	</listener>
	<!-- ▲▲▲ Listener ▲▲▲ -->

	<!-- MessageBroker Servlet -->
	<!-- ▼▼▼ Servlet ▼▼▼ -->
	<servlet>
		<servlet-name>MessageBrokerServlet</servlet-name>
		<servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
		<init-param>
			<param-name>services.configuration.file</param-name>
			<param-value>/WEB-INF/flex/services-config.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>MessageBrokerServlet</servlet-name>
		<url-pattern>/messagebroker/*</url-pattern>
	</servlet-mapping>
	<!-- ▲▲▲ Servlet ▲▲▲ -->
</web-app>

services-config.xml
次にFlex用の設定ファイルを作成していきます。
WebContent/WEB-INFの下に「flex」フォルダを作成します。そのフォルダの中にservices-config.xmlを作成します。
services-config.xmlではサービスの設定をするのですが、service-includeタグを使うことで、部分的に定義を外部化できるようです。ここでは、Javaサービスの設定をremoting-config.xmlに外部化しました。

(WebContent/WEB-INF/flex/services-config.xml)

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
	<services>
		<service-include file-path="remoting-config.xml" />
	</services>
	
	<channels>
		<channel-definition id="my-amf"
			class="mx.messaging.channels.AMFChannel">
			<endpoint
				url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf"
				class="flex.messaging.endpoints.AMFEndpoint" />
			<properties>
				<polling-enabled>false</polling-enabled>
			</properties>
		</channel-definition>
	</channels>
</services-config>

remoting-config.xml
services-config.xmlから外部化したリモートサービスの設定を行います。デフォルトのチャネルをservices-config.xmlで定義した"my-amf"にしておきます。タグでは呼出し先のJavaサービスを記述します。

(WebContent/WEB-INF/flex/remoting-config.xml)

<?xml version="1.0" encoding="UTF-8"?>
<service id="remoting-service" class="flex.messaging.services.RemotingService">
	<adapters>
		<adapter-definition id="java-object"
			class="flex.messaging.services.remoting.adapters.JavaAdapter"
			default="true" />
	</adapters>

	<default-channels>
		<channel ref="my-amf" />
	</default-channels>

	<destination id="<span style="color:#FF0000;font-weight:bold;">hello</span>">
		<properties>
			<source>sample.hello.HelloService</source>
		</properties>
	</destination>
</service>

Javaサービス
Flex画面側から呼出すメソッドを定義します。

(src/sample/hello/HelloService.java)

package sample.hello;

public class HelloService {
	public String sayHello(String name) {
		return "hello, " + name;
	}
}

flex側プロジェクト作成

RemoteObjectを定義しておき、destination属性にremoting-config.xmlで定義したhelloを指定します。endpoint属性には、services-config.xmlで定義したチャネルのendpointを指定します。services-config.xmlと違って、変数によるバインドが効かないので、ここでは設定値をベタ書きにしています。
blazeds-turnkeyに付属するtomcatのポート番号は8400で設定されているので注意してください。
ボタンクリック時のイベントとしてsayHelloメソッドの呼出しを指定します。

(src/HelloBlazeDS.mxml)

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
	<mx:RemoteObject
		id="service"
		destination="hello"
		endpoint="http://localhost:8400/hello_blazeds_server/messagebroker/amf"/>
	<mx:TextInput x="10" y="20" id="helloText"/>
	<mx:Button x="178" y="20" label="Send" click="service.sayHello(helloText.text)"/>
	<mx:Label x="10" y="50" text="{service.sayHello.lastResult}"/>
</mx:Application>

完成

以上でhelloサンプルの完成。
blazedsのサンプルをみると、services-config.xmlのほかに、messaging-config.xmlやproxy-config.xml、remoting-config.xmlが入っているけど、実際に必要なのはservices-config.xmlだけでした。他の定義ファイルは、services-config.xmlの定義を外部化しただけなんですね。

参考

http://blog.isocchi.com/2008/04/javaflexblazedsflexjava.html
http://www.stbbs.net/blog/2008/01/blazedsweb.html