タイトル/名前 | 更新者 | 更新日 |
---|---|---|
JavaScript/WYSIWYGエディタ/contenteditable属性 + readonly | msakamoto-sf | 2014-08-24 00:33:44 |
JavaScript/正規表現参考リンク集 | msakamoto-sf | 2014-08-23 22:42:30 |
JavaScript/QUnit | msakamoto-sf | 2014-08-23 19:54:56 |
Java/正規表現(java.util.regex)の練習 | msakamoto-sf | 2014-08-18 21:00:55 |
Java/JAX-RS/JerseyでJSONリクエスト/レスポンスをJacksonのObjectMapperでLinkedHashMap<String, Object>でテキトーに読み書きするには | msakamoto-sf | 2014-08-10 22:54:02 |
Java/JAX-RS/Jerseyでmultipartのリクエストを受け取るには | msakamoto-sf | 2014-08-10 20:38:44 |
Java/Maven3/archetype:generateショートカット集 | msakamoto-sf | 2014-08-10 16:26:26 |
技術/Windows/コマンドプロンプトで折り返す(line break)にはキャレット記号(^) | msakamoto-sf | 2014-08-10 16:18:08 |
Java/ThreadLocalの使い方メモ | msakamoto-sf | 2014-08-03 19:32:31 |
技術/運用管理/Vagrant/"Vagrant Cloud"メモ | msakamoto-sf | 2014-08-03 00:45:20 |
WYSIWYGエディタを作るための基本的な仕組みとして、"contenteditable"属性や"designMode"プロパティがある。
ヒント:
ここで、"contenteditable"属性を有効にした領域をreadonly扱いにするには、keydownイベントでイベント伝播をストップすればよい。
具体的には、jQueryの場合はkeydownイベントがfalseを返せば良い。
上記を組み合わせたサンプル:
JavaScriptでの正規表現操作に関する参考リンク集
JavaScriptのテストフレームワークの一つ、QUnitの勉強メモ。
使い方は結構簡単。ブラウザ上で実行できて、テスト結果を見れる。
jsfiddleで練習してみた(QUnitの公式ドキュメントからのコピペ):
自分の練習:
Web上からJavaの正規表現をテストできるサイト:
Java/JAX-RS/Jerseyでmultipartのリクエストを受け取るには に引き続き、JerseyでJSONのリクエストを受け取ったり、JSONレスポンスを出力するやり方を調べてみました。
これもmultipartの例と同様、JAX-RS自体の仕様には含まれておらず、ライブラリやフレームワーク側でProviderなどのショートカット用のクラス・メソッドを用意してくれている状況です。
Jersey 2.x系:
ただしこの辺は、実務で使うとおもいっきりアプリ独自色に引きずられる場所なので、あまりライブラリやフレームワーク側のお仕着せやショートカットに依存させたくありませんでした。
そのため、MessageBodyReader/Writerを自作して、好きなJSONライブラリで好きなようにRead/Writeできる方式を今回は試してみました。
参考:
ということで、一番ゆるゆるに扱えるMap<String, Object>形式でJacksonにてMessageBodyReader/Writerを作ってみた例が以下になります。ぶっちぇけ前掲の "Jersey、JSONでボディマッピングの実装" のパクリ・・・です・・・。
ポイントとしては、Map<String, Object>ではなくLinkedHashMap<String, Object> にしているところです。
実は最初は、「Mapでイインジャネ?」とMap<String, Object>にしてたんですが・・・ObjectMapperのreadValue()、Map<String, Object>でパースした場合、実装型としてはLinkedHashMapのインスタンス返してくるんですよね・・・。
なので、MessageBodyWriterのisWritable()メソッドのtype引数がLinkedHashMap.classになってしまうので、そちらで合わせる必要が出てきました。
そうすると、リソースクラスで、戻り値だけLinkedHashMapで、引数がMapというのもバランスがおかしいように見えてきます。
→ というわけで、最終的に全部LinkedHashMapに揃えた次第です。
あんまりBean作りまくって厳密に型にはめる必要がなければ、一番ゆるゆるなMap<String, Object>形式でも良い意味で手抜きできますので、一応今回はそのケースで試してみました。
今回の記事では、Jersey 2.11でのmultipart/form-dataを受け取るやり方を調べてみました。
JAX-RS 2.0の仕様自体には、特別なmultipartリクエストの対応は盛り込まれていません。
もともと基本的にServletの仕様に乗っかっていて、フィルタやMessageReaderなどリクエストを間で見張って独自に変更できる仕組みを備えています。
そのため、multipartのような複雑なリクエストについては開発者の方でパース処理を実装する考え方のようです。
JerseyやRESTeasyなどメジャーな実装ライブラリでは、開発者が自力で実装しなくても、便利なアノテーションやフィルタ、Providerなどを既に用意してくれていますので、それを利用するのをまず検討しましょう。
ただし、それらはあくまでも実装ライブラリに依存したクラス名やアノテーション名になりますので、他の実装に切り替えた際はそこの修正が必要になると思われます。
Jersey本家ドキュメントをまず参照:
基本的な流れ:
サンプル:
以下、https://github.com/msakamoto-sf/jaxrs2-exercise-jersey-servlet を例に実施にmultipart受付機能を組み込む手順を、対応するコミットログと一緒に解説します。
解説:
Mavenプロジェクトなので、素直にdependencyに追加してます。
解説:本来のJAX-RSでは"Application"というクラスに、ResourceクラスやフィルタなどのProviderクラスを登録します。
Jerseyなど実装によっては、Javaコードで記述せずにweb.xmlで設定できたりします。
今回はJerseyでの解説になり、↑のcommit例では web.xml の org.glassfish.jersey.servlet.ServletContainer の初期パラメータとして、プロバイダのクラス名を与えられる "jersey.config.server.provider.classnames" に直接MultiPartFeatureを設定して登録させています。
公式ドキュメントだけでは以下のようにそっけないコード例しか無いので、慣れてないとどこを直せばよいのか戸惑いそうです。
Example 8.43. Creating JAX-RS application with MultiPart feature enabled. // Create JAX-RS application. final Application application = new ResourceConfig() .packages("org.glassfish.jersey.examples.multipart") .register(MultiPartFeature.class)
Jersey公式のmultipartのサンプルでは、ResourceConfigを継承した "MyApplication" クラスのコンストラクタで、super()で直接登録してしまってます。
https://github.com/jersey/jersey/blob/2.11/examples/multipart-webapp/src/main/java/org/glassfish/jersey/examples/multipart/webapp/MyApplication.java
@ApplicationPath("/") public class MyApplication extends ResourceConfig { public MyApplication() { super(MultiPartResource.class, MultiPartFieldInjectedResource.class, MultiPartFeature.class); } }
→このクラス名が、web.xml中で org.glassfish.jersey.servlet.ServletContainer の初期パラメータ "javax.ws.rs.Application" の値に設定されています。
https://github.com/jersey/jersey/blob/2.11/examples/multipart-webapp/src/main/webapp/WEB-INF/web.xml
今回の例では、1個のアップロードしか想定しない方を "FormDataContentDisposition + InputStream" のセットで受け取り、複数ファイルの受け取りを想定する方を FormDataBodyPart 型で受け取り、ループで処理してます。
以上になりますが、"jersey-media-multipart"の依存関係を見てたら Commons のFileUploadではなく、java.net製のmultipart処理ライブラリである "mimepull" というのを使ってました。ライセンスの都合でAPL2のライブラリが使えないせいか、それとも他の技術的な理由かは分かりませんが、commons-fileupload以外のmime処理の選択肢があることは歓迎できますので、頭の片隅に覚えておきたいと思いました。
実験等で頻繁にMavenプロジェクトを作るときに、archetype:generateのオプションをいちいち調べるのは面倒ですので、こちらによく使いそうなarchetypeオプションをまとめておきます。
※unix系のシェルでの複数行改行の"\"ですが、Windowsのコマンドプロンプトから実行する場合は"\"を"^"に置換してください。
Maven提供の "maven-archetype-quickstart" を使います。
archetype:generateを実行するとデフォルトでは対話モードでgroupIdやartifactIdを指定できます。
> mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Maven Stub Project (No POM) 1 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] >>> maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom >>> [INFO] [INFO] <<< maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom <<< [INFO] [INFO] --- maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom --- [INFO] Generating project in Interactive mode Define value for property 'groupId': : mvn-test Define value for property 'artifactId': : mvn-jar-test Define value for property 'version': 1.0-SNAPSHOT: : Define value for property 'package': mvn-test: : Confirm properties configuration: groupId: mvn-test artifactId: mvn-jar-test version: 1.0-SNAPSHOT package: mvn-test ...
"mvn-jar-test"ディレクトリが作成され、pom.xmlなどが展開されます。
より厳密にarchetypeを指定する場合:
mvn archetype:generate \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DarchetypeVersion=1.1
"-B"オプションまたは"-DinteractiveMode=false"を指定します。
mvn archetype:generate -B \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DarchetypeVersion=1.1 \ -DgroupId=mvn-test \ -DartifactId=mvn-jar-test \ -Dversion=1.0-SNAPSHOT \ -Dpackage=mvn.jar.test [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Maven Stub Project (No POM) 1 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] >>> maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom >>> [INFO] [INFO] <<< maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom <<< [INFO] [INFO] --- maven-archetype-plugin:2.0:generate (default-cli) @ standalone-pom --- [INFO] Generating project in Batch mode [INFO] Archetype repository missing. Using the one from [org.apache.maven.archetypes:maven-archetype-quickstart:1.1] found in catalog remote [INFO] ---------------------------------------------------------------------------- [INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.1 [INFO] ---------------------------------------------------------------------------- [INFO] Parameter: groupId, Value: mvn-test [INFO] Parameter: packageName, Value: mvn.jar.test [INFO] Parameter: package, Value: mvn.jar.test [INFO] Parameter: artifactId, Value: mvn-jar-test [INFO] Parameter: basedir, Value: c:\in_vitro\tmp\mvntest [INFO] Parameter: version, Value: 1.0-SNAPSHOT [INFO] ********************* End of debug info from resources from generated POM *********************** [INFO] project created from Old (1.x) Archetype in dir: c:\in_vitro\tmp\mvntest\mvn-jar-test [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.654s [INFO] Finished at: Sun Jun 10 18:07:00 JST 2012 [INFO] Final Memory: 7M/18M [INFO] ------------------------------------------------------------------------
2012-06時点でのオススメはcodehausのwebapp-jee5なので、先にそちらを紹介します。
codehausのarchetypeはJavaEEのバージョン番号で分かれています。以下にJavaEEのバージョン番号と対応するServlet/JSP/JSTLのバージョン番号をまとめておきます。
JavaEE6 : Servlet 3.0, JSP2.2 : Tomcat 7以上 JavaEE5 : Servlet 2.5, JSP 2.1 : Tomcat 6以上 J2EE 1.4 : Servlet 2.4, JSP 2.0 : Tomcat 5.x, 5.5.x以上 J2EE 1.3 : Servlet 2.3 JSP 1.2 -> TOmcat 4.x
今回はTomcat6以上で対応しているJavaEE5用のcodehausのWebアプリテンプレートを使ってみます。2012-06時点での最新版は1.3となっています。
ショートカット:
mvn archetype:generate -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=webapp-jee5
バッチ実行版:
mvn archetype:generate -B \ -DarchetypeGroupId=org.codehaus.mojo.archetypes \ -DarchetypeArtifactId=webapp-jee5 \ -DarchetypeVersion=1.3 \ -DgroupId=mvn-test \ -DartifactId=mvn-war-test \ -Dversion=1.0-SNAPSHOT \ -Dpackage=mvn.war.test
以下のようなディレクトリ構成が展開されます。javaパッケージディレクトリは空で作成されます。
mvn-war-test/ pom.xml src/ main/ java/... (空) webapp/ WEB-INF/web.xml index.jsp
codehausのWebアプリテンプレートですが、JavaEE6向けの、Servlet 3.0 APIベースのバージョンが出てました。2014-08時点での最新版は 1.5 となってます。
ショートカット:
mvn archetype:generate -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=webapp-javaee6
Maven提供の"maven-archetype-webapp"を紹介します。2012-06時点でのバージョン1.0を使っています。
ショートカット:
mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp
バッチ実行版:
mvn archetype:generate -B \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DarchetypeArtifactId=maven-archetype-webapp \ -DarchetypeVersion=1.0 \ -DgroupId=mvn-test \ -DartifactId=mvn-war-test \ -Dversion=1.0-SNAPSHOT \ -Dpackage=mvn.war.test
但し、maven-archetype-webappの場合は以下のようなソースが展開され、肝心のJavaディレクトリが含まれていません。
mvn-war-test/ pom.xml src/ main/ resources/ (空) webapp/ WEB-INF/web.xml index.jsp
というわけで個人的にはJavaディレクトリが含まれるcodehausがオススメです。(とはいえcodehaus側はresources/が無かったりするのですが・・・)
Maven提供ですと以下のリポジトリURLを覗いてみますと、色々サンプルがあるようです。
http://search.maven.org/#browse%7C-1174413781
codehausのarchetypesにも色々あります。
http://search.maven.org/#browse%7C-1053090499
例:キャレット記号(^)の直前の空白有無の影響に注意
> echo abc^ More? def ^ More? ghi^ More? jkl abcdef ghijkl
ThreadLocalの使い方参考リンク、アプリサーバで使うときのClassLoaderのリソースリークの注意点など。
使い方のイロハ:
リソースリーク関連:
Vagrantの利用は、以下のような魔法のコマンドを叩くところから始まります。
vagrant init hashicorp/precise64
この時、裏側では"Vagrant Cloud"からBoxファイルをダウンロードしています。
"Vagrant Cloud"はBoxファイルをダウンロードするための共有リポジトリとして利用できます。また、今回は試してませんが"Vagrant Share"をするときもVagrant Cloudへのログインアカウントが必要だったりします。
今回は、 技術/運用管理/Vagrant/"Minimal Desktop"インストールのCentOS 6.5のBox作例メモ で作成したCentOSのイメージをVagrant Cloudで公開してみました。
Vagrant Cloudは無料でも使えますが、いくつか制限があります。有料版を使うことで、それらの制限を解除して、企業内のクローズドな開発体制にマッチした機能が利用できます。
Vagrang CloudがWeb上のサービスであるのに対し、"Vagrant Share"はあくまでもクライアント側の機能になります。
今回は試してないので認識が間違ってるかもしれませんが、SSHのトンネリングなどを駆使して、ローカル上で起動しているVagrantのVMに対してインターネットからHTTP(S)でVM上のHTTPサーバにアクセスしたり、LAN内の他のマシンからSSHで接続できるようにするための機能と認識してます。
まずは参考URL:
そんなに複雑な登録作業では無いです。無料利用枠ではVagrant Cloud自体にBoxファイルをアップロードすることは出来ませんので、外部のWebサーバにBoxファイルを配置し、そのURLをVagrant Cloud側に登録する必要があります。
Range機能が使えないWebサーバは対応していない、というような情報も見かけましたので、Vagrantクライアントが直接そのURLからDLするのではなく、Vagrant Cloudが間に入って中継しているのかもしれません。
今回は 技術/運用管理/Vagrant/"Minimal Desktop"インストールのCentOS 6.5のBox作例メモ で作成したboxファイルをAmazon S3にアップして一般公開し、そのURLを登録しました。
ということで今回公開してみたBoxファイルです:
https://vagrantcloud.com/msakamoto-sf/centos65-minimal-desktop
↑のVagrant Cloudのページで、"vagrant init"コマンドの例が表示されてますので、そのままコピペすればOKです。
> vagrant init msakamoto-sf/centos65-minimal-desktop A `Vagrantfile` has been placed in this directory. You are now ready to `vagrant up` your first virtual environment! Please read the comments in the Vagrantfile as well as documentation on `vagrantup.com` for more information on using Vagrant.
まだこの時点ではBoxファイルのダウンロードは発生しません。"vagrant up"すると、自動的にダウンロードが始まります。
> vagrant up Bringing machine 'default' up with 'virtualbox' provider... ==> default: Box 'msakamoto-sf/centos65-minimal-desktop' could not be found. Attempting to find and install... default: Box Provider: virtualbox default: Box Version: >= 0 ==> default: Loading metadata for box 'msakamoto-sf/centos65-minimal-desktop' default: URL: https://vagrantcloud.com/msakamoto-sf/centos65-minimal-desktop ==> default: Adding box 'msakamoto-sf/centos65-minimal-desktop' (v0.1.0) for provider: virtualbox default: Downloading: https://vagrantcloud.com/msakamoto-sf/centos65-minimal-desktop/version/1/provider/virtualbox.b ox default: Progress: 100% (Rate: 5066k/s, Estimated time remaining: --:--:--) ==> default: Successfully added box 'msakamoto-sf/centos65-minimal-desktop' (v0.1.0) for 'virtualbox'! ==> default: Importing base box 'msakamoto-sf/centos65-minimal-desktop'... ==> default: Matching MAC address for NAT networking... ==> default: Checking if box 'msakamoto-sf/centos65-minimal-desktop' is up to date... ==> default: Setting the name of the VM: centos65_md_demo ==> default: Clearing any previously set network interfaces... ==> default: Preparing network interfaces based on configuration... default: Adapter 1: nat ==> default: Forwarding ports... default: 22 => 2222 (adapter 1) ==> default: Running 'pre-boot' VM customizations... ==> default: Booting VM... ==> default: Waiting for machine to boot. This may take a few minutes... default: SSH address: 127.0.0.1:2222 default: SSH username: vagrant default: SSH auth method: private key default: Warning: Connection timeout. Retrying... default: Warning: Connection timeout. Retrying... default: Warning: Remote connection disconnect. Retrying... ==> default: Machine booted and ready! ==> default: Checking for guest additions in VM... ==> default: Mounting shared folders... default: /vagrant => C:/work/tmp/vagrant_ex/t7
以下のディレクトリが作成され、圧縮状態のvmdk, metadata.json, Vagrantfile, box.ovf が展開されます。
$HOME/.vagrant.d/boxes/msakamoto-sf-VAGRANTSLASH-centos65-minimal-desktop/0.1.0/virtualbox
今回は試してませんが、"vagrant init" コマンド、および "vagrant box add" コマンドではURLを指定できますので、LAN内で立ちあげたWebサーバからBoxファイルをDLすることは十分可能と思われます。
※もしかしたら "vagrant init" の場合と "vagrant box add" の場合とでURLを指定した時の動きの中身は違うかも。