HipChatで送信したファイルを消す

HipChat、つかってますか!

最近使い始めたのですけど、人にファイルを送信したくてファイルをD&Dしたらuploading…って出てファッ!?となりました。
どうやらS3に上がるっぽいですね。こわい。

・Files uploaded to HipChat are stored on Amazon's S3 servers.
・All uploaded files are accessible via an obscure URL which is shared with people in the chat when the file is uploaded.
・Users are not required to be logged in to view uploaded files - they are visible to anyone who has the link. (This means the links can be shared easily with anyone you want to be able to view the file).

http://help.hipchat.com/knowledgebase/articles/64477-are-files-uploaded-to-hipchat-secure-private-

URLはランダムで生成されるのでピンポイントで誰のものを見るみたいなことは出来なさそうですが、現状特に認証とかはないみたいなのでちょっと怖い。

ので削除する方法を探したところ、ブラウザから消せるっぽいです。
How do I delete chat history and files? – Help Center

ルームでアップロードしたファイルを消す場合は以下、
https://www.hipchat.com/rooms
1:1でアップロードしたファイルを消す場合は以下
https://www.hipchat.com/people
にアクセスし、表示されたヒストリーで該当のファイルの行をマウスオーバーすると「×」マークが出るので、これを選択すると削除できます。

わかりづらい。

tomcatで一つのアプリを複数のバーチャルホストで配信する

普通こんなことしないとは思うんですけど。

tomcatにアプリを二つ以上乗っけて、かつ一方のアプリは複数のドメインで配信する方法です。

たとえば、tm8r.jpはwebbappsにあるアプリが処理、
tm8r.com、tm8rhoge.com、tm8rfuga.comはwebapps2にあるアプリが処理したい場合。

<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="false">
</Host>
<Host name="tm8r.com" appBase="webapps2"
unpackWARs="true" autoDeploy="false">
  <Alias>tm8rhoge.com</Alias>
  <Alias>tm8rfuga.com</Alias>
</Host>

こんな感じでAliasを定義してやれば実現出来るもよう。
はじめてつかった。

tm8r.jpは明示的に定義されてないのでlocalhostの設定を参照し、
それ以外の明示的に指定されているドメインはtm8r.comの方を参照します。

tm8rhoge.comとtm8rfuga.comをそれぞれHostに書いてappBaseにwebapp2を指定しても実現できるっちゃできるんですが、書いた分だけアプリがデプロイされてしまうのでこんな感じに。
もっといい方法あったりするのだろうか。とりあえず実現できたのでよいとする。

jsoupでHTMLをパースする

オヒサシブリデース。
ついにはてなブログに移行しました。インポート楽ちんでよいですね。

ここのところあんまり新しいことしてなかったので書くことなかったんですが、
久々に触ったことなかったライブラリに触ったので覚書までに。

HTMLをパースする案件がありまして、以下のパーサを触ったんですが、
割と精度も使い勝手もパフォーマンスもよかったjsoupを紹介してみます。

  1. jsoup
  2. jericho
  3. HtmlCleaner
  4. Validator.nu
  5. HTMLEditorKit
  6. TagSoup
  7. HTML Parser
  8. NekoHtml
  9. JTidy

準備

mavenでビルドするのを想定して、まずはpom.xmlに以下を追記します。

<dependency>
	<groupId>org.jsoup</groupId>
	<artifactId>jsoup</artifactId>
	<version>1.7.3</version>
</dependency>

公式サイトによると1.7.3が最新みたいです。
jsoup Java HTML Parser, with best of DOM, CSS, and jquery

実装してみる

まずはHTMLをパースして、タグ名、属性名、属性値、テキストを表示するものを実装してみます。

public class JsoupTest {
  private static final String PREFIX = "<html><head></head><body>";
  private static final String SUFFIX = "</body></html>";

  private static final String BACKQUOTE = "`";
  private static final String DOUBLEQUOTE = "\"";

  public static void main(String[] args) {
    String target = "<a href=\"#\">hoge</a>";
    // パーサがバッククォートをタグ内のクォートとして認識しないので置換(IE対策)
    String replacedText = StringUtils.replace(target, BACKQUOTE, DOUBLEQUOTE);

    // 閉じられてないタグのみを渡した場合にパーサが勝手にHTMLとして整形し、渡したものが除去されるので自前で整形する
    replacedText = PREFIX + replacedText + SUFFIX;
    Document doc = Jsoup.parse(replacedText);
    Elements elements = doc.getAllElements();
    for(Element element : elements) {
      System.out.println("tagname:" + element.tagName() + ", text:" + element.ownText());
      for (Attribute attr : element.attributes()) {
        System.out.println("key:" + attr.getKey() + ", value:" + attr.getValue() + "\n-");
      }
      System.out.println("-----");
    }
  }
}

実行結果は以下の通り。

tagname:#root, text:
-----
tagname:html, text:
-----
tagname:head, text:
-----
tagname:body, text:
-----
tagname:a, text:hoge
key:href, value:#
-
-----

らくちんですね!

ちなみにhtmlタグとかheadタグとかをつけてるのは、
仮にこのあたりのタグがなかったとき、jsoupが勝手にそのへんのタグを補完してくれるからです。
基本的にはこの保管に任せておいて問題ないのですが、
たとえば以下のような文字列を渡した場合。

String target = "<a href=\"#\"aaa";

以下のような結果になってしまします。

<html>
 <head></head>
 <body></body>
</html>

_人人 人人_
> 突然の死 <
 ̄Y^Y^Y^Y ̄

というわけで自前でつけてます。
htmlタグとかheadタグとかが入力内容に入ってくるのであれば別の方法を考えないといけないかと思いますが、今回は入ってこない想定です。

まあこれはこれで以下みたいな変なHTMLが生成されたりはするんですが。

<html>
 <head></head>
 <body>
  <a href="#" aaa<="" ody=""></a>
 </body>
</html>

なくなるよりはいいかなという感じで今後の修正に期待します。

特定のタグを抽出する

jsoupには特定の条件に合致するタグを取得するメソッドが用意されています。
コードを以下のように書き換えてみます。

public class JsoupTest {
  private static final String PREFIX = "<html><head></head><body>";
  private static final String SUFFIX = "</body></html>";

  private static final String BACKQUOTE = "`";
  private static final String DOUBLEQUOTE = "\"";

  public static void main(String[] args) {
    String target = "<a href=\"#\" onmouseover=\"alert(0)\">aa</a><img onerror=\"alert(1)\">";
    // パーサがバッククォートをタグ内のクォートとして認識しないので置換(IE対策)
    String replacedText = StringUtils.replace(target, BACKQUOTE, DOUBLEQUOTE);

    // 閉じられてないタグのみを渡した場合にパーサが勝手にHTMLとして整形し、渡したものが除去されるので自前で整形する
    replacedText = PREFIX + replacedText + SUFFIX;
    Document doc = Jsoup.parse(replacedText);
    // aタグを抽出
    System.out.println("a:" + doc.getElementsByTag("a").size());
    // 属性名がhrefのものを含むタグを抽出
    System.out.println("href:" + doc.getElementsByAttribute("href").size());
    // 属性名がonで始まるものを含むタグを抽出
    System.out.println("on:" + doc.getElementsByAttributeStarting("on").size());
  }
}

それぞれのメソッドが何をしてくれるかはコメントの通りです。
この実行結果は以下のようになります。

a:1
href:1
on:2

べんり!

特定のタグ抽出メソッドを実装する

上で便利な抽出メソッドを紹介しましたが、もともと用意されていない抽出方法を実装したいこともあるかと思います。
たとえば、属性名と属性値を指定して、指定した属性名の値に指定した値が含まれているものを抽出するメソッドははありますが、属性名を限定せずに指定した文字列が属性値に含まれるものを抽出するメソッドはありません。
というわけで実装してみます。

public class CustomEvaluator {

  /**
   * 指定した文字列が属性値に含まれているものを抽出
   */
  public static final class ValueContainsEvalutor extends Evaluator {

    private String value;

    public ValueContainsEvalutor (String value) {
      Validate.notEmpty(value);
      this.value = value.trim().toLowerCase();
    }
    @Override
    public boolean matches(Element root, Element element) {
      for (org.jsoup.nodes.Attribute attribute : element.attributes().asList()) {
        if (attribute.getValue().toLowerCase().contains(value))
          return true;
      }
      return false;
    }
  }
}

次に、こいつを呼び出してみます。

    System.out.println("alert:"
        + Collector.collect(new CustomEvaluator.ValueContainsEvalutor("alert"), doc).size());

実行結果は以下のようになります。

alert:2

できた!

おわり

以上、jsoupのご紹介でした。
お手軽でよいですね!

一番ブラウザに近いパースをしてくれたのはValidator.nuだったんですが、
パフォーマンスがjsoupの方が優れてる感じだったので見送りました。
こちらの紹介は気が向いたら。向かなさそうですが。

jacksonでデシリアライズする際に未知のプロパティを無視する

大したことじゃないんですけど毎回忘れて調べてる気がするので覚書。
jacksonであるjsonをデシリアライズしたいんだけど、そこに未知のプロパティがあったとき、UnrecognizedPropertyExceptionが発生しちゃうので、それを回避するやつです。


たとえば以下のようなクラスがあったとして。

public class TestJsonBean {
	private String str;
	public String getStr() {
		return str;
	}
	public void setStr(String str) {
		this.str = str;
	}
}


こんなコードでデシリアライズを実行してみます。

ObjectMapper om = new ObjectMapper();
String testStr = "{\"str\":\"hoge\",\"unknownstr\":\"fuga\"}";
TestJsonBean bean = om.readValue(testStr,TestJsonBean.class);


すると、案の定以下のようなExceptionが発生します。

Exception in thread "main" org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "str3" (Class jp.ameba.TestJsonBean), not marked as ignorable


今回は未知のプロパティを無視して、存在するプロパティだけをデシリアライズしたいので、クラスに以下のようなアノテーションを追記します。

@JsonIgnoreProperties(ignoreUnknown=true)
public class TestJsonBean {
(略)
}


この状態で先ほどと同じコードでデシリアライズを実行すると、無事存在する「str」だけがデシリアライズされる結果になります。
かんたん!

javaでパスワード保護されたExcelファイルを作る

javaでExcelファイルとか作るのテンション下がりますよね!こんにちは!


Excelファイル全体をパスワード保護して、パスワード入力しないと開けないようにしたいという案件がありまして。
それをjavaで実装する必要がありまして。まあ自分の案件じゃないんですけど。
質問されたのでちょっと調べてみました。


java - Create a password protected excel file using apache poi? - Stack Overflow
去年の1月の投稿ですが、「無料で暗号化できるライブラリなんて存在しないぜ!金払え金!それが嫌ならzip圧縮してそいつにパスワードかけな!」みたいなことが書いてある雰囲気です。
有償だとAspose.Cellとかが結構有名みたいですね。


でもやっぱりこんなことのために有償のもの使うの嫌なので必死に探して見つけました。
Edit/View Excel Spreadsheet with Jxcell in Java
このライブラリでパスワードがかけられそうです。


サンプルも結構充実してます。
Edit/View Excel Spreadsheet with Jxcell in Java
暗号化は「Open Encrypted Excel files and Save Encrypted Excel files」ってやつを使えばいけそうです。


サンプルのソースを一部引用します。

View m_view = new View();
// 指定したセルに値を書き込み
m_view.setTextAsValue(1,2,"Jan");
m_view.setTextAsValue(1,3,"Feb");
(略)
// encrypt.xlsという名前でhiというパスワードをかけて保存
m_view.write(".\\encrypt.xls", "hi");


かんたん…!
あんまり詳しく調べてませんが、関数の使用なども含め、簡単なExcelファイルの作成は出来そうです。
ちなみに日本語も使えました。


Mavenに上がってないのだけがちょっとアレ。
他にも同様の操作の出来る無償ライブラリはあるかもしれませんが、とりあえず要件は満たせたので今回はこんなところで。

facebookのhtml5版いいねボタンにURLエンコードしたURLを指定するとエラー

もうタイトルまんまなんですけど。


html5版のいいねボタンは、表示したいところに以下のようなコードを貼付ける形になります。

<div class="fb-like" data-href="http://d.hatena.ne.jp/tm8r/" data-send="false" data-width="450" data-show-faces="false"></div>


で、これまではdata-hrefにURLエンコードしたURLを指定してても大丈夫だったのですが、
最近URLエンコードしたものを指定するとエラーと表示されるようになったみたいです。


というわけでなんか急にいいねボタンが表示されなくなった場合、data-hrefを確認してみたら解決するかもねというお話でした。

FreeMarkerだけでランダムにtrue/falseの分岐をする

大した話ではないのですけれども。


ちょっとランダムに要素の出し分けをしたいんだけど、java側で制御はしたくないでござる…!みたいなときに使えるかもしれないやつです。

<#if ((.now?long % 2) == 0)>
<p>trueだよ!</p>
<#else>
<p>falseだよ!</p>
</#if>


うむ。


.nowが使えるようになったのがバージョン2.3.17なので古いの使ってる場合アップデートしないとですけど。
oddとかevenみたいなビルトインがあってもよさそうですけど無いのですね。


FreeMarkerに結構依存しているのでアップデートされなくなったら困るなーと思う日々なのですが、今年の6/27にもアップデートされてるっぽくて安心した今日この頃です。
FreeMarker Manual - 2.3.20

スポンサーリンク