GWTでサーバを使用せずにZipファイルをクライアントへ保存
外部JavaScript(JS)ライブラリとJSNIを使って実装する。FileSaverとJSZipを利用する。
Shift_JISは扱えません!!
- 使用するjsライブラリ
- jszip.js JSZip
- FileSaver.js eligrey/FileSaver.js · GitHub
- war/js に使用するJSファイルを入れておく
- htmlファイルでJSファイルをリンクしておくのも忘れずに
- javaファイルでは、JSNIで外部JSライブラリにアクセス($wndを忘れずに)。JSNI内部ではjava.util.Mapが使えないようなので、JSONObjectを使用する。
- JSONObjectを使うために、.gwt.xmlファイルにinheritsを追加する必要がある。
参考
gwt - Java Hashmap and Mutlidimensional array type sig in JSNI? - Stack Overflow
eligrey/FileSaver.js · GitHub
JSZip
*.html
(前略) <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link type="text/css" rel="stylesheet" href="/css/global.css" /> <script language="javascript" src="js/jszip.js"></script> <script language="javascript" src="js/FileSaver.js"></script> </head> (後略)
*.java
public void onModuleLoad() { Button save = new Button("保存"); save.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { //JSONObject がMap代わりに使える JSONObject obj = new JSONObject(); for (int i = 0; i < 3; i++) { obj.put("k"+i, new JSONString("v"+i)); } JavaScriptObject jsObj = obj.getJavaScriptObject(); saveAsZip(jsObj); } }); RootPanel.get().add(save); } /** * @param fileMap * key:ファイル名 value:ファイル内容 * @return */ private native boolean saveAsZip(JavaScriptObject fileMap) /*-{ var zip = new $wnd.JSZip(); for (key in fileMap) { zip.file(key + ".txt", fileMap[key]); } var blob = zip.generate({ type : "blob" }); $wnd.saveAs(blob, "text.zip"); return true; }-*/;
<module> <inherits name="com.google.gwt.user.User" /> <inherits name="com.google.gwt.user.theme.standard.Standard" /> <inherits name="com.google.gwt.json.JSON" /> <entry-point class="(エントリーポイントクラス)" /> <source path="client" /> </module>
FlowPanelの要素にPanelを追加しても浮動化してくれない
GWTを使用していて、FlowPanelの要素にPanelを追加しても浮動化してくれないとき。
追加する要素を<span></span>で囲み、CSSでfloat属性を指定すればよい
参考:
Simplifying GWT Markup with HTML Widgets « TurboManage
Main.java
public class Gwt_tmp implements EntryPoint { private static final int COUNT = 10; public void onModuleLoad() { VerticalPanel mainPanel = new VerticalPanel(); RootPanel.get().add(mainPanel); mainPanel.addStyleName("contents"); { mainPanel.add(new Label("ボタンをFlowPanelに追加")); FlowPanel fp = new FlowPanel(); fp.setWidth("800px"); mainPanel.add(fp); fp.clear(); for (int i=0; i<COUNT; i++) { Button item= new Button("button"+i); item.setWidth("100px"); fp.add(item); } } mainPanel.add(new HTML("<hr>"));[f:id:black-skin:20150315175347p:plain] { mainPanel.add(new Label("VerticalPanelはFlowPanelでも横配置できない")); FlowPanel fp = new FlowPanel(); mainPanel.add(fp); for (int i = 0; i < COUNT; i++) { VerticalPanel item = new VerticalPanel(); item.setStyleName("name-" + i); item.add(new Label("title" + i)); item.add(new Button("ボタン")); fp.add(item); } } mainPanel.add(new HTML("<hr>")); { mainPanel.add(new Label("<span>で囲み、floatを指定")); FlowPanel fp = new FlowPanel(); fp.setWidth("800px"); mainPanel.add(fp); fp.clear(); for (int i=0; i<COUNT; i++) { VerticalPanel item = new VerticalPanel(); item.setWidth("150px"); item.setStyleName("name-" + i); item.add(new Label("title" + i)); item.add(new Button("ボタン")); fp.add(new FloatSpanItemWidget(item)); } } } }
FloatSpanItemWidget.java
import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; public class FloatSpanItemWidget extends SimplePanel { public FloatSpanItemWidget() { super((Element) Document.get().createSpanElement().cast()); addStyleName("float"); } public FloatSpanItemWidget(String s) { this(); getElement().setInnerText(s); } public FloatSpanItemWidget(Widget w) { this(); this.add(w); } }
<!--(中略)--> .float{ float: left; }
DecoratorPanelを使ったが、padding,border,marginが反映されなかったとき
ちょっとはまったので。
以下の内容が記述されたCSSファイルが読み込まれるように自分で設定すること。
たとえば、decoratorPanel.cssという名前のファイルにして読み込む
decoratorPanel.css
.gwt-DecoratorPanel { } .gwt-DecoratorPanel .topCenter, .gwt-DecoratorPanel .bottomCenter { background: url(images/hborder.png) repeat-x; } .gwt-DecoratorPanel .middleLeft, .gwt-DecoratorPanel .middleRight { background: url(images/vborder.png) repeat-y; } .gwt-DecoratorPanel .topLeftInner, .gwt-DecoratorPanel .topRightInner, .gwt-DecoratorPanel .bottomLeftInner, .gwt-DecoratorPanel .bottomRightInner { width: 5px; height: 5px; zoom: 1; } .gwt-DecoratorPanel .topLeft { background: url(images/corner.png) no-repeat 0px 0px; -background: url(images/corner_ie6.png) no-repeat 0px 0px; } .gwt-DecoratorPanel .topRight { background: url(images/corner.png) no-repeat -5px 0px; -background: url(images/corner_ie6.png) no-repeat -5px 0px; } .gwt-DecoratorPanel .bottomLeft { background: url(images/corner.png) no-repeat 0px -5px; -background: url(images/corner_ie6.png) no-repeat 0px -5px; } .gwt-DecoratorPanel .bottomRight { background: url(images/corner.png) no-repeat -5px -5px; -background: url(images/corner_ie6.png) no-repeat -5px -5px; } html>body .gwt-DecoratorPanel { } * html .gwt-DecoratorPanel .topLeftInner, * html .gwt-DecoratorPanel .topRightInner, * html .gwt-DecoratorPanel .bottomLeftInner, * html .gwt-DecoratorPanel .bottomRightInner { width: 5px; height: 5px; overflow: hidden; }
Google App Engine のデプロイで苦労した話
投稿日 : 2015.3.13
GoogleAppEngineはjspファイルを含んでいる場合、デプロイが失敗することがある、ということはググれば結構出てくるが、それでもはまったのでメモを残す。
java.lang.RuntimeException: Cannot get the System Java Compiler. Please use a JDK, not a JRE.
というエラーメッセージが出てくるときは、原因として環境要因(eclipse.iniでjdkを指定していなかった)とソースファイル要因(jspのコンパイルエラー、でもローカルサーバで実行時は普通に動く)が考えられる。
●環境要因の解決法 参考:GAE−JSPがあるとデプロイが失敗する件 - sabonightの日記
・Javaはjdk7以下を使うこと。GAEがまだ8に対応してないからUnsupportedClassVersionErrorが出る (2015.3.13)
・eclipse.ini に以下のコードを追加
-vm
C:\Program Files\Java\jdk1.7.0_75\bin\javaw.exe(jdkに含まれるjavaw.exeへのパス)
-vmargs
-Dosgi.requiredJavaVersion=1.7
・環境変数パスの java_homeをjdk7のものに(C:\Program Files\Java\jdk1.7.0_75\ など)、変数pathの最初に%java_home%\binを移動
●ソースファイル要因の解決法 参考:Issue 8725 - googleappengine - Failed to deplay - Google App Engine - Google Project Hosting
・jspファイルからすべてimport文を除外。クラス名はすべて完全修飾クラス名で書く。
・jspのinclude元・include先で変数名の衝突が起こらないようにする。例えば、多くのjspファイルから参照される、ページレイアウトを定義したjspファイルなどは変数名を冗長にしておく。
●環境要因とソースファイル要因を切り分けるためには?
まず、(GooglePlugInを利用するなどして)jspを含まない新規GAEプロジェクトを作成し、デプロイを試す。これは成功するはず。
次に、warファイルに単純なjspファイルを作成(ファイル内でjavaが使われていなくてもいい。Eclipseなら、PackageExplorerでwarフォルダを右クリック -> New -> Other... -> Web -> JSP File で作成したデフォルトのJSPファイルで十分)。デプロイを試す。失敗したら環境要因を修正する必要がある。成功すれば環境要因はクリア。デプロイしようとしているプロジェクトに含まれるJSPファイルに問題があることになる。
-------------------------------------------------------------------------------------------
以下経緯。反面教師。
エラーメッセージ-----------------
Preparing to deploy:
Created staging directory at: 'R:\Temp\appcfg6583028287260996029.tmp'
Scanning for jsp files.
Compiling jsp files.
java.lang.RuntimeException: Cannot get the System Java Compiler. Please
use a JDK, not a JRE.
Debugging information may be found in
R:\Temp\appengine-deploy8586597779396298325.log
----------------------------------------
JREじゃなくてJDKを使えって書いてあるけど、ちゃんとそのように指定した。
→ eclipse.iniの-vm に jdk7を指定(jdk 6
32bitも試した)参考:https://groups.google.com/forum/#!topic/google-app-engine-japan/ymbsf-1Mf4I
⇒ だめ
↓
appcfg.cmdを使うとシステムのJDKを取得できる 参考:http://qiita.com/MOKYN/items/cec526f981f1daba6afb
(Googleアカウントにログインするときに、普通のパスワード入力だと試行ブロックに引っかかり、その後ログインできなくなった。--oauth2 パラメータを利用することで解決)⇒ だめ
↓
実はJSPのコンパイルエラー 参考:https://code.google.com/p/googleappengine/issues/detail?id=8725
プリコンパイルのできるJSPページを追加してアクセス(slim3を使っていたので、FrontServletによるリダイレクトは解除しないとエラーでるようだ)、.jspファイルからすべてimport文を除外(アップロード用コンパイラはローカルサーバ用コンパイラよりも繊細なためエラーが出るとか) ⇒ だめ
↓
環境(java version?)かプロジェクト(JSP?)かの切り分けをためしてみる
初期プロジェクトデプロイ : 成功
index.jspを追加(jspにはjavaコードすら書かれていない): 失敗
eclipse.iniを修正
-vm (半角スペース)(改行)
javawへのパス(/区切り)
とすればよいらしい
⇒ 初期プロジェクト+jspファイル : 成功
⇒ でもメインのプロジェクトはだめ
↓
jspファイル間のローカル変数の衝突をさけるため、レイアウト関連jspはファイル名を変数名の先頭につけてみる
⇒ エラーログが変わった
前:java.lang.RuntimeException: Cannot get the System Java
Compiler. Please use a JDK, not a JRE.(実際はjspのプリコンパイルエラー)
後: com.google.apphosting.utils.config.AppEngineConfigException:
Received SAXException parsing the input stream
↓
web.xmlは日本語コメント使えない 参考:psycho cafe: gaeのweb.xmlでは日本語コメントが使えない
⇒デプロイ成功!
でも、jspが動作しないようだ。java8でjspをコンパイルしたが、gaeはjava7までしか対応していないため、「ava.lang.UnsupportedClassVersionError:
クラス名 (Unsupported major.minor version
Mj.Mi)」の例外が発生。環境変数pathを書き換え(pathの値は最初の方から順番に評価されるようだ。最初が「C:\ProgramData\Oracle\Java\javapath」だったため、java8でコンパイルされたのかも。
参考:Javaアプリケーション メモ(Hishidama's Java-Application Memo)
⇒ 完全にデプロイ成功