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

2012年6月11日月曜日

WindowsにJenkinsを入れてCI #Jenkns

散歩していたら思いついた。

非力なメインサーバーでJenkinsを入れて、ビルドをするよりも実はCorei7-2600Kなメディアサーバーさんでビルドすればいいんじゃね?

Windowsに入れることが、なんか色々とハードル高そうだけど、わりとやってる人がいるっぽいので挑戦。

Windows上でJenkinsを動作、Linuxで動いているGitリポジトリからプロジェクトをクローンし、antでビルドする感じ。

インストール

インストール自体は簡単。公式サイト(http://jenkins-ci.org/)から、インストール用のファイルをダウンロードしてきて、実行するだけ。

色々と設定

とりあえず、URLを変更。ちなみに、今回は
F:\/Program Files/

にインストールしたので。

F:\Program Files\Jenkins\jenkis.xml

を開いて

 <arguments>-Xrs -Xmx256m -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar "%BASE%\jenkins.war" --httpPort=8080</arguments>



  <arguments>-Xrs -Xmx256m -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar "%BASE%\jenkins.war" --prefix=/jenkins --httpPort=80</arguments>

これで http://localhost/jenkins でアクセスできるようになる。

次いで、実行ユーザーの変更。

タスクマネージャー→サービス→サービス から Jenkins を選び プロパティ

ログオンタブで アカウント を設定する。


Gitと言うか、SSHでリポジトリにアクセスするためのユーザに設定。

ちなみに、gitはCygwinのgitを使っています。

 この辺まで来たら再起動。サービス画面から停止、開始でも良いけどコマンドでやるなら

NET STOP Jenkins

NET START Jenkins

とする。

次に、Jenkinsの設定をブラウザから行う。

JenkinsのWorkspaceはデフォルトでは

インストールしたフォルダ\jobs 以下に作られるらしい。Program File なんてスペースの入ったフォルダの下で作業しようものなら、何が起こるか分からんので変更。

システムの管理→高度な設定


から、適当な場所を選択する。

プラグイン

使っているプラグインは

Jenkins git plugin : https://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin

android emulator plugin : https://wiki.jenkins-ci.org/display/JENKINS/Android+Emulator+Plugin

それぞれインストールしたら、パスの設定なんかをやっておく。Android SDK や Gitのインストールは省略。

とりあえずこんなもん。

現状

残念ながらこれでビルドをしようとすると失敗する。エミュレーターの立ちあげでコケてる感じがする。

スペック不足はたぶん無いので、プロジェクトの設定かなと疑ってみる。

まあ、気長にやっていきましょうかね。

2012年3月2日金曜日

[JUnit]DatePickerをテストケースから操作する[Android]

Androidのテストでは、ウィジェットの操作はUIのスレッドで動作させる必用があるとのこと。

今回は、DatePickerを操作する。ソースは以下。

  activity = getActivity();

  fromDate = (DatePicker) activity.findViewById(R.id.from_data);
  toDate = (DatePicker) activity.findViewById(R.id.to_data);
  activity.runOnUiThread(new Runnable() {

   @Override
   public void run() {
    // TODO Auto-generated method stub
    fromDate.init(fromY, fromM, fromD, null);
    toDate.init(toY, toM, toD, null);
   }
  });
アクティビティのインスタンスから、runOnUiThreadを使ってUIのスレッドでの動作を行わせるとのこと。

今夜はリファクタリング的なことをやって、無駄に増えていった処理用のクラスを纏めていました。


リファクタリングとは、外部から見た時の挙動が変わってはいけない、とのこと。この本に書かれていた。

多分、クラスを分ける必用があるんだろうなぁとは思うけれど、どう分ければいいのかがよく分かっていなくて、後から見返すとかなりアレなことになっていることが多い。

2012年2月26日日曜日

【JUnit】データベースを操作するテスト【試してみた?】



先日作ったByte配列に変換したりその逆にしたりするメソッドを利用して、データベース周りを実装する。

割りと、参考サイトを見ていると実装の方法は同じなので今回つまった部分とか。

Blob型で保存をするので、PupSQLiteなどのビューアを利用してもデータの内容の確認ができない。そこで、正しくデータが保存できているのか等を確認するために、JUnitを利用することにする。

Byte配列とは出るけど、内容確認は無理




1:データベース操作クラスのテスト
  • データを挿入するinput
  • 削除するdelete
  • 全てのデータを得るfindAll
  • 任意のデータを得るfindById

それぞれのテストの実装は次の通り。
public class PartyDaoTest extends AndroidTestCase {
 DbHelper helper;
 PartyDao dao;

 Party party; //保存対象のデータ

 public PartyDaoTest() {
 }

 protected void setUp() throws Exception {
  super.setUp();

  helper = new DbHelper(getContext());
  dao = new PartyDao(helper.getReadableDatabase());

  setData();
 }


 protected void tearDown() throws Exception {
  super.tearDown();
 }

 public void testInsert() {
  assertNotNull("Should be not null if save data", dao.insert(party));
 }

 public void testFindAll() {
  dao.insert(party);
  
  assertTrue("Should be returned bigger than one.",
    dao.findAll().size() > 1);
 }

 public void testDelete() {
  assertNotNull("Shoudl be not null", dao.delete((int) dao.insert(party)));
 }

 public void testFindById() {
  int id = (int) dao.insert(party);
  Party p = dao.findById(id);
  
  assertEquals(party.getDate(), p.getDate());
 }
}

問題なくテストも成功するから多分、大丈夫。

2:利用するActivityのテスト

保存対象のデータ'Party'を新たに作ったIntentにputし,setActiviyIntentでActivityに渡す。

testSaveData()で、実際のテストを行なっている。ちなみに、テスト対象のsaveData()メソッドは、データを保存すると返り値としてデータのIDを返すと言う物。
public class SelectMemberActivityTest extends
  ActivityInstrumentationTestCase2<テスト対象のActivity> {

 Party party; //保存対象のデータ
 SelectMemberActivity mActivity;

 public SelectMemberActivityTest() {
  super("パッケージ名", テスト対象のActivity.class);

 }

 protected void setUp() throws Exception {
  super.setUp();
  Intent intent = new Intent(getInstrumentation().getContext(),
    テスト対象のActivity.class);
  party = setData();
  intent.putExtra("store_data", party);

  setActivityIntent(intent);
 }

 protected void tearDown() throws Exception {
  super.tearDown();
 }

 public void testSaveData() {
  assertNotNull("データを保存する.返り値がNull以外で成功とする", getActivity().saveData());
 }
}

さて、この簡単な実装だけど最初コンストラクタになぜか引数がついていてRuntimeExceptionが発生してた。気づいて修正するのにほぼ1日かかったって言うね・・・。ココみて直した。

さて、興味があるのでJUnitとか使ってますけど、決して今の開発スタイルはTDDとは言えないと思います。

そもそも、今回書いたテストも正しいテストと言えるのか分からない。その辺りは勉強なんだけど。

今風のソフトウェア開発の本を読めば多分書いてあるんだろうね。まあ、実践も一緒にやっていきたい所ではあるけれど。

多分今回書いたのはこのスライドで言うところの学習テストに当たるのかな。初めて使う機能だもんね、しっかりと抑えておきたい所ではある。

2012年2月24日金曜日

【Android】オブジェクトをByte配列にしたりその逆したり【作ったよ】

現在製作中のAndroidアプリでデータベースを利用することになっている。

カラムをいちいち作ってもいいんだけど、割りとクラスの構成が面倒くさいのでBlob型で保存することにした。

という訳で、オブジェクトをByte配列に変換したり戻したりするメソッドを実装しますか。

Byte配列に変換
 public byte[] toByte() {
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  try {
   ObjectOutputStream oos = new ObjectOutputStream(baos);
   oos.writeObject(this);
  } catch (IOException e) {
   // TODO: handle exception
   return null;
  }
  return baos.toByteArray();
 }


バイト配列から変換
 public static Party byteToParty(byte[] b) {
  Party p = null;
  try {
   p = (Party) new ObjectInputStream(new ByteArrayInputStream(b))
     .readObject();
  } catch (OptionalDataException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (StreamCorruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return p;
 }


JUnit使って動作確認。テストケースはこれで。
 public void testToByte() {
  byte[] b = party.toByte();
  assertNotNull(b);
 }

 public void testByteToParty() {
  byte[] b = party.toByte();
  Party p = Party.byteToParty(b);
  assertEquals(p.getDate(), date);
  assertEquals(p.getTime(), time);
  assertEquals(p.getPlace(), place);
  assertEquals(p.getperson(), person);
  assertEquals(p.getKeyword(), keyword);
  assertEquals(p.getPlaceBy(), placeBy);
 }

問題なくテストを通ったから良いでしょう。

2012年2月5日日曜日

【Android】複数のメールアドレスに対して送信するIntentを作りたい

Intentから、メールを送信するのはやり方としては様々なサイトに書かれていましたが、複数のメールアドレスに対して送る方法が無かったのでメモ。

具体的には、extra data を EXTRA_EMAILに、そしてToアドレスをStringの配列にする必用があったとのこと。

参考にしたサイトはこちら

今作っているアプリは、ListViewで選択をしたメールアドレスに対してメールを送信するので

1.ArrayListにToアドレスを保存

2.配列に変換

3.putExtra

な感じ。

コードは以下。
ArrayList toList = new ArrayList();
    Intent intent = new Intent(Intent.ACTION_SEND);
    for (Member member : memberList) {
     if (member.isJoin()) {
      toList.add(member.getMail());
     }
    }
    intent.putExtra(Intent.EXTRA_EMAIL,
      (String[]) toList.toArray(new String[0]));
    intent.putExtra(Intent.EXTRA_TEXT, "メール本文");
    intent.putExtra(Intent.EXTRA_SUBJECT, "メールタイトル");
    intent.setType("message/rfc822");
    startActivity(Intent.createChooser(intent,
      "choose email client"));


当然だけど、putExtraはArrayList型を引数にしてあげても動く。お陰で詰まってた。ArrayListでも大丈夫だろう、と勘違い。

2012年1月19日木曜日

[YahooPipes!]XMLをJSONに変換したい!!


xmlよりもJsonの方が好きです。

JavaScriptからデータを持ってくるとき、XMLHttpRequestだと例のクロスドメイン問題があるからね。

動的にscriptタグを作ってJSONPとかを埋め込むと便利なんです。

しかし、割りとxmlでしかデータを配信していないサービスが存在するのもまた、事実。

という訳で、xmlをJsonに変換するサービスを作ればいいじゃないかっ!

時間もないし簡単に作りたいじゃないかっ!

なんて、探していたらYahoo!Pipesってのがあるんですね。知らなかったよ。

マッシュアップをする時には、すごく便利そう。今回使うモジュールは,
-URLInput
-URLBuilder
-FetchData

のみ。アウトプットはノーカンだろJK...

出来たパイプは下の図の通り。すっげぇ!! 何もやってない!!
今回作ったモジュールたち

JSONへの変換だとかは、YahooPipes!のデフォルトの機能を使います。

URL

パラメータ
_reader:出力形式。JSONにしましょう。
xmlurl:XMLを吐き出すURL。WebAPIだとか色々。
_callback:コールバック関数。JSONPにしたいときに。

作ったもののレベル的には、本気で大したものじゃないですね。でも、必要だったんだからしかたない。

JSONの出力もYahooPipes!の機能ではなく自作するべき。いらん情報がくっついて来ているわけだし。

まあ,当面はこれを利用することになる。改造とかはそのうち・・・そのうち・・・

2012年1月7日土曜日

Windowsタスクスケジューラとシェルスクリプトを使って、サーバーのバックアップを自動化したい

この記事を読んだ。

>PCのバックアップやデフラグなど、ソフト側でスケジュール化できるものは自動化を優先し

あー、やっぱりバックアップは自動化するべきなんだなぁ・・・。

家のサーバーのバックアップは手動で

1. VMWareESXi上で、仮想ハードディスクをコピー

2. サブマシンにコピー

3. 外付けハードディスクにコピー

を行なっている。基本的に週一で、土曜日に行う。

手動で、しかもサブマシンはLAN外には公開していないので外出時にはバックアップを取ることができないといえば、できない。

ちょっと、問題には思っていた。

という訳で、バックアップ処理の自動化を行うべくスクリプトを書く。

今回は、Cygwin上でSSHのリモートコマンドを実行し、最終的にサブマシンにコピーする部分までを一気に行う。

外付けハードディスクは、実は常時通電をしておらず、バックアップの時のみ手動で電源を入れている。この作業の自動化は今回は見送る。

と言うか、無理なんじゃないだろうか・・・?

これが、今回用意したスクリプト
backup.sh

backup.sh
#!/bin/sh

USER_NAME=user_name
HOST_NAME=host_name_OR_ip_adress
BACKUP_CMD=vmfs/volumes/DateStore1/backup.sh

TARGETS="Server1 Server2"

STORE_DIR=/cygdrive/G

delete_old_data(){
        ssh $USER_NAME@$HOST_NAME rm -rf /vmfs/volumes/DateStore2/*
}

delete_old_data

for target in $TARGETS; do
        ssh $USER_NAME@$HOST_NAME $BACKUP_CMD $target
done

scp $USER_NAME@$HOST_NAME:~/vmfs/volumes/DateStore2/* $STORE_DIR

delete_old_data


ま、これだけですね。
$BACKUP_CMDは事前に用意したVMWareESXiで仮想ハードディスクをコピーするためのコマンド。このサイトを参考に作った。

ちなみに、参考サイトではCRONを使って自動化を行なっている。というのも、別のNFS別のサーバーにコピーしたファイルを転送しているため。

今回は、サブマシンがWindowsなのでその手段がとれない(できるけど、面倒くさい)。

Winndowsと言うことで、自動化はタスクスケジューラを使う。

コントロールパネル→管理ツール→タスクスケジューラ

”基本タスクの作成”

から、名前と説明を入力し

トリガー:毎週
    毎週:開始日、間隔、曜日を設定。

操作:プログラムの実行
    プログラムの開始:sh.exeのパスをプログラム/スクリプトに、さっき書いたスクリプトのパスを、引数の追加(オプションん)に入力。
   
これで完了ですね。

設定が正しいかどうかは、追加したタスクを選択して実行をクリックする。

これで、バックアップも自動で行えるようになりました。

あとは、アップデートかなぁ・・・?サーバーたちのアップデートは毎晩行うように設定してありますが、Windowsマシンはどうでもないので。

これは、大したことでもないでしょうね。

2012年1月5日木曜日

【Android】電話帳からメールアドレスと名前を取得する

今作っているアプリで、電話帳から登録名とメールアドレスを取得し一覧を表示する必用がある。

そこで、実装を試す。そんなに難しくないとたかをくくっていた。

なんとか、取得ができたのでメモ。

まず、失敗例





String[] Projection = { Email.DISPLAY_NAME, Email.DATA };

Cursor managedQuery = getContentResolver().query(Email.CONTENT_URI,Projectionl, null, null, null);

while (managedQuery.moveToNext()) {
 builder.append(managedQuery.getString(managedQuery.
getColumnIndex(Email.DISPLAY_NAME))).append(",").
append(managedQuery.getString(managedQuery.getColumnIndex(Email.DATA1)));
}
Log.d(TAG, builder.toString());

これを実行すると、メールアドレスの取得は出来るのだが、DISPLAY_NAMEがひたすらnullになる。それじゃあ、困る。


そこで、そもそも本当に登録名も取得できるのかを確認。

Cursor managedQuery = getContentResolver().query(Email.CONTENT_URI,
 null, null, null, null);

StringBuilder builder = new StringBuilder();
while (managedQuery.moveToNext()) {
 for (int i = 0; i < managedQuery.getColumnCount(); i++) {
  builder.append(i).append("=").append(managedQuery.getString(i));
  builder.append(",");
 }
 builder.append(managedQuery.getString(1));
 builder.append("\n");
 }
Log.d(TAG, builder.toString());

一応 i = 31 || 33 || 57 || 59

の時に、登録名を取得できていることが確認できた。

次に

builder.append(" is ").append(managedQuery.getColumnName(i));

を挟んでみて、カラムの名前を取りに行くと

i = 31 がdisplay_name

i = 32 がdisplay_name_alt

i = 57 がsort_key_alt

i = 59 がsort_key

さて、データがあるのなら話は早い。失敗例を元に実装を行う。


String[] Projection = { "display_name", Email.DATA };

Cursor managedQuery = getContentResolver().query(Email.CONTENT_URI,
 Projection, null, null, null);

StringBuilder builder = new StringBuilder();
while (managedQuery.moveToNext()) {
 builder.append(managedQuery.getString(0));
 builder.append(managedQuery.getString(1));
 builder.append("\n");
}
Log.d(TAG, builder.toString());

これで、登録した名前とメールアドレスを取得することができた。長かったぜ・・・。

ちなみに、Email.DISPLAY_NAMEの値は公式リファレンスによると、Data4 となっているらしい。

2番目の方法で、データを全部取得した時Data4は確かにnullになっていた。確かに・・・ああ、なっていたさ・・・。

2011年12月9日金曜日

PreferenceActivity でサマリを動的に変更したい。



現在作成中のアプリでは、PreferenceActivityを利用しています。

設定した値によって、表示されている項目のサマリーが変更するとステキなので、実装をします。



こういう感じで、設定項目の下に設定値を表示したい。
具体的には、項目の内容が変更されたときのリスナーを設定し、コールバックメソッドでサマリーの内容を書き換えます。

リスナーの設定を
onResume()

で行い、解除を

onPause()


で行なっています。


 SharedPreferences.OnSharedPreferenceChangeListener listener; //設定値変更時のリスナーを保存するField
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.preference_activity);
  addPreferencesFromResource(R.xml.party_preference);
  
  //省略
  listener = setOnChangeLitener();
 }
 
 //設定値変更時のリスナークラスの定義
 private SharedPreferences.OnSharedPreferenceChangeListener setOnChangeLitener() {
  return new SharedPreferences.OnSharedPreferenceChangeListener() {

   @Override
   //コールバックメソッド
   public void onSharedPreferenceChanged(
     SharedPreferences sharedPreferences, String key) {
    // TODO Auto-generated method stub
    Log.d(TAG, "value is changed");
    //全てのPreferenceに実行する
    findPreference(key).setSummary(
      sharedPreferences.getString(key, ""));
    //特定のキーの場合のみ、実行する
    if(key.equals(SEARCHBOOKS_KEY)) 
    searchbooksPref.setSummary(
      searchbooksMap.get(sharedPreferences.getString(key, ""))); }    
   }
  };
 }

 @Override
 protected void onPause() {
  // TODO Auto-generated method stub
  super.onPause();
  //登録
  getPreferenceScreen().getSharedPreferences()
    .unregisterOnSharedPreferenceChangeListener(listener);
 }

 @Override
 protected void onResume() {
  // TODO Auto-generated method stub
  super.onResume();
  //消去
  getPreferenceScreen().getSharedPreferences()
    .registerOnSharedPreferenceChangeListener(listener);
 }  

2011年11月29日火曜日

JSONGen JSON文字列からJavaのコードを作りたい

WebAPIなんかを利用する際、JSON文字列を取得し解析し、JAVAのクラスとして利用することは割とある。

データの量が少ないものならば良いが、多くなってくると自分でコードを書くのが面倒くさくなる。

そんなとき、このようなサービスがあった。


JSONGen

使い方は簡単、URLにWebAPIのリクエストを書けばいい。

折角なので試してみよう。

URLを
http://express.heartrails.com/api/json?method=getAreas

とし、パッケージ名、クラス名を適当につける。

また、Use Java 5 Genericsにチェックを付ける。


次のようなJAVAのコードが得られる。


package tets;

import java.util.List;

public class Result1{
    private Response response;

  public Response getResponse(){
  return this.response;
 }
 public void setResponse(Response response){
  this.response = response;
 }
}


package tets;

import java.util.List;

public class Response{
    private List area;

  public List getArea(){
  return this.area;
 }
 public void setArea(List area){
  this.area = area;
 }
}


これは便利。

ただし、JSONにJavaの予約語を利用している場合は注意。問答無用で変数が作られるので、困ったことになる。

その場合は、利用しているライブラリによって異なるが例えばJSONICならアンダースコアをつけることで回避できる。

何にせよ便利。