heroku shared dbのバックアップ
- OSX 10.7.2
- ruby 1.8.7 (2010-01-10 patchlevel 249) [universal-darwin11.0]
herokuの共有DBのバックアップをとろうと「heroku db:pull」を実行したらエラーになった。
$ heroku db:pull sqlite://20111126.sqlite Taps Load Error: no such file to load -- taps/operation You may need to install or update the taps gem to use db commands. On most systems this will be: sudo gem install taps
tapsをインストールしろとのことなので、インストールする。
$ sudo gem install taps
インストールしたのに、まったく同じエラーが表示される。
sqliteをアップグレードしてみる。
$ sudo gem update sqlite3 sqlite-ruby
まだエラー。
railsもインストールしてみる。
$ sudo gem install rails
このコマンドを実行したところ返答がまったくない。
一度キャンセルし、gemをアップデート
$ sudo gem update --system
再度「sudo gem install rails」を実行したら、すぐに返答があって無事インストール完了。
しかし、まだ「db:pull」するとエラーはでる。
herokuコマンドはheroku-toolbelt.pkgをインストールしたやつだったので、
gemでherokuをインストールしてみる。
$ sudo gem install heroku
ようやくバックアップが成功した。
$ heroku db:pull sqlite://20111126.sqlite Loaded Taps v0.3.23 Warning: Data in the database 'sqlite://20111126.sqlite' will be overwritten and will not be recoverable. ! WARNING: Potentially Destructive Action ...
jquery mobile を使ってるときのpost後のredirect
jquery mobileを使ったページでフォームデータのpost -> redirect後にリロードすると、再度postされる。
formに「data-ajax=false」を指定することで回避。
play view
#{form @Foo.save(hoge.id), 'data-ajax':'false'} ... #{/form}
play controller
public static void save(Long hogeId) { ... redirect("Foo.list"); }
aタグにはほぼ「rel="external"」つけてるし、jquery mobileを有効に使えてんのかな?
heroku上のアプリの状態確認
アプリのプロセス確認
$ heroku ps Process State Command ------------ ------------------ ------------------------------ web.1 up for 50m play run --http.port=$PORT $PLAY_O..
アプリ情報
$ heroku info === ************** Web URL: http://************.herokuapp.com/ Git Repo: git@heroku.com:**********.git Repo size: 117M Slug size: 26M Stack: cedar Data size: 320k ← 使用中DBサイズ Addons: Basic Logging, Basic Release Management, Shared Database 5MB Owner: ********@****.com
ダウンロード終了時の判定
ダウンロード開始時に画面の操作をできないようにし、ダウンロード終了後に画面操作できるようにするのに、いいアイデアをみつけた。
処理のフローとしては、
1.画面を操作できないようにする。
2.クライアントjsで、トークンを生成する。
3.クライアントjsで、一定時間ごとに、「1.」のトークンがクッキーに入っているか確認する処理を実行。
4.「2.」のトークンをサーバーに送信する。
5.サーバーで、受信したトークンをクッキーに設定する。
6.サーバーで、ダウンロード処理を開始する。
7.「2.」の処理が「4.」でサーバーから設定されたトークンを発見する。
8.画面を操作できるようにする。
処理内容はなんてことないけど、思いつかなかった。すばらしい。
元記事をちょっと変更してjavaで実装してみた。
変更点
クライアント javascript
var Demo = {}; /** * ダウンロード終了管理 * トークンをクッキーにつけて送信し、 * レスポンスの受信クッキーで終了を判定する。 * @constructor */ Demo.downloadManager = function() { this.fileDownloadCheckTimer = undefined; this.tokenName = Demo.downloadManager.cookieToken; this.successTokenName = Demo.downloadManager.cookieSuccessToken; this.maxLoopCount = 10; this.loopCount = 0; }; /** 送信トークン名(サーバーで受信)*/ Demo.downloadManager.cookieToken = 'download-token'; /** 受信トークン名(サーバーから送信)*/ Demo.downloadManager.cookieSuccessToken = 'download-success-token'; /** 画面フリーズ */ Demo.downloadManager.prototype.blockUIForDownload = function() { Demo.blockUI(); // 画面を操作できないようにする。 this.loopCount = 0; var token = new Date().getTime(); // 現在時刻でトークンを生成 Demo.setCookie(this.tokenName, token, '/'); Demo.setCookie(this.successTokenName, 'START', '/'); var self = this; this.fileDownloadCheckTimer = window.setInterval(function() { // 一定時間ごとにクッキーの値を確認しダウンロードが終わったか判定 var cookieValue = Demo.getCookie(self.successTokenName); //console.log(self.loopCount + ':' + cookieValue); if (cookieValue == token) { self.finishDownload(); } self.loopCount++; if (self.loopCount > self.maxLoopCount) { self.finishDownload(); } }, 1000); }; /** 画面フリーズ解除 */ Demo.downloadManager.prototype.finishDownload = function() { // ダウンロードが終わった後に後始末を行う window.clearInterval(this.fileDownloadCheckTimer); Demo.setCookie(this.tokenName, 'END', '/'); Demo.setCookie(this.successTokenName, 'END', '/'); Demo.unBlockUI(); // 画面を操作できるようにする。 }; Demo.setCookie = function(name, value, path) { // クッキー設定処理 }; Demo.getCookie = function(name) { // クッキー返却処理 }; Demo.blockUI = function(){ // 画面操作を抑制する処理 }; Demo.unBlockUI = function(){ // 画面操作抑制を解除する処理 };
HTML
<input type="button" value="test" onclick="new Demo.downloadManager().blockUIForDownload()" />
Server処理 java
public static final String COOKIE_NAME_DOWNLOAD_TOKEN = "download-token"; public static final String COOKIE_NAME_DOWNLOAD_SUCCESS_TOKEN = "download-success-token"; … // ダウンロード処理の前にクッキーを設定する String downloadToken = ""; for (Cookie c : request.getCookies()) { if (c.getName().equals(COOKIE_NAME_DOWNLOAD_TOKEN)) { downloadToken = c.getValue(); } } log.debug("Download Token:" + downloadToken); Cookie resultCookie = new Cookie(COOKIE_NAME_DOWNLOAD_SUCCESS_TOKEN, downloadToken); resultCookie.setPath("/"); response.addCookie(resultCookie); // ダウンロード処理 …
CSVをソート (複数列指定)
ComparatorChain便利だった。
適当なライブラリを使ってCSVを読み込んでフィールドを文字列のリストにして、
それをさらに行数分リストにしたのを用意してソートする。
package demo; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.apache.commons.collections.comparators.ComparatorChain; /** CSVデータソート用クラス */ public final class CSVSort { /** * CSVをソートします * @param csv csvデータ * @param metaDatas ソート情報 */ @SuppressWarnings("unchecked") public static void sort(List<List<String>> csv, List<SortMetaData> metaDatas) { if (metaDatas.isEmpty()) { return; } // ソート情報をキー順に並べ替え Collections.sort(metaDatas, new Comparator<SortMetaData>() { @Override public int compare(SortMetaData o1, SortMetaData o2) { return (o1.order < o2.order ? -1 : (o1.order == o2.order ? 0 : 1)); } }); ComparatorChain cc = new ComparatorChain(); for (final SortMetaData meta : metaDatas) { cc.addComparator(new Comparator<List<String>>() { @Override public int compare(List<String> o1, List<String> o2) { return o1.get(meta.colNo).compareTo(o2.get(meta.colNo)); } }, !meta.isAscending); } // ソート Collections.sort(csv, cc); } /** ソート情報 */ public static class SortMetaData { /** 列番号 */ public final int colNo; /** キー順(何番目に有効にするか) */ public final long order; /** 昇順の場合{@code true} */ public final boolean isAscending; /** * コンストラクタ * @param colNos 列番号 * @param order キー順 * @param isAccending 昇順の場合{@code true} */ public SortMetaData(int colNos, long order, boolean isAccending) { this.colNo = colNos; this.order = order; this.isAscending = isAccending; } } }
testコード
package demo; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import junit.framework.TestCase; import demo.CSVSort.SortMetaData; public class CSVSortTest extends TestCase { private static List<String> r(String... strings) { return Arrays.asList(strings); } private static String d(List<List<String>> csv) { StringBuilder sb = new StringBuilder(); for (List<String> line : csv) { for (String field : line) { sb.append(field); sb.append(','); } sb.deleteCharAt(sb.length() - 1); sb.append('\n'); } return sb.toString(); } /** sort test */ public void testSort() { List<List<String>> act = new ArrayList<List<String>>(); // r()は文字列配列をリストにするメソッド act.add(r("100", "120", "110")); act.add(r("100", "110", "120")); act.add(r("200", "100", "200")); List<SortMetaData> metaList = new ArrayList<SortMetaData>(); metaList.add(new SortMetaData(0, 1, false)); metaList.add(new SortMetaData(1, 1, true)); CSVSort.sort(act, metaList); List<List<String>> exp = new ArrayList<List<String>>(); exp.add(r("200", "100", "200")); exp.add(r("100", "110", "120")); exp.add(r("100", "120", "110")); // d()はオブジェクトをダンプするメソッド assertEquals(d(exp), d(act)); System.out.println(d(exp)); } }
play gae リストのサイズ取得時のエラー
ローカルでは
${list.size}
で動いていたのに、GAEにデプロイしたらエラーになった
Execution error occured in template /app/views/xxxxxx.html. Exception raised was IllegalAccessException : Reflection is not allowed on private int java.util.ArrayList.size. play.exceptions.TemplateExecutionException: Reflection is not allowed on private int java.util.ArrayList.size at play.templates.BaseTemplate.throwException(BaseTemplate.java:84) at play.templates.GroovyTemplate.internalRender(GroovyTemplate.java:252) at play.templates.Template.render(Template.java:26)
フィールドにアクセスしてエラーになっていたので、メソッドにアクセスするようにすればOK
${list.size()}