blob: cf5fb15e8c08b1d30afdad1b7617241034759938 [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<!--
Copyright (c) 2009, 2010, 2011, 2014 Oracle and/or its affiliates. All rights reserved.
-->
<html>
<head>
<title>WebアプリケーションでのWebSocket APIの使用</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >
<meta name="description" content="A tutorial on how to use NetBeans IDE to use the WebSocket API in a Java EE 7 web application.">
<link rel="stylesheet" href="../../../netbeans.css">
<meta name="author" content="ken ganfield">
</head>
<body>
<h1>WebアプリケーションでのWebSocket APIの使用</h1>
<p>このチュートリアルでは、1つのサーバー・アプリケーションに接続されているクライアント・ブラウザの間のコラボレーションを可能にする単純なWebアプリケーションを作成する方法を示します。ユーザーがクライアント・ブラウザでキャンバスにグラフィック要素を描画すると、接続されているすべてのクライアントのキャンバスにその要素が表示されます。使用方法ブラウザがWebページをロードすると、クライアント側のスクリプトはWebSocketハンドシェイク・リクエストをアプリケーション・サーバーに送信します。アプリケーションは、セッションで接続されているクライアントからJSONおよびバイナリ・メッセージを受け付けて、接続されているすべてのクライアントにメッセージをブロードキャストできます。</p>
<p>このチュートリアルでは、ブラウザ・クライアントとアプリケーション・サーバーとの間の双方向の通信を可能にするJava API for WebSocket (<a href="http://www.jcp.org/en/jsr/detail?id=356">JSR 356</a>)を使用してWebアプリケーションを作成します。Java API for WebSocketでは、WebSocket Javaコンポーネントの作成、WebSocketイベントの開始とインターセプト、WebSocketのテキストおよびバイナリのメッセージの作成と消費のサポートが提供されます。チュートリアルでは、Java API for JSON Processing (<a href="http://jcp.org/en/jsr/detail?id=353">JSR 353</a>)を使用してJSONを生成および消費する方法も示します。Java API for WebSocketおよびJava API for JSON ProcessingはJava EE 7プラットフォーム(<a href="http://jcp.org/en/jsr/detail?id=342">JSR 342</a>)の一部です。</p>
<p>アプリケーションには、WebSocketエンドポイント、デコーダおよびエンコーダのインタフェース、Webページ、およびページのロード時またはWebページのフォームからの起動時にクライアント・ブラウザで実行されるJavaScriptファイルが含まれます。Java EE 7テクノロジのリファレンス実装であるGlassFish Server Open Source Edition 4にアプリケーションをデプロイします。</p>
<p class="notes"><strong>注意:</strong>このチュートリアルは、<a href="http://blog.arungupta.me/" target="_blank">Arun Gupta氏のブログ</a>で見ることができるブログ投稿<a href="https://blogs.oracle.com/arungupta/entry/collaborative_whiteboard_using_websocket_in" target="_blank"> Collaborative Whiteboard using WebSocket in GlassFish 4 - Text/JSON and Binary/ArrayBuffer Data Transfer (TOTD #189)</a>およびその他のブログ・エントリに基づいています。このブログにアクセスして、WebSocket APIやGlassFish 4の使用に関するその他の多くの有用なエントリをぜひ参照してください。</p>
<p class="tips"><a href="maven-websocketapi-screencast.html">「WebアプリケーションでのWebSocket APIの使用のビデオ」</a>も参照できます。</p>
<p><b>チュートリアルの課題</b></p>
<img alt="このページの内容は、NetBeans IDE 7.3、7.4および8.0に適用されます" class="stamp" src="../../../images_www/articles/73/netbeans-stamp-80-74-73.png" title="このページの内容は、NetBeans IDE 7.3、7.4および8.0に適用されます">
<ul>
<li><a href="#Exercise_1">Webアプリケーション・プロジェクトの作成</a></li>
<li><a href="#createendpoint">WebSocketエンドポイントの作成</a>
<ul>
<li><a href="#createendpoint1">エンドポイントの作成</a></li>
<li><a href="#createendpoint2">WebSocketセッションの開始</a></li>
<li><a href="#createendpoint3">エンドポイントのテスト</a></li>
</ul>
</li>
<li><a href="#createwhiteboard">ホワイトボードの作成</a>
<ul>
<li><a href="#createwhiteboard1">キャンバスの追加</a></li>
<li><a href="#createwhiteboard2">POJOの作成</a></li>
<li><a href="#createwhiteboard3">座標クラスの作成</a></li>
<li><a href="#createwhiteboard6">JSON文字列の生成</a></li>
<li><a href="#createwhiteboard4">エンコーダおよびデコーダ・インタフェースの実装</a></li>
<li><a href="#createwhiteboard5">アプリケーションの実行</a></li>
</ul>
</li>
<li><a href="#sendbinary">エンドポイントへのバイナリ・データの送信</a></li>
<!--<li><a href="#Exercise_7">Downloading the Solution Project</a></li>-->
</ul>
<p><b>このチュートリアルに従うには、次のソフトウェアとリソースが必要です。</b></p>
<table>
<tbody>
<tr>
<th class="tblheader" scope="col">ソフトウェアまたはリソース</th>
<th class="tblheader" scope="col">必須バージョン</th>
</tr>
<tr>
<td class="tbltd1"><a href="https://netbeans.org/downloads/index.html">NetBeans IDE</a></td>
<td class="tbltd1">7.3.1、7.4、8.0、Java EEバージョン</td>
</tr>
<tr>
<td class="tbltd1"><a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">Java Development Kit (JDK)</a></td>
<td class="tbltd1">バージョン7または8</td>
</tr>
<tr>
<td class="tbltd1"><a href="https://glassfish.java.net/">GlassFish Server Open Source Edition</a></td>
<td class="tbltd1">4</td>
</tr>
</tbody>
</table>
<p class="notes"><strong>注意:</strong>GlassFish 4は、NetBeans IDEのJava EEダウンロード・バンドルにバンドルされています。</p>
<p><b>前提条件</b></p>
<p>このドキュメントは、次のテクノロジについて基本的な知識またはプログラミング経験を持つ読者を想定して書かれています。</p>
<ul>
<li>Javaプログラミング</li>
<li>JavaScript/HTMLプログラミング</li>
<li>NetBeans IDE</li>
</ul>
<p>このチュートリアルを開始する前に、必要に応じて次のドキュメントをお読みください。</p>
<ul>
<li><a href="http://wiki.netbeans.org/MavenBestPractices" target="_blank">NetBeans IDEでのApache Mavenのベスト・プラクティス</a></li>
<li><a href="http://books.sonatype.com/mvnref-book/reference/introduction.html" target="_blank">Chapter 1. Introducing Apache Maven</a> (<a href="http://books.sonatype.com/mvnref-book/reference/index.html" target="_blank">Maven: The Complete Reference </a>)</li>
</ul>
<p class="tips"><a href="https://netbeans.org/projects/samples/downloads/download/Samples/JavaEE/WhiteboardApp.zip">終了したプロジェクトのZIPアーカイブ</a>はダウンロードできます。</p>
<!-- ===================================================================================== -->
<a name="Exercise_1"></a>
<!--Exercise 1: -->
<h2>Webアプリケーション・プロジェクトの作成</h2>
<p>この課題の目標は、IDEで新規プロジェクト・ウィザードを使用してWebアプリケーション・プロジェクトを作成することです。プロジェクトを作成する際、Java EEバージョンとして「Java EE 7」を、アプリケーション・サーバーとしてGlassFish 4を選択します。GlassFish 4はJava EE 7プラットフォームのリファレンス実装です。このチュートリアルでアプリケーションを作成するには、IDEに登録されたJava EE 7をサポートするアプリケーション・サーバーが必要です。</p>
<ol>
<li>メイン・メニューから「ファイル」>「新規プロジェクト」(Windowsの場合は[Ctrl]-[Shift]-[N]、Macの場合は[⌘]-[Shift]-[N])を選択します。</li>
<li>「Maven」カテゴリから「Webアプリケーション」を選択します。「次」をクリックします。</li>
<li>「プロジェクト名」に<strong>「WhiteboardApp」</strong>と入力し、プロジェクトの場所を設定します。</li>
<li>「グループID」に<strong>「org.sample」</strong>と入力します。「次」をクリックします。</li>
<li>「サーバー」に<strong>「GlassFish Server 4.0」</strong>を選択します。 </li>
<li>「Java EEバージョン」を<strong>「Java EE 7 Web」</strong>に設定します。「終了」をクリックします。<br> <img alt="新規プロジェクト・ウィザードのプロジェクトの詳細" class="margin-around b-all" src="../../../images_www/articles/73/javaee/ee7-websocket/websocket-newproject.png" title="新規プロジェクト・ウィザードの「サーバー」および「Java EEバージョン」">
</li>
</ol>
<p>「終了」をクリックすると、IDEがプロジェクトを作成し、そのプロジェクトが「プロジェクト」ウィンドウで開きます。</p>
<!-- ===================================================================================== -->
<a name="createendpoint"></a>
<h2>WebSocketエンドポイントの作成</h2>
<p>この項では、WebSocketエンドポイント・クラスおよびJavaScriptファイルを作成します。WebSocketエンドポイント・クラスには、セッションのオープン時に実行される基本的なメソッドを含めます。次に、ページのロード時にサーバーとのハンドシェイクを開始するJavaScriptファイルを作成します。次に、接続が正常であることをテストするアプリケーションを実行します。</p>
<p class="tips">WebSocket APIおよび注釈の使用の詳細は、<a href="https://javaee-spec.java.net/nonav/javadocs/javax/websocket/package-summary.html" target="_blank"> javax.websocket</a>パッケージのサマリーを参照してください。</p>
<div class="indent">
<a name="createendpoint1"></a>
<h3>エンドポイントの作成</h3>
<p>この課題では、IDEのウィザードを利用してWebSocketエンドポイント・クラスを作成します。</p>
<ol>
<li>「プロジェクト」ウィンドウで「ソース・パッケージ」ノードを右クリックし、「新規」>「その他」を選択します。</li>
<li>「Web」カテゴリで「WebSocketエンドポイント」を選択します。「次」をクリックします。</li>
<li>「クラス名」に<strong>「MyWhiteboard」</strong>と入力します。</li>
<li>「パッケージ」ドロップダウン・リストで「<tt>org.sample.whiteboardapp</tt>」を選択します。</li>
<li>「WebSocket URI」に<strong>「/whiteboardendpoint」</strong>と入力します。「終了」をクリックします。<br> <img alt="新規ファイル・ウィザードのWebSocketエンドポイント" class="margin-around b-all" src="../../../images_www/articles/73/javaee/ee7-websocket/websocket-newendpoint.png" title="新規ファイル・ウィザードのWebSocketエンドポイント">
<p>「終了」をクリックすると、IDEによってWebSocketエンドポイント・クラスが生成され、ソース・エディタでファイルが開きます。エディタで、IDEによってWebSocket APIの一部である注釈が生成されたことを確認できます。クラスには、クラスがエンドポイントであることを識別する<tt><a href="https://javaee-spec.java.net/nonav/javadocs/javax/websocket/server/ServerEndpoint.html" target="_blank">@ServerEndpoint</a></tt>という注釈が付けられ、注釈のパラメータとしてWebSocket URIが指定されています。IDEによって<tt><a href="https://javaee-spec.java.net/nonav/javadocs/javax/websocket/OnMessage.html" target="_blank">@OnMessage</a></tt>という注釈が付けられたデフォルトの<tt>onMessage</tt>メソッドも生成されました。<tt>@OnMessage</tt>という注釈が付けられたメソッドは、クライアントがWebSocketメッセージを受信するたびに起動されます。</p>
<pre class="examplecode">
@ServerEndpoint("/whiteboardendpoint")
public class MyWhiteboard {
@OnMessage
public String onMessage(String message) {
return null;
}
}</pre>
</li>
<li>次のフィールド(<strong>太字</strong>部分)をクラスに追加します。
<pre class="examplecode">
@ServerEndpoint("/whiteboardendpoint")
public class MyWhiteboard {
<strong>private static Set&lt;Session&gt; peers = Collections.synchronizedSet(new HashSet&lt;Session&gt;());</strong>
@OnMessage
public String onMessage(String message) {
return null;
}
}</pre>
</li>
<li>次の<tt>onOpen</tt>および<tt>onClose</tt>メソッドを追加します。
<pre class="examplecode">
@OnOpen
public void onOpen (Session peer) {
peers.add(peer);
}
@OnClose
public void onClose (Session peer) {
peers.remove(peer);
}</pre>
<p><tt>onOpen</tt>および<tt>onClose</tt>メソッドには、<tt><a href="https://javaee-spec.java.net/nonav/javadocs/javax/websocket/OnOpen.html" target="_blank">@OnOpen</a></tt>および<tt><a href="https://javaee-spec.java.net/nonav/javadocs/javax/websocket/OnClose.html" target="_blank">@OnClose</a></tt>のWebSocket API注釈が付けられています。<tt>@OnOpen</tt>という注釈が付けられたメソッドは、Webソケット・セッションが開かれたときにコールされます。この例では、注釈の付いた<tt>onOpen</tt>メソッドでブラウザ・クライアントを現在のセッションのピアのグループに追加し、<tt>onClose</tt>メソッドでブラウザをグループから削除します。</p>
<p class="tips">メソッドの生成には、ソース・エディタのヒントとコード補完を使用すると便利です。クラスの宣言の横の左マージンのヒント・グリフをクリックし(または、カーソルをクラスの宣言内に置いて[Alt]-[Enter])、ポップアップ・メニューでメソッドを選択します。コード補完をメソッドのコーディングに使用すると便利です。</p>
<img alt="ソース・エディタのコード・ヒントのスクリーンショット" class="margin-around b-all" src="../../../images_www/articles/73/javaee/ee7-websocket/websocket-endpoint-hint.png" title="ソース・エディタのコード・ヒント">
</li>
<li>エディタで右クリックし、「インポートを修正」を選択します([Alt]-[Shift]-[I]、Macの場合は[⌘]-[Shift]-[I])。変更を保存します。
<p><tt>javax.websocket</tt>のクラスのインポート文がファイルに追加されます。</p></li>
</ol>
<p>これでエンドポイントが作成されました。次にWebSocketセッションを開始するためのJavaScriptファイルを作成する必要があります。
</p>
<!-- ===================================================================================== -->
<a name="createendpoint2"></a>
<h3>WebSocketセッションの開始</h3>
<p>この課題では、WebSocketセッションを開始するJavaScriptファイルを作成します。ブラウザ・クライアントは、サーバーとのHTTPハンドシェイクを使用し、TCPを介してセッションに参加します。JavaScriptファイルで、エンドポイントの<tt>wsURI</tt>の名前を指定し、WebSocketを宣言します。<tt>wsURI</tt> URIスキームはWebSocketプロトコルの一部で、アプリケーションのエンドポイントのパスを指定します。</p>
<ol>
<li>「プロジェクト」ウィンドウでプロジェクト・ノードを右クリックし、「新規」>「その他」を選択します。</li>
<li>新規ファイル・ウィザードの「Web」カテゴリで「JavaScriptファイル」を選択します。「次」をクリックします。</li>
<li>「JavaScriptファイル名」に<strong>「websocket」</strong>と入力します。「終了」をクリックします。</li>
<li>次のコードをJavaScriptファイルに追加します。
<pre class="examplecode">
var wsUri = "ws://" + document.location.host + document.location.pathname + "whiteboardendpoint";
var websocket = new WebSocket(wsUri);
websocket.onerror = function(evt) { onError(evt) };
function onError(evt) {
writeToScreen('&lt;span style="color: red;"&gt;ERROR:&lt;/span&gt; ' + evt.data);
}</pre>
<p>このスクリプトは、ブラウザによって<tt>websocket.js</tt>がロードされる際、サーバーとセッション・ハンドシェイクを開始します。</p>
</li>
<li><tt>index.html</tt>を開き、ページのロードの終了時に<tt>websocket.js</tt>をロードする次のコード(<strong>太字</strong>部分)をファイルの最後に追加します。
<pre class="examplecode">&lt;body&gt;
<strong>&lt;h1&gt;Collaborative Whiteboard App&lt;/h1&gt;
&lt;script type="text/javascript" src="websocket.js"&gt;&lt;/script&gt;</strong>
&lt;/body&gt;</pre>
</li>
</ol>
<p>これで、WebSocketエンドポイントが機能していること、セッションが開始されたこと、およびクライアントがセッションに追加されたことをテストできます。</p>
<!-- ===================================================================================== -->
<a name="createendpoint3"></a>
<h3>エンドポイントのテスト</h3>
<p>この課題では、ブラウザがエンドポイントに接続されたら、ブラウザ・ウィンドウに<tt>wsURI</tt>を出力するよう、簡単なメソッドをいくつかJavaScriptに追加します。</p>
<ol>
<li>次の<tt>&lt;div></tt>タグ(<strong>太字</strong>部分)を<tt>index.html</tt>に追加します。
<pre class="examplecode">&lt;h1&gt;Collaborative Whiteboard App&lt;/h1&gt;
<strong>&lt;div id="output"&gt;&lt;/div&gt;</strong>
&lt;script type="text/javascript" src="websocket.js"&gt;&lt;/script&gt;</pre>
</li>
<li>次の宣言とメソッドを<tt>websocket.js</tt>に追加します。変更を保存します。
<pre class="examplecode">// For testing purposes
var output = document.getElementById("output");
websocket.onopen = function(evt) { onOpen(evt) };
function writeToScreen(message) {
output.innerHTML += message + "&lt;br&gt;";
}
function onOpen() {
writeToScreen("Connected to " + wsUri);
}
// End test functions</pre>
<p>ページがロードされると、JavaScript関数は、ブラウザがエンドポイントに接続されていることを示すメッセージを出力します。エンドポイントが正しく実行されていることを確認したら、関数を削除できます。 </p>
</li>
<li>「プロジェクト」ウィンドウでプロジェクトを右クリックし、「実行」を選択します。</li>
</ol>
<p>アプリケーションを実行すると、IDEでGlassFishサーバーが起動され、アプリケーションがビルドおよびデプロイされます。ブラウザでindexページが開かれ、ブラウザ・ウィンドウに次のメッセージが表示されます。</p>
<img alt="ブラウザ・ウィンドウ内のエンドポイントへの接続メッセージ" class="margin-around b-all" src="../../../images_www/articles/73/javaee/ee7-websocket/websocket-browser1.png" title="ブラウザ・ウィンドウ内のエンドポイントへの接続メッセージ">
<p>ブラウザ・ウィンドウに、メッセージが受け付けられたエンドポイント(<tt>http://localhost:8080/WhiteboardApp/whiteboardendpoint</tt>)が表示されます。</p>
</div>
<!-- ===================================================================================== -->
<a name="createwhiteboard"></a>
<h2>ホワイトボードの作成</h2>
<p>この項では、JSONテキスト・メッセージを送受信するクラスおよびJavaScriptファイルを作成します。コンテンツ、およびペイントブラシの形状と色を指定するラジオ・ボタンを含むHTML<tt>&lt;form></tt>を描画および表示するための<a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html">HTML5 Canvas</a>要素も追加します。</p>
<!-- ===================================================================================== -->
<div class="indent">
<a name="createwhiteboard1"></a>
<h3>Webページへのキャンバスの追加</h3>
<p>この課題では、<tt>canvas</tt>要素および<tt>form</tt>要素をデフォルトのindexページに追加します。フォームのチェックボックスによって、キャンバスのペイントブラシのプロパティが決まります。</p>
<ol>
<li>ソース・エディタで<tt>index.html</tt>を開きます。</li>
<li>エンドポイントのテスト用に追加した<tt>&lt;div></tt>タグを削除し、開始のbodyタグの後に次の<tt>&lt;table></tt>および<tt>&lt;form></tt>要素(<strong>太字</strong>部分)を追加します。
<pre class="examplecode">&lt;h1&gt;Collaborative Whiteboard App&lt;/h1&gt;
<strong>&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;form name="inputForm"&gt;
&lt;/form&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;</strong>
&lt;script type="text/javascript" src="websocket.js"&gt;&lt;/script&gt;
&lt;/body&gt;</pre>
</li>
<li>canvas要素用に次のコード(<strong>太字</strong>部分)を追加します。
<pre class="examplecode">
&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;
<strong>&lt;canvas id="myCanvas" width="150" height="150" style="border:1px solid #000000;"&gt;&lt;/canvas&gt;</strong>
&lt;/td&gt;</pre>
</li>
<li>次の<tt>&lt;table></tt>を追加して、色と形状を選択するラジオ・ボタンを追加します。変更を保存します。
<pre class="examplecode">
&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;canvas id="myCanvas" width="150" height="150" style="border:1px solid #000000;"&gt;&lt;/canvas&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;form name="inputForm"&gt;
<strong>&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;Color&lt;/th&gt;
&lt;td&gt;&lt;input type="radio" name="color" value="#FF0000" checked="true"&gt;Red&lt;/td&gt;
&lt;td&gt;&lt;input type="radio" name="color" value="#0000FF"&gt;Blue&lt;/td&gt;
&lt;td&gt;&lt;input type="radio" name="color" value="#FF9900"&gt;Orange&lt;/td&gt;
&lt;td&gt;&lt;input type="radio" name="color" value="#33CC33"&gt;Green&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;th&gt;Shape&lt;/th&gt;
&lt;td&gt;&lt;input type="radio" name="shape" value="square" checked="true"&gt;Square&lt;/td&gt;
&lt;td&gt;&lt;input type="radio" name="shape" value="circle"&gt;Circle&lt;/td&gt;
&lt;td&gt;&nbsp;&lt;/td&gt;
&lt;td&gt;&nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;</strong>
&lt;/form&gt;</pre>
<p>キャンバス上に描画された図形の形状、色、および座標は、JSONの構造の文字列に変換され、WebSocketエンドポイントにメッセージとして送信されます。</p>
</li>
</ol>
<!-- ===================================================================================== -->
<a name="createwhiteboard2"></a>
<h3>POJOの作成</h3>
<p>この課題では、単純なPOJOを作成します。</p>
<ol>
<li>プロジェクト・ノードを右クリックし、「新規」>「Javaクラス」を選択します。</li>
<li>「クラス名」に<strong>「Figure」</strong>と入力し、「パッケージ」ドロップダウン・リストで「<tt>org.sample.whiteboardapp</tt>」を選択します。「終了」をクリックします。</li>
<li>ソース・エディタで、次のコード(<strong>太字</strong>部分)を追加します。
<pre class="examplecode">public class Figure {
<strong>private JsonObject json;</strong>
}</pre>
<p>コードを追加すると、<tt>javax.json.JsonObject</tt>のインポート文を追加するよう求められます。求められない場合は、[Alt]-[Enter]を押します。</p>
<p class="tips"><tt>javax.json.JsonObject</tt>の詳細は、Java EE 7仕様の一部であるJava API for JSON Processing (<a href="http://jcp.org/en/jsr/detail?id=353">JSR 353</a>)を参照してください。</p>
</li>
<li><tt>json</tt>の取得および設定メソッドを作成します。
<p class="tips">「コードを挿入」ポップアップ・メニュー(Windowsの場合は[Alt]-[Insert]、Macの場合は[Ctrl]-[I])で取得および設定メソッドを選択すると、「取得メソッドおよび設定メソッドの生成」ダイアログ・ボックスが開きます。または、メイン・メニューから「ソース」>「コードを挿入」を選択します。</p>
<img alt="「取得メソッドおよび設定メソッドの生成」ダイアログ・ボックス" class="margin-around b-all" src="../../../images_www/articles/73/javaee/ee7-websocket/websocket-generategetter.png" title="「取得メソッドおよび設定メソッドの生成」ダイアログ・ボックス">
</li>
<li><tt>json</tt>のコンストラクタを追加します。
<pre class="examplecode">
public Figure(JsonObject json) {
this.json = json;
}</pre>
<p class="tips">「コードを挿入」ポップアップ・メニュー([Ctrl]-[I])で「コンストラクタ」を選択します。</p>
<img alt="「コンストラクタの生成」ポップアップ・メニュー" class="margin-around b-all" src="../../../images_www/articles/73/javaee/ee7-websocket/websocket-generateconstructor.png" title="「コンストラクタの生成」ポップアップ・メニュー">
</li>
<li>次の<tt>toString</tt>メソッドを追加します。
<pre class="examplecode">
@Override
public String toString() {
StringWriter writer = new StringWriter();
Json.createWriter(writer).write(json);
return writer.toString();
}</pre>
</li>
<li>エディタで右クリックし、「インポートを修正」を選択します([Alt]-[Shift]-[I]、Macの場合は[⌘]-[Shift]-[I])。変更を保存します。</li>
</ol>
<!-- ===================================================================================== -->
<a name="createwhiteboard3"></a>
<h3>座標クラスの作成</h3>
<p>ここで、キャンバスに描画される図形の座標のクラスを作成します。</p>
<ol>
<li>プロジェクト・ノードを右クリックし、「新規」>「Javaクラス」を選択します。</li>
<li>新規Javaクラス・ウィザードで「クラス名」に<strong>「Coordinates」</strong>と入力し、「パッケージ」ドロップダウン・リストで「<tt>org.sample.whiteboardapp</tt>」を選択します。「終了」をクリックします。</li>
<li>ソース・エディタで、次のコードを追加します。変更を保存します。
<pre class="examplecode">
private float x;
private float y;
public Coordinates() {
}
public Coordinates(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
</pre>
</li>
</ol>
<p>クラスには<tt>x</tt><tt>y</tt>座標のフィールドおよび取得と設定のメソッドのみが含まれます。</p>
<!-- ===================================================================================== -->
<a name="createwhiteboard6"></a>
<h3>JSON文字列の生成</h3>
<p>この課題では、<tt>canvas</tt>要素に描画される図形の詳細を、websocketエンドポイントに送信されるJSON構造にするJavaScriptファイルを作成します。</p>
<ol>
<li>プロジェクト・ノードを右クリックし、「新規」>「JavaScriptファイル」を選択して新規JavaScriptファイル・ウィザードを開きます。</li>
<li>「ファイル名」に<strong>「whiteboard」</strong>と入力します。「終了」をクリックします。
<p>「終了」をクリックすると、IDEで空のJavaScriptファイルが作成され、エディタでこのファイルが開きます。「プロジェクト」ウィンドウの「Webページ」ノードの下に新規ファイルが表示されます。</p></li>
<li>キャンバスを初期化し、イベント・リスナーを追加する次のコードを追加します。
<pre class="examplecode">
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
canvas.addEventListener("click", defineImage, false);</pre>
<p>ユーザーが<tt>canvas</tt>要素内をクリックすると、<tt>defineImage</tt>メソッドが起動されることがわかります。</p>
</li>
<li>次の<tt>getCurrentPos</tt><tt>defineImage</tt>および<tt>drawImageText</tt>メソッドを追加して、JSON構造を作成し、エンドポイントに送信します(<tt>sendText(json)</tt>)。
<pre class="examplecode">
function getCurrentPos(evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
function defineImage(evt) {
var currentPos = getCurrentPos(evt);
for (i = 0; i &lt; document.inputForm.color.length; i++) {
if (document.inputForm.color[i].checked) {
var color = document.inputForm.color[i];
break;
}
}
for (i = 0; i &lt; document.inputForm.shape.length; i++) {
if (document.inputForm.shape[i].checked) {
var shape = document.inputForm.shape[i];
break;
}
}
var json = JSON.stringify({
"shape": shape.value,
"color": color.value,
"coords": {
"x": currentPos.x,
"y": currentPos.y
}
});
drawImageText(json);
sendText(json);
}
function drawImageText(image) {
console.log("drawImageText");
var json = JSON.parse(image);
context.fillStyle = json.color;
switch (json.shape) {
case "circle":
context.beginPath();
context.arc(json.coords.x, json.coords.y, 5, 0, 2 * Math.PI, false);
context.fill();
break;
case "square":
default:
context.fillRect(json.coords.x, json.coords.y, 10, 10);
break;
}
}</pre>
<p>送信されるJSONの構造は次のようになります。</p>
<pre class="examplecode">{
"shape": "square",
"color": "#FF0000",
"coords": {
"x": 31.59999942779541,
"y": 49.91999053955078
}
} </pre>
<p><tt>websocket.send()</tt>を使用してJSON文字列を送信する<tt>sendText(json)</tt>メソッドを追加する必要があります。</p>
</li>
<li>エディタで<tt>websocket.js</tt>を開き、JSONをエンドポイントに送信するためのメソッドおよびエンドポイントからメッセージを受信したらイメージを描画するためのメソッドを追加します。
<pre class="examplecode">
websocket.onmessage = function(evt) { onMessage(evt) };
function sendText(json) {
console.log("sending text: " + json);
websocket.send(json);
}
function onMessage(evt) {
console.log("received: " + evt.data);
drawImageText(evt.data);
}</pre>
<p class="notes"><strong>注意:</strong>エンドポイントのテスト用に<tt>websocket.js</tt>に追加したコードは削除できます。</p>
</li>
<li><tt>whiteboard.js</tt>をロードする次の行(<strong>太字</strong>部分)を<tt>index.html</tt>の最後に追加します。
<pre class="examplecode">
&lt;/table&gt;
&lt;script type="text/javascript" src="websocket.js"&gt;&lt;/script&gt;
<strong>&lt;script type="text/javascript" src="whiteboard.js"&gt;&lt;/script&gt;</strong>
&lt;body&gt;
</pre>
</li>
</ol>
<!-- +++++++++++++++ Creating Implementations of Encoder and Decoder Interfaces +++++++++++++++ -->
<a name="createwhiteboard4"></a>
<h3>エンコーダおよびデコーダ・インタフェースの実装</h3>
<p>この課題では、デコーダおよびエンコーダ・インタフェースを実装するクラスを作成し、Webソケット・メッセージ(JSON)をPOJOクラス<tt>Figure</tt>にデコードし、エンドポイントに送信するために<tt>Figure</tt>をJSON文字列としてエンコードします。</p>
<p class="tips">詳細は、技術記事<a href="http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html">JSR 356、Java API for WebSocket</a>のメッセージ・タイプおよびエンコーダ、デコーダに関する項を参照してください。</p>
<ol>
<li>プロジェクト・ノードを右クリックし、「新規」>「Javaクラス」を選択します。</li>
<li>「クラス名」に<strong>「FigureEncoder」</strong>と入力し、「パッケージ」ドロップダウン・リストで「<tt>org.sample.whiteboardapp</tt>」を選択します。「終了」をクリックします。</li>
<li>ソース・エディタで次のコード(<strong>太字</strong>部分)を追加し、WebSocket Encoderインタフェースを実装します。
<pre class="examplecode">
public class FigureEncoder <strong>implements Encoder.Text&lt;Figure&gt;</strong> {
}</pre>
</li>
<li><tt>javax.websocket.Encoder</tt>のインポート文を追加し、抽象メソッドを実装します。
<p class="tips">クラスの宣言にカーソルを置き、[Alt]-[Enter]を押して、ポップアップ・メニューから<strong>「すべての抽象メソッドを実装」</strong>を選択します。</p></li>
<li>次の変更(<strong>太字</strong>部分)を加えて、生成された抽象メソッドを変更します。変更を保存します。
<pre class="examplecode">
@Override
public String encode(Figure <strong>figure</strong>) throws EncodeException {
<strong>return figure.getJson().toString();</strong>
}
@Override
public void init(EndpointConfig ec) {
<strong>System.out.println("init");</strong>
}
@Override
public void destroy() {
<strong>System.out.println("destroy");</strong>
}</pre>
</li>
<li>プロジェクト・ノードを右クリックし、「新規」>「Javaクラス」を選択します。</li>
<li>「クラス名」に<strong>「FigureDecoder」</strong>と入力し、「パッケージ」ドロップダウン・リストで「<tt>org.sample.whiteboardapp</tt>」を選択します。「終了」をクリックします。</li>
<li>ソース・エディタで、次のコード(<strong>太字</strong>部分)を追加し、WebSocket Decoderインタフェースを実装します。
<pre class="examplecode">
public class FigureDecoder <strong>implements Decoder.Text&lt;Figure&gt;</strong> {
}</pre>
</li>
<li><tt>javax.websocket.Decoder</tt>のインポート文を追加し、抽象メソッドを実装します。</li>
<li>生成された抽象メソッドに次の変更(<strong>太字</strong>部分)を加えます。
<pre class="examplecode">
@Override
public Figure decode(String <strong>string</strong>) throws DecodeException {
<strong>JsonObject jsonObject = Json.createReader(new StringReader(string)).readObject();
return new Figure(jsonObject);</strong>
}
@Override
public boolean willDecode(String <strong>string</strong>) {
<strong>try {
Json.createReader(new StringReader(string)).readObject();
return true;
} catch (JsonException ex) {
ex.printStackTrace();
return false;
}</strong>
}
@Override
public void init(EndpointConfig ec) {
<strong>System.out.println("init");</strong>
}
@Override
public void destroy() {
<strong>System.out.println("destroy");</strong>
}</pre>
</li>
<li>インポートを修正して変更内容を保存します。</li>
</ol>
<p>次に、<tt>MyWhiteboard.java</tt>を変更して、エンコーダとデコーダを指定する必要があります。</p>
<!-- +++++++++++++++ Running the Application +++++++++++++++ -->
<a name="createwhiteboard5"></a>
<h3>アプリケーションの実行</h3>
<p>これでアプリケーションを実行する準備がほぼ整いました。この課題では、WebSocketエンドポイント・クラスを変更してJSON文字列のエンコーダとデコーダを指定し、メッセージを受信したら、接続されているクライアントにJSON文字列を送信するメソッドを追加します。</p>
<ol>
<li>エディタで<tt>MyWhiteboard.java</tt>を開きます。</li>
<li><tt>@ServerEndpoint</tt>注釈を変更し、エンドポイントのエンコーダとデコーダを指定します。エンドポイントの名前のパラメータ<tt>value</tt>を明示的に指定する必要があります。
<pre class="examplecode">
@ServerEndpoint(<strong>value=</strong>"/whiteboardendpoint"<strong>, encoders = {FigureEncoder.class}, decoders = {FigureDecoder.class}</strong>)
</pre>
</li>
<li>デフォルトで生成された<tt>onMessage</tt>メソッドを削除します。</li>
<li>次の<tt>broadcastFigure</tt>メソッドを追加し、メソッドに<tt>@OnMessage</tt>の注釈を付けます。
<pre class="examplecode">
@OnMessage
public void broadcastFigure(Figure figure, Session session) throws IOException, EncodeException {
System.out.println("broadcastFigure: " + figure);
for (Session peer : peers) {
if (!peer.equals(session)) {
peer.getBasicRemote().sendObject(figure);
}
}
}</pre>
</li>
<li>エディタで右クリックし、「インポートを修正」を選択します([Alt]-[Shift]-[I]、Macの場合は[⌘]-[Shift]-[I])。変更を保存します。</li>
<li>「プロジェクト」ウィンドウでプロジェクトを右クリックし、「実行」を選択します。</li>
</ol>
<p>「実行」をクリックすると、IDEはブラウザ・ウィンドウで<a href="http://localhost:8080/WhiteboardApp/">http://localhost:8080/WhiteboardApp/</a>を開きます。</p>
<p class="notes"><strong>注意:</strong>以前のアプリケーションをアプリケーション・サーバーからアンデプロイするか、ブラウザでページを強制的に再ロードする必要がある場合があります。
</p>
<p>ブラウザ・メッセージを確認すると、キャンバスをクリックするたびに、文字列がJSONを介してエンドポイントに送信されていることがわかります。</p>
<img alt="ブラウザ内のアプリケーションのスクリーンショット" class="margin-around b-all" src="../../../images_www/articles/73/javaee/ee7-websocket/websocket-onebrowser.png" title="ブラウザ内の図形を含むキャンバスおよびWebコンソールに表示されたJSON">
<p>別のブラウザで<tt>http://localhost:8080/WhiteboardApp/</tt>を開くと、一方のブラウザのキャンバス内をクリックするたびに新しい円や正方形が他方のブラウザのキャンバスに複製されることがわかります。</p>
<img alt="2つのブラウザ内のアプリケーションのスクリーンショット" class="margin-around b-all" src="../../../images_www/articles/73/javaee/ee7-websocket/websocket-twobrowsers.png" title="エンドポイントを介してJSONを送信する2つのブラウザ">
</div>
<!-- ===================================================================================== -->
<a name="sendbinary"></a>
<h2>エンドポイントへのバイナリ・データの送信</h2>
<p>これで、アプリケーションで文字列を処理し、JSONを介してエンドポイントに送信できます。文字列は、接続されているクライアントに送信されます。この項では、バイナリ・データを送受信するようJavaScriptファイルを変更します。</p>
<p>バイナリ・データをエンドポイントに送信するために、WebSocketの<tt>binaryType</tt>プロパティを<tt>arraybuffer</tt>に設定する必要があります。これによって、WebSocketを使用したバイナリ転送は<tt>ArrayBuffer</tt>を介して行われることが保証されます。バイナリ・データ変換は、<tt>whiteboard.js</tt><tt>defineImageBinary</tt>メソッドによって行われます。</p>
<ol>
<li><tt>websocket.js</tt>を開き、WebSocketの<tt>binaryType</tt>プロパティを<tt>arraybuffer</tt>に設定する次のコードを追加します。
<pre class="examplecode">
websocket.binaryType = "arraybuffer";</pre>
</li>
<li>バイナリ・データをエンドポイントに送信する次のメソッドを追加します。
<pre class="examplecode">
function sendBinary(bytes) {
console.log("sending binary: " + Object.prototype.toString.call(bytes));
websocket.send(bytes);
}</pre>
</li>
<li><tt>onMessage</tt>メソッドを変更し、受信メッセージのデータ型に応じてキャンバスを更新するメソッドを選択する次のコード(<strong>太字</strong>部分)を追加します。
<pre class="examplecode">
function onMessage(evt) {
console.log("received: " + evt.data);
<strong>if (typeof evt.data == "string") {</strong>
drawImageText(evt.data);
<strong>} else {
drawImageBinary(evt.data);
}</strong>
}</pre>
<p>バイナリ・データのメッセージを受信すると、<tt>drawImageBinary</tt>メソッドが起動されます。</p>
</li>
<li><tt>whiteboard.js</tt>を開いて、次のメソッドを追加します。受信バイナリ・データの解析後、<tt>drawImageBinary</tt>メソッドを起動してキャンバスを更新します。<tt>defineImageBinary</tt>メソッドを使用して、バイナリ・データとしてキャンバスのスナップショットを準備します。
<pre class="examplecode">
function drawImageBinary(blob) {
var bytes = new Uint8Array(blob);
// console.log('drawImageBinary (bytes.length): ' + bytes.length);
var imageData = context.createImageData(canvas.width, canvas.height);
for (var i=8; i&lt;imageData.data.length; i++) {
imageData.data[i] = bytes[i];
}
context.putImageData(imageData, 0, 0);
var img = document.createElement('img');
img.height = canvas.height;
img.width = canvas.width;
img.src = canvas.toDataURL();
}
function defineImageBinary() {
var image = context.getImageData(0, 0, canvas.width, canvas.height);
var buffer = new ArrayBuffer(image.data.length);
var bytes = new Uint8Array(buffer);
for (var i=0; i&lt;bytes.length; i++) {
bytes[i] = image.data[i];
}
sendBinary(buffer);
}</pre>
<p>バイナリ・データを<tt>ArrayBuffer</tt>型として生成し、エンドポイントに送信する場合に<tt>defineImageBinary</tt>を起動する方法を追加する必要があります。</p>
</li>
<li><tt>index.html</tt>を開き、<tt>&lt;table></tt>要素を変更して、フォームの表に次の行を追加します。
<pre class="examplecode">
&lt;tr&gt;
&lt;th&gt;&nbsp;&lt;/th&gt;
&lt;td&gt;&lt;input type="submit" value="Send Snapshot" onclick="defineImageBinary(); return false;"&gt;&lt;/td&gt;
&lt;td&gt;&nbsp;&lt;/td&gt;
&lt;td&gt;&nbsp;&lt;/td&gt;
&lt;td&gt;&nbsp;&lt;/td&gt;
&lt;/tr&gt;
</pre>
<p>新しい行には、接続されているピアにキャンバスのバイナリ・スナップショットを送信する「Send Snapshot」ボタンが含まれます。ボタンがクリックされると、<tt>whiteboard.js</tt><tt>defineImageBinary</tt>メソッドが起動されます。</p>
</li>
<li><tt>MyWhiteboard.java</tt>を開き、エンドポイントがバイナリ・データのメッセージを受信すると、ピアにバイナリ・データを送信する次のメソッドを追加します。
<pre class="examplecode">
@OnMessage
public void broadcastSnapshot(ByteBuffer data, Session session) throws IOException {
System.out.println("broadcastBinary: " + data);
for (Session peer : peers) {
if (!peer.equals(session)) {
peer.getBasicRemote().sendBinary(data);
}
}
}</pre>
<p class="notes"><strong>注意:</strong><tt>java.nio.ByteBuffer</tt>のインポート文を追加する必要があります。
</p>
</li>
</ol>
<p class="tips">
アプリケーションを変更して、ユーザーがエンドポイントへのデータの送信を停止できるようにできます。デフォルトでは、すべてのピアはページを開くとすぐに接続され、データはブラウザからすべての接続されているピアに送信されます。オプションが選択されていない場合にデータがエンドポイントに送信されないよう単純な条件文を追加できます。これは、受信するデータに影響しません。データは引き続き、エンドポイントから受信されます。</p>
<div class="indent">
<ol>
<li><tt>whiteboard.js</tt><tt>defineImage</tt>メソッドを変更し、次のコード(<strong>太字</strong>部分)を追加します。
<pre class="examplecode">
drawImageText(json);
<strong> if (document.getElementById("instant").checked) {</strong>
sendText(json);
<strong> }</strong>
}</pre>
<p>id <tt>checked</tt>の要素をチェックする条件コード</p>
</li>
<li><tt>index.html</tt>を開き、<tt>&lt;table></tt>要素を変更してフォームにチェックボックスを追加します。
<pre class="examplecode">
&lt;tr&gt;
&lt;th&gt;&nbsp;&lt;/th&gt;
&lt;td&gt;&lt;input type="submit" value="Send Snapshot" onclick="defineImageBinary(); return false;"&gt;&lt;/td&gt;
&lt;td&gt;<strong>&lt;input type="checkbox" id="instant" value="Online" checked="true"&gt;Online</strong>&lt;/td&gt;
&lt;td&gt;&nbsp;&lt;/td&gt;
&lt;td&gt;&nbsp;&lt;/td&gt;
&lt;/tr&gt;
</pre>
<p>「Online」チェックボックスが選択解除されている場合、データは送信されませんが、クライアントは引き続きエンドポイントからデータを受信します。</p>
</ol>
<p>「Send Snapshot」ボタンおよび「Online」チェックボックスを追加してアプリケーションを再度実行すると、indexページに新しい要素が表示されます。別のブラウザを開いて「Online」ボタンを選択解除すると、キャンバス内をクリックしたときにJSONメッセージが送信されないことがわかります。</p>
<img alt="ブラウザ内のアプリケーションのスクリーンショット" class="margin-around b-all" src="../../../images_www/articles/73/javaee/ee7-websocket/websocket-onebrowser-binary.png" title="バイナリ・データが送信されたというメッセージを表示するブラウザ内のWebコンソール">
<p>「Send Snapshot」をクリックすると、バイナリ・データがエンドポイントに送信され、接続されているクライアントにブロードキャストされます。</p>
</div>
<!--
<a name="Exercise_7"></a>
<h2>Downloading the Solution Project</h2>
<p>You can download the solution to this tutorial as a project in the following ways.</p>
<ul>
<li>Download <a href="https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252FMavenEnterpriseApp.zip">a zip archive of the finished project</a>.</li>
<li>Checkout the project sources from the NetBeans Samples by performing the following steps:
<ol>
<li>Choose Team &gt; Subversion &gt; Checkout from the main menu.</li>
<li>In the Checkout dialog box, enter the following Repository URL:<br>
<tt>https://svn.netbeans.org/svn/samples~samples-source-code</tt><br>
Click Next.</li>
<li>Click Browse to open the Browse Repostiory Folders dialog box.</li>
<li>Expand the root node and select <strong>samples/javaee/MavenEnterpriseApp</strong>. Click OK.</li>
<li>Specify the Local Folder for the sources (the local folder must be empty).</li>
<li>Click Finish.
<p>When you click Finish, the IDE initializes the local folder as a Subversion repository
and checks out the project sources.</p>
</li>
<li>Click Open Project in the dialog that appears when checkout is complete.</li>
</ol>
<p class="notes"><strong>Notes.</strong> For more about installing Subversion,
see the section on <a href="../ide/subversion.html#settingUp">Setting up Subversion</a> in the <a href="../ide/subversion.html">Guide to Subversion in NetBeans IDE</a>.</p>
</li>
</ul>
-->
<!--
<a name="Exercise_5"></a>
<h2>Troubleshooting</h2>
<p>The following are some of the problems you may encounter when creating your project.</p>
<div class="indent">
<h3 class="tutorial">Problem with JMS Resources</h3>
<p>When using the wizard to create JMS resources,
you may see the following server error message in the output window:</p>
<pre>[com.sun.enterprise.connectors.ConnectorRuntimeException:
JMS resource not created : jms/Queue]
</pre>
<p>This message could indicate that the JMS resource was not created or was not registered with the application server.
You can use the Admin Console of the application server to check, create and edit JMS resources.</p>
<p>To open the Admin Console, do the following:</p>
<ol>
<li>Confirm that the application server is running by expanding the Servers node in the Services window of the IDE.
A small green arrow next to the application server node indicates the server is running.</li>
<li>Right-click the application server node and choose View Admin Console to open the login window in your browser.</li>
<li>Log in to the server. The default user name and password are <tt>admin</tt> and <tt>adminadmin</tt>.</li>
<li>In the Admin Console in your browser, expand the Resources node and JMS Resources node in the left frame.</li>
<li>Click on the Connection Factories and Destination Resources links in the left frame to check if the resources are
registered with the server and if necessary modify the resources. If the resources do not exist, you can create them
in the Admin Console.</li>
</ol>
<p>You need to make sure that the JMS connection factory resource
in the PostMessage servlet is mapped to the correct JNDI name of the JMS connection factory resource
registered with the Sun Java System Application Server.</p>
<p>The following resources should be registered with the Sun Java System Application Server:</p>
<ul>
<li>a Destination resource with the JNDI name <tt>jms/NewMessage</tt> and type <tt>javax.jms.Queue</tt></li>
<li>a Connection Factory resource with the JNDI name <tt>jms/NewMessageFactory</tt> and type <tt>
javax.jms.QueueConnectionFactory</tt></li>
</ul>
<p>make sure that the import in PostMessage is not <tt>javax.resource.cci.ConnectionFactory</tt></p>
<h3 class="tutorial">Problem with the Datasource</h3>
</div>-->
<br>
<div class="feedback-box" ><a href="/about/contact_form.html?to=3&amp;subject=Feedback:%20Using%20the%20WebSocket%20API%20in%20a%20Web%20Application">このチュートリアルに関するご意見をお寄せください</a></div>
<br style="clear:both;" >
<!-- ======================================================================================= -->
<h2><a name="nextsteps"></a>関連項目</h2>
<p>NetBeans IDEを使用したJava EEアプリケーションの開発方法の詳細は、次のリソースを参照してください。
</p>
<ul>
<li>デモ: <a href="maven-websocketapi-screencast.html">WebアプリケーションでのWebSocket APIの使用</a></li>
<li><a href="javaee-intro.html">Java EEテクノロジ入門</a></li>
<li><a href="javaee-gettingstarted.html">Java EEアプリケーションの開始</a></li>
<li><a href="../../trails/java-ee.html">Java EEおよびJava Webの学習</a></li>
</ul>
<p>Java EEの使用に関する詳細は、<a href="http://download.oracle.com/javaee/6/tutorial/doc/">Java EEチュートリアル</a>を参照してください。</p>
<p><a href="../../../community/lists/top.html">nbj2eeメーリング・リストに登録する</a>ことによって、NetBeans IDE Java EE開発機能に関するご意見やご提案を送信したり、サポートを受けたり、最新の開発情報を入手したりできます。</p>
</body>
</html>