ラベル Java の投稿を表示しています。 すべての投稿を表示
ラベル Java の投稿を表示しています。 すべての投稿を表示

2011/05/21

Project Euler をやり始めてみた

数日前に存在を初めて知ったProject Euler
Project Euler is a series of challenging mathematical/computer programming problems that will require more than just mathematical insights to solve. Although mathematics will help you arrive at elegant and efficient methods, the use of a computer and programming skills will be required to solve most problems.
About Project Euler - Project Euler
要は、プログラミングで解く数学パズル、みたいなもの。WEB上に用意された沢山の問題を一つづつ解いていく。ちなみに、他のユーザが書いたコードを見ることもできて、ちょっと勉強になったりもする。
全部で300以上ある問題のうち、まだ10問しかクリアしてないけど、解き方を思いついたり、うまく一発で答えを当てられたときの気持よさはなかなかのもの。少しでもプログラミングできる人は是非お試しを。

さて、プログラミングにはどんな言語を使ってもいいんだけど、僕は結局Javaを選択して少しずつ問題を消化していこうかと。ちょっとだけクラスの設計もしたので、使ってみたかったgithubにリポジトリ作ってみた(こちら)。この設計に基づくと、記念すべき1問目、Problem1を解くSolverはこんな感じになる。
package euler;

public class Solver001 implements Solver
{
  public String solve()
  {
    int sum = sum(3, 1000) + sum(5, 1000) - sum(15, 1000);
    return Integer.toString(sum);
  }

  public int sum(int base, int max)
  {
    int n = (max - 1) / base;
    return base * n * (n+1) / 2;
  }
}
ま、あまり設計とか考えずにmain関数ベタ書きでよかったかも -_-;

まずは25問クリアして"Level1"になるのが目標です。

2011/04/19

閲覧制限したBloggerブログのフィードを公開する方法(改)

以前書いた記事の方法ではあまりにややこしかったので、
もうちょっとだけシンプルな方法を用意してみた。

まず、目的は前回の記事と同じ。
Bloggerには閲覧者を制限する機能があって、プライベートなブログを作るには便利。なんだけど、ちょっとした不便がある。それは『閲覧制限をかけると記事やコメントのフィードが生成されない』というもの。ちょっと古いけど、こんな関連記事もチラホラ見かけられる。本文は特定の人以外に公開したくないけど、記事のタイトルやコメントが付いたことくらいはフィードリーダーで確認できたら便利だなぁ、という人、少しくらいはいそうだ。というより、僕が便利だ!w
Bloggerで閲覧制限したブログのフィードを公開する方法 - cyclic landscape

というわけで、今回用意したのはコレ。
Blogger Feed Creator


使い方は以下の通り。
  1. まず、Blogger Feed Creator ページ右上の"login"をクリックしてGoogleアカウントでログイン。Blogger Feed Creatorを初めて使う場合、Blogger Feed CreatorがBloggerへアクセスすることを許可するかどうか確認するためのページが表示される。内容を了解出来れば"アクセスを許可"。
  2. ログインしたGoogleアカウントで閲覧可能なBloggerブログの一覧が表示される。閲覧制限をかけたブログも一覧に表示されるはずなので、フィードを公開したいもののラジオボタンを"ON"に。
  3. これで、"posts"のURLをブログ記事のフィード(タイトルのみ)、"comments"のURLをコメントのフィード(コメント本文の先頭20文字のみ)として利用できる。
    ちなみに、Googleリーダ(などのPubSubHubbub対応フィードリーダ)でフィードを読む人は、一覧に表示されているメールアドレスを、Bloggerの更新通知先(ダッシュボードの「設定→メールとモバイル」と「設定→コメント」に設定項目あり)として登録しておくと、フィードの更新が早くなって便利。


今回は、slim3を使って作りました。
慣れてくると手放せません。

2010/03/19

Bloggerの通知メールをWebHookに変換するWEBサービスを作ってみた

hook2feedと同様、これもまた「とある目的」のためにGAE for Javaで作ってみた。

bmail2hook - blogger's notification e-mails to web hooks

Googleアカウントでログイン後、WebHookの送信先URLを指定して「create new setting」すると、専用のメールアドレスが作成される。作成されたメールアドレスは以下のようにして使える。
  • 作成されたメールアドレスを、Bloggerの設定画面で新規記事や新規コメントの通知メール宛先に設定する
  • bmail2hookは、受信した通知メールから、記事のタイトル・URL・本文(の一部)を抽出して、指定したURLに対してWebHookする
これもまたhook2feedと同じく、「とある目的」以外の用途が思い付かない。困った。。。無理やり例を挙げるとするならば、、、HookHubとか経由してtwitterへ転送すれば、リアルタイムなtwitterへのブログ更新通知のできあがり、なんて如何?

ちなみに、bmail2hookとhook2feedで実現したかった「とある目的」については別記事にて。

2010/03/18

WebHookを溜めてRSSフィードを生成するWEBサービスを作ってみた

とある目的(については後日改めて)で使いたくて、GAE for Javaのお勉強がてら作ってみた。WebHooksの意義を考えると本末転倒な感じもするけど、深くは考えないことにする。。。

hook2feed - create feeds from web hooks

Googleアカウントでログインして「create new setting」すると、専用の2つのURL(「hook point URL」と「feed URL」)が作成される。hook2feedは、、、
  • 「hook point URL」の方に来たWebHook(HTTP POST)の履歴(最新20件分)を溜める
  • 「feed URL」の方で、WebHook履歴からRSSフィードを生成する
というように動く。
ちなみに、PubSubHubbubに一応対応していて、WebHookされたらhubにpublishするようになってる。PubSubHubbub対応のフィードリーダ(livedoorリーダなど)で見ると、リアルタイム性を体感できるはず。

さて、「とある目的」以外でどんな用途があるか、我ながらよく分からないけど、ブラウザで閲覧中のサイトタイトル&URLとダイアログボックスで入力したメモを「hook point URL」にWebHookしてフィード化するbookmarkletを作ってみた。以下のテキストボックスに「hook point URL」の末尾10桁のIDを入力してボタンを押すと、bookmarkletとして使えるリンクができる。
「hook point URL」のID(10桁) :
気付けば、まんま「あとで読む」なんだけど、役に立つシーンはあるかしら。他にも、HookHubを使って色んなWebHookをフィード化してみると、うれしいシーンが見つかる、かもしれない。

2010/03/06

GAE for Javaでquoted-printableなメールの受信

Google App Engine for Javaでメールを受信する処理を作ってたら、quoted-printableエンコーディングされたメール本文をデコードすると、デコード結果が途中で切れてしまう現象に遭遇した。調べてみると、quoted-printableエンコーディングでいうところの「Soft Line Breaks」がうまく認識されないことが原因だったので対策してみた。

まず、「Soft Line Breaks」については、RFCのquoted-printableエンコーディング規則の中で以下のように説明されている。要は、長すぎる行には適宜「=」+「改行」を入れて分割する、というエンコーディングが行われるとのこと。
Rule #5 (Soft Line Breaks):
The Quoted-Printable encoding REQUIRES that encoded lines be no more than 76 characters long. If longer lines are to be encoded with the Quoted-Printable encoding, 'soft' line breaks must be used. An equal sign as the last character on a encoded line indicates such a non-significant ('soft') line break in the encoded text.
RFC 1521: MIME Part One
RFCにはこう書いてあるのに、試してみると、JavaMail 1.4のMimeUtility.decode()でも、Commons Codec 1.4のQuotedPrintableCodec.decode()でも、Soft Line Breaksが正しく処理されず、最初のSoft Line Breakのところまででデコード結果が切れてしまう。で、なんでかなぁと思ってQuotedPrintableCodecのドキュメントをちゃんと読んでみると、きちんと明記されていた。
Note:
Rules #3, #4, and #5 of the quoted-printable spec are not implemented yet because the complete quoted-printable spec does not lend itself well into the byte[] oriented codec framework.
QuotedPrintableCodec (Commons Codec 1.4 API)
というわけで、自分でSoft Line Breaksを取り除いてやる必要がある。MimeMessageから取り出したMimeBodyPartをデコードする処理は、例えばこんな感じ。効率の悪いコードだけど、今のところ問題なく動いているようだ。
private String _decodeBody(MimeBodyPart bp)
{
  // parse header
  String contentType = null;
  String contentEncoding = null;
  String charset = null;
  try{
    contentType = bp.getContentType();
    contentEncoding = bp.getEncoding();
  }catch(MessagingException e){
  }
  String[] elems = contentType.split(";");
  for(String elem : elems){
    if(elem.trim().startsWith("charset=")){
      charset = elem.trim().substring("charset=".length());
    }
  }
  if(charset!=null){
    if(charset.startsWith("\"")) charset = charset.substring(1);
    if(charset.endsWith("\"")) charset = charset.substring(0, charset.length()-1);
  }
  // get inputstream
  InputStream in = null;
  try{
    in = bp.getRawInputStream();
  }catch(MessagingException e){
  }
  if(in==null) return "";
  // convert quoted-printable
  if(contentEncoding!=null && contentEncoding.equals("quoted-printable")){
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    int len;
    byte[] buffer = new byte[1024];
    try{
      while( (len=in.read(buffer, 0, buffer.length)) != -1 ){
        baos.write(buffer, 0, len);
      }
    }catch(IOException e){
    }
    byte[] b = baos.toByteArray();
    baos = new ByteArrayOutputStream();
    for(int j=0;j<b.length;j++){
      if(b[j]=='=' && j<b.length-1 && b[j+1]=='\n'){
        j++;
      }else{
        baos.write(b[j]);
      }
    }
    b = baos.toByteArray();
    in = new ByteArrayInputStream(b);
  }
  // decode
  if(in!=null && contentEncoding!=null){
    try{
      in = MimeUtility.decode(in, contentEncoding);
    }catch(MessagingException e){
    }
  }
  if(in==null) return "";
  // read body
  Reader r = null;
  if(charset!=null){
    try{
      r = new InputStreamReader(in, charset);
    }catch(UnsupportedEncodingException e){
    }
  }else{
    r = new InputStreamReader(in);
  }
  StringBuffer sb = new StringBuffer();
  BufferedReader br = null;
  try{
    br = new BufferedReader(r);
    String line = null;
    while( (line=br.readLine())!=null ){
      sb.append(line.trim());
      sb.append("\n");
    }
  }catch(IOException e){
  }finally{
    if(br!=null){
      try{
        br.close();
      }catch(IOException e){}
    }
  }
  return sb.toString();
}

2009/09/28

OSX上のArduinoIDE(0017)で"Serial Port Already in Use"エラー

Javaでシリアル通信するためにRXTXライブラリを/Library/Java/Extensions/に入れたら、スケッチをArduinoへ転送する際にタイトルの通りのエラーが出るようになってしまった。

ArduinoIDEのパッケージに含まれるRXTXライブラリと何かしらバッティングでもしてるのか、などと疑って調べてみたら、見当違いだった。エラーは、/Library/Java/Extensions/に入れた方のライブラリが出しているもので、シリアルポートの排他制御のために使う/var/lock/ディレクトリが存在しなかったのが原因。どうやら、RXTXライブラリのコンパイルオプションでは/var/lock/を使うのがデフォルトらしい。
Arduino Forumに情報があった。
the default compilation options for RXTX create a library that uses locks to help ensure that only one program tries to access the serial port at a time. This requires you to setup the lock directory (this was the purpose macosx_setup.command script that used to come with Arduino and Processing). If you don't set up the lock directory, you get an error like the one you saw.

Arduino Forum - Serial Port Already in Use - MacOSX leopard
というわけで対策は、/var/lock/を使わないオプションでRXTXをリコンパイルするか、/var/lock/ディレクトリを作成して、ユーザから書き込めるようにパーミッションを設定してやるか。
僕はお手軽に、
$ sudo mkdir /var/lock
$ sudo chown <user> /var/lock
で済ませた。
ちなみに、Arduino Forumの記事にあるようなchmod 777はちょっとイヤだったので、オーナーを使用ユーザに変更するにとどめておいた。今のところ問題なく動いているようだ。

2009/09/23

Java on OSXでWiiリモコン

今さら感は拭えないが、Wiiリモコンとarduinoを組み合わせて遊びたい。
そこでまずは、MacBook上でJavaからWiiリモコンを触れるようにした。
wii remote

準備

JavaとWiiリモコンをBluetoothでつなげてみよう - ブログ: 岡崎 - Okazaki's blog」や「WiiLi.org Wii Linux - JP:WiiremoteJ/ReadMe」などにあるように、必要なものは、Java版Bluetooth API(JSR-82)のオープンソース実装bluecoveと、WiiプロトコルのJavaライブラリWiiRemoteJだけ。2009年9月22日現在での最新バージョン、BlueCove2.1.0とWiiRemoteJ v1.6を使ってみた。

JavaVMの起動オプション

いきなりつまずいた。リモコンを探しに行くところで、
java.lang.IllegalArgumentException: PCM values restricted by JAR82 to minimum 4097
とエラーメッセージが出て止まってしまう。
Googleで検索してみると色々勉強になる(例えばここ)が、結局、「JavaVMの起動オプションに以下を加えることで問題を回避できる」そうだ。
-Dbluecove.jsr82.psm_minimum_off=true
理由は問わない。魔法だと思おう...

実装例 for 安定動作?

とりあえずWiiRemoteJの配布物に含まれているサンプルコードを動かしてみる。
リモコン探して、つないで、ボタンのイベントや加速度センサのデータなどを取得する、一通りの方法はとりあえず分かった。が、このWiiRemoteJ、安定して動作させるのがなかなか難しい、ということも分かった。特にリモコンとの接続確立時に、アプリ側で対処できない(と思われる)エラーがよく起きる。

色々やってみた結果、サンプルコードで使われているfindRemote()でなく、findRemotes(WiiDeviceDiscoveryListener listener)を使った方が、動作が安定する、っぽい。あくまで「っぽい」だけど。WiiDeviceDiscoveryListenerを使ったコードは、例えば以下のようになる。
import wiiremotej.WiiRemote;
import wiiremotej.WiiRemoteJ;
import wiiremotej.event.WRAccelerationEvent;
import wiiremotej.event.WRButtonEvent;
import wiiremotej.event.WRStatusEvent;
import wiiremotej.event.WiiDeviceDiscoveredEvent;
import wiiremotej.event.WiiDeviceDiscoveryListener;
import wiiremotej.event.WiiRemoteAdapter;

public class TestWiiRemote extends WiiRemoteAdapter implements WiiDeviceDiscoveryListener
{
public static void main(String[] args)
{
WiiRemoteJ.setConsoleLoggingAll();
TestWiiRemote test = new TestWiiRemote();
try{
WiiRemoteJ.findRemotes(test,1);
}catch(Exception e){
e.printStackTrace();
}
}

public TestWiiRemote()
{
}

public void findFinished(int numFound)
{
System.out.println(numFound+" remotes has been found!");
}

public void wiiDeviceDiscovered(WiiDeviceDiscoveredEvent evt)
{
System.out.println("remote(#"+evt.getNumber()+") was discovered!");
WiiRemote remote = (WiiRemote)evt.getWiiDevice();

try{
remote.setAccelerometerEnabled(true);
remote.setSpeakerEnabled(true);
remote.setLEDIlluminated(0,true);
}catch(Exception e){
e.printStackTrace();
if(remote!=null && remote.isConnected()){
remote.disconnect();
}
}

final WiiRemote remoteF = remote;
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable(){
public void run(){
if( remoteF!=null && remoteF.isConnected() ){
remoteF.disconnect();
}
}
}));

remote.addWiiRemoteListener(this);
}

public void disconnected()
{
System.out.println("Remote disconnected...");
System.exit(0);
}

public void statusReported(WRStatusEvent evt)
{
System.out.println("Battery level: " + (double)evt.getBatteryLevel()/2+ "%");
System.out.println("Continuous: " + evt.isContinuousEnabled());
System.out.println("Remote continuous: " + evt.getSource().isContinuousEnabled());
}

public void accelerationInputReceived(WRAccelerationEvent evt)
{
double x = evt.getXAcceleration();
double y = evt.getYAcceleration();
double z = evt.getZAcceleration();

System.out.println(x+","+y+","+z);
}

public void buttonInputReceived(WRButtonEvent evt)
{
if (evt.wasPressed(WRButtonEvent.TWO))System.out.println("2");
if (evt.wasPressed(WRButtonEvent.ONE))System.out.println("1");
if (evt.wasPressed(WRButtonEvent.B))System.out.println("B");
if (evt.wasPressed(WRButtonEvent.A))System.out.println("A");
if (evt.wasPressed(WRButtonEvent.MINUS))System.out.println("Minus");
if (evt.wasPressed(WRButtonEvent.HOME))System.out.println("Home");
if (evt.wasPressed(WRButtonEvent.LEFT))System.out.println("Left");
if (evt.wasPressed(WRButtonEvent.RIGHT))System.out.println("Right");
if (evt.wasPressed(WRButtonEvent.DOWN))System.out.println("Down");
if (evt.wasPressed(WRButtonEvent.UP))System.out.println("Up");
if (evt.wasPressed(WRButtonEvent.PLUS))System.out.println("Plus");
}

}
少なくとも自分の環境では、接続時にエラーが出ることはほとんどなくなった。ただ、アプリ終了時のdisconnect()がどうしてもうまくいかない。Ctrl+Cとかでアプリを終了させると、ShutdownHookが利いてdisconnect()が呼ばれるところまでは行くのだが、きちんと切断しきってくれない。アプリが終了した後、Bluetooth接続が残ってしまう。まぁそのままでもいいんだけど、これ、きれいに切りたいなぁ...