2014年10月31日金曜日

com.sun.syndication.io.ParsingFeedException: Invalid XML: Error on line 14: An invalid XML character (Unicode: 0x1d) was found in the CDATA section.

概要

JavaでRSSフィードを取得してパースしているときに発生しました
原因はXML内にunicodeが含まれるためでした
力技ですがunicodeが含まれている場合の対応コードを書いたので紹介します
本当はRSSを配信しているサービスとかASP側で対応してほしいものですが。。。

環境

  • Java 1.7
  • Eclipse 4.4(Luna)
  • rome-fetcher 0.9

対応方法

今回、自分はrome-fetherというJavaのRSSパーサを使っていました
エラーが発生する箇所はFeedFetcher.retrieveFeedというメソッドをコールした部分で
retrieveFeedの引数に指定したRSSフィードのXMLがぶっ壊れているとParsingFeedExceptionが発生するようです

対応方法はだいぶ力技な感じがしますがParsingFeedException or SAXException が発生したら独自のRSSパーサがコールされ、独自のパーサでは取得したRSS情報のXMLからunicode文字を削除した上でXMLの解析を開始させます

やっていることは以上でソース的には以下のような感じにしました

package test;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * 独自のRSSパーサをテストするためのクラス
 * 
 * @author kakakikikeke
 *
 */
public class RssReader {

    String uri;

    public RssReader(String uri) {
        super();
        this.uri = uri;
    }

    /**
     * 独自のRSSパーサ
     */
    public void fetcher() {
        // Javaがデフォルトで提供するDOM解析クラスを使ってパースします
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            factory.setValidating(true);
            Node root = null;
            try {
                // 何も考えずパースする、XMLが壊れている場合はSAXExceptionが発生する
                root = builder.parse(uri);
            } catch (SAXException e) {
                // XMLが壊れている場合はunicodeを取り除いた上でパースする
                root = builder.parse(getTrimmedUnicodeXML(), "UTF-8");
            }
            // パースが完了したらDOM構造から必要な情報を取得する
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                if (nl.item(i).getNodeName().equals("rss")) {
                    NodeList nl2 = nl.item(i).getChildNodes();
                    for (int j = 0; j < nl2.getLength(); j++) {
                        NodeList nl3 = nl2.item(j).getChildNodes();
                        for (int k = 0; k < nl3.getLength(); k++) {
                            if (nl3.item(k).getNodeName().equals("item")) {
                                NodeList nl4 = nl3.item(k).getChildNodes();
                                for (int l = 0; l < nl4.getLength(); l++) {
                                    System.out.println(nl4.item(l).getNodeName());
                                    System.out.println(nl4.item(l).getFirstChild().getNodeValue());
                                }
                                System.out.println();
                            }
                        }
                    }       
                }
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * unicode文字を取り除したXML情報を取得する
     * 
     * @return
     * @throws IOException
     */
    public InputStream getTrimmedUnicodeXML() throws IOException {
        // RSSフィードを指定してXMLを取得し文字列に格納する
        URL url = new URL(uri);
        HttpURLConnection urlconn = (HttpURLConnection)url.openConnection();
        urlconn.setRequestProperty("Accept-Charset", "UTF-8");
        urlconn.connect();
        BufferedReader reader = new BufferedReader(new InputStreamReader(urlconn.getInputStream(), "UTF-8"));
        String xml = "";
        while (true){
            String line = reader.readLine();
            if ( line == null ){
                break;
            }
            xml += line;
        }
        reader.close();
        urlconn.disconnect();
        // 取得したXMLからunicode文字を取り除く
        xml = xml.replaceAll("[\\00-\\x08\\x0a-\\x1f\\x7f]", "");

        // 最終的にInputStreamで渡す必要があるため一旦ファイルに書き出す
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("rss20.xml"),"UTF-8"); 
        PrintWriter pw = new PrintWriter(osw);
        pw.write(xml);
        pw.close();
        osw.close();

        // ファイルからInputStremを生成する
        InputStream in = new FileInputStream("rss20.xml");
        return in;
    }

    /**
     * 実行メインクラス
     * 
     * @param args
     */
    public static void main(String[] args) {
        // ここにパースが失敗するRSSフィードのURLを指定してください
        String uri = "http://rssblog.ameba.jp/figma/rss20.xml";
        RssReader rss2 = new RssReader(uri);
        rss2.fetcher();
    }

}

ポイントは
root = builder.parse(uri);の部分でここでSAXExceptionが発生したらgetTrimmedUnicodeXMLでunicode文字が除外されたXMLを元にparseを実行します

parseメソッドはjavax.xml.parsers.DocumentBuilderクラスのメソッドでInputStreamを引数にすることでパースすることもできます
なのでgetTrimmedUnicodeXMLでは一旦XMLを取得した上でStringに格納しreplaceAllメソッドでunicode文字を空白に置換した上で
さらにファイルに書き込んでそれを読み込むことでInputStreamを生成しています
(この辺はわざわざファイルにしなくてもInputStreamを生成できるもっといい方法があるかもしれません)

流れはだいたいそんな感じです
一応これを使ったらちゃんとパースして目的の情報を取得することはできました
それでもまだエラーになってしまう場合はreplaceAllする文字の種類を増やしてみてください
XMLの詳しいルールはわかりませんが、まだXML内に含んではいけない文字が含まれているはずです

参考サイト

2014年10月29日水曜日

【emacs】cond: Invalid version list `(8 5 -4)'

概要

Cygwin + apt-cyg でインストールしたemacsでpackage.elを使おうとしたら発生しました
原因はよくわからないですが、emacsをapt-cygでアップデートしたら直ったのでメモしておきます

環境

  • Cygwin 1.7.32-1
  • emacs 24.4-1
  • apt-cyg 0.59

現象

emacsの24系からデフォルトでインストールされているpackage.elを使ってpackage-list-packagesを実行しようとしたところ
パッケージの一覧が表示されずミニバッファに
cond: Invalid version list(8 5 -4)’`が表示され終了となってしまう

対応方法

自分の場合はemacsをアップデートしたら直りました
emacs24.3系から24.4にアップデートされました

apt-cyg remove emacs
apt-cyg update
apt-cyg install emacs

でインストールしたemacsのバージョンが24.4系になっていればOKです
再度package-list-packagesを実行すればパッケージの一覧が表示されるはずです

ちょっと原因がわからず気持ち悪い感じですが、とりあえず自分は上記で対応できたので忘れないようにメモしておきます

2014年10月24日金曜日

iPhoneアプリを公開するために行った10のコト

enter image description here

概要

iOSアプリを公開するための手順を自分なりにまとめてみました
新しくなったiTunesConnectやMemberCenterといろいろと必要な要素が登場してきます
一応、上から順を追って記載しますがそれぞれ単発で見てもOKかと思います

所感としては、Androidの10倍は大変でした。。
たぶん慣れてる人は簡単にできると思うのですが、自分みたいな素人だと結構たいへんかなと思います

環境

  • Xcode5.1.1
  • Mac OS X 10.8.5

やった10のコト

Xcodeでのアプリ開発

言わずもがな
ほとんどのデベロッパーの方はまずAppleStoreからXcodeをダウンロードしてアプリを作成してシミュレータで動作確認して、なんか公開しようかなーと思っていろいろとやりはじめると思います
自分はそうでした
そしてここからが苦難の連続でした

AppleDeveloperProgramへの参加

年間8,000円弱をAppleにお布施することで得られるアプリ公開権利です
やり方はネットで探せばいろいろと出てくると思います
お持ちのAppleAccountを使って登録すると思いますが、途中でカードの情報とか請求先情報とか入力しました
すでにAppleAccountにそういう情報が登録されている場合は不要になるかもしれません

DeveloperProgramの登録でつまづいたのが「アクティベーション」でした
DeveloperProgramに参加すると参加者だけが使える管理画面(MemberCenterやiTunesConnectなど)があるのですが購入したら速攻使えるわけではありません
所謂、アクティベーション作業が必要でWebでいろいろ調べる限りだとアクティベーション用のメールが来て、そこに書かれているコードを専用のフォームに入力すると完了、という感じらしいのですが一向にメールが来ませんでした。。。
StackOverFlowとかで調べてみると同じようにメールが来ない人がいて、その人はメールアドレスを変更したいという問い合わせして解決しているようでした
自分もいつまで経っても(と言っても半日くらい)アクティベーションされなかったので、英語で問い合わせしたところどうやらメール方式でのアクティベーション方法は廃止されたようで、自動でアクティベーションされるようです
1日1回くらいのタイミングでアクティベーションされるようで、購入したちょうど24時間後くらいのタイミングでMemberCenterを確認したところアクティブ状態になっていました
自分が短気すぎて半日で問い合わせしてしまってすいませんでした。。。
ちなみにアクティベーションされているかどうかはMemberCenterのYour Accountにログインすれば確認できると思います

キーチェーンアクセスを使ってCSRを作成する

CSRはCertificateを作成するために必要なファイルです
これも作成方法はWebに転がっているので簡単に紹介すると

  • Spotlightでキーチェーンアクセスを起動
  • キーチェーンアクセス -> 証明書アシスタント -> 認証局に証明書を要求を選択、入力時のポイントは以下
    • ユーザのメールアドレスにAppleDeveloperとして登録したメールアドレスを入力
    • 通称は何でもOK
    • CAのメールアドレスは空でOK
    • ディスクに保存にチェック
    • 鍵ペア情報を指定にチェック
  • 保存先を選択して完了

保存が完了するとキーチェーンアクセスの鍵の一覧に表示されるようになります
保存したCSRはあとで使うので捨てないようにしてください

MemberCenterでCertificatesを作成する

MemberCenterとはアプリに必要な証明書やプロビジョニングファイルを管理するための管理UIです

まずMemberCenterで証明書を登録します
MemberCenterにアクセスして

Certificates -> All と選択して [+] ボタン

から新規でCertificateを作成します
作成するときのポイントはキーチェーンアクセスで作成したCSRを指定するところでしょうか
特につまづくとこはないと思います
自分はDevelopmentとProductionのそれぞれの証明書を作成しました

MemberCenterでAppIDを作成する

いきなりですが、
XcodeのBundle IDと同じBundleIDを持つようにAppIDを作成する
これポイントです
XcodeでBundleIDを確認するには

プロジェクトを選択して Targets -> General -> Bundle Identifier

で確認できます
例えばXcodeで確認したBundle Identifierが「kakakikikeke.testApp」だったとすると
MemberCenterでAppIDを作成するときにApp ID Suffixで以下のように入力します
enter image description here
見てのとおりですが、Wildcard App IDを選択してBundle IDにワイルドカード付きのIDを指定します

別にこれと同じにする必要な全くないですが、要するにここのIDがXcodeで設定したBundle Identifierと同じまたはワイルドカードで識別できるIDにしなければいけないということです

あとで説明もしますがここの設定がちゃんとできていないXcodeからiTunesConnectにうまくアプリを配布することができません
アプリを配布できない他、Validateという配布前にやる儀式みたいなやつもずっとエラーになってパニックになります
なので、BundleIDは意識して進めることをおすすめします

XcodeにMemberCenterの情報を同期してプロビジョニングファイルを自動生成させて、設定する

Xcodeを開いて

Xcode -> Preferences -> Accounts -> View Details で左下の更新ボタン

を押すと存在するAppID分Development用のプロビジョニングファイルを作成してくれます
そしてXcode上にMemberCenter上にある証明書とプロビジョニングファイル情報を同期してくれます

プロビジョニングファイル自体の作成は正直手動でやっても問題ないです
でもMemberCenterに行かなくてもXcodeからプロビジョニングファイルを作成することができる便利な機能なので載せました
あとそういう機能があり勝手にMemberCenterにプロビジョニングファイルが作成されるということを認識しておくことも大事かなと思います

また、アプリを公開するときはDistribution用の証明書でDistribution用のプロビジョニングファイルを作成する必要があります、これに関しては次で紹介します

MemberCenterでDistribution用のプロビジョニングファイルを作成

アプリを配布するためのプロビジョニングファイルをMemberCenterで作成します
作成するときのポイントは

  • Distribution -> App Storeで作成すること
  • AppIDの選択では、公開するアプリのBundle IDと同じAppIDを選択すること
  • 証明書の選択ではDistribution用の証明書を選択すること(ない場合はCertificatesから作成しておくこと)

といった感じです、名前は好きなモノを入力してください
証明書とプロビジョニングファイルは削除可能なのでミスったりしたら作りなおしてOKです
ただ、AppIDは一度作成すると削除することができません
おそらく(後述しますが)iTunesConnectで作成したアプリとXcodeのBundle IDに紐付いており削除できてしまうとiTunesConnectに作成したアプリが公開されなくなったりするためだと思います

Xcodeへのプロビジョニングファイルの設定

MemberCenterで作成したプロビジョニングファイルをXcodeに設定します

プロジェクトの選択 -> Targets -> Build Settings -> Code Signing -> Provisioning Profile

で作成したDistribution用のプロビジョニングファイルを設定します

作成したプロビジョニングファイルがでてこない場合は、前に紹介した「XcodeにMemberCenterの情報を同期させる」方法で同期すると選択できるようになると思います

iTunesConnectにアプリを登録する

MemberCenterとは別にアプリ自体を管理するサイトがiTunesConnectです
AppleAcountでログインしましょう
※iPhone6の発表翌日からiTunesConnectが新しくなりました、本紹介では2014/10/23現在でもiTunesConnectをベースに話を進めます

アプリの登録の仕方もそんなに大変ではありません
アプリを登録して上から順次必要な情報を埋めていけばOKです
情報が下不足な場合には「保存」した際や「レビュー審査のために送信」をクリックしたときに赤枠や赤字で教えてくれます

登録時に自分が気になったポイントだけ紹介します

  • スクリーンショットはXcodeでシミュレータ起動してCmd+sで取得できる(iPhone6とiPhone6Plus用のスクリーンショットをアップロードすることもできる)
    • 保存場所はデスクトップに保存される
  • Appアイコンは角丸にしなくてOK、1024 * 1024 を四角いまんまアップロードする
  • レーティングもちゃんと設定すること
  • 新規で登録するときに「バンドルID」を選択するところがあるが、これはXcodeとMemberCenterに登録したAppIDと同じものを選択すること、あとから変更することもできる
  • 価格タブで公開するときのアプリの値段を決まること

設定する項目を多くポイントを言い出すとキリがない感じもしますが、だいたい上記かと思います
特にBundleIDの部分は気をつけないとちゃんとXcodeとiTunesConnectの連携ができなくなるのでValidateの時に困ると思います

実機をつないでValidateをかける(実機テストもやりましょう)

Validateとは簡単に言ってしまうと公開前に実施する最終テストみたいなものです
Validateには実機が必要です

実機のiPhoneをMacに接続します
接続するとまずOrganizerというXcodeの機能が実行されます

Window -> Organizer -> Devices

まず端末情報をMemberCenterに登録します
OrganizerのDevicesで左下にAdd to Member Centerというボタンがあるのでこれクリックするだけです
これはボタン一発でXcodeが自動でやってくるので問題ないと思います
この時OrganizerでDevicesを見た時に該当のデバイスのランプが緑になっていないと実機テストができません
oganizer_device_check.png

ただ、実機テストはできませんがValidateをかけられる準備は完了しました
ValidateをかけるにはXcodeに実機を認識させる必要があるみたいです(かなり不確かなので間違っていたらご指摘ください)

Validateはアプリを配布する最終手順みたいな感じです
Xcodeの左上でアプリを実行するデバイスを選択できるのでシミュレータから「iOS Device」に切り替えましょう
そして、

Product -> Archive

とすればビルドが実行されます
ビルドが成功するとOrganizerが起動します
Organizerが起動したら右のほうにある「Validate」をクリックします
Validateでは

  • iTunesConnectへの認証
  • MemberCenter側のプロビジョニングファイルとアプリに設定されているプロビジョニングファイルの妥当性検証
  • アプリに瑕疵や過不足がないかのチェック

を主に行ってくれます
その名の通りAppStoreで公開するためのチェックをいろいろとしてくれます
Validateの後は審査があるのでそもそも審査できる状態にあるアプリかどうかをXcode側でチェックする感じでしょうか
最終的に以下のように表示されればValidate完了です

validate_success.png

一応この状態になればDistributeすることができますが、せっかく実機をつないだので実機テストもやっておきましょう
MemberCenterに登録されたデバイスでかつOrganizerでデバイス情報が緑になっていれば、Xcodeでビルドして実機でテストすることができます
実機テストする際にちょっとつまずいた点を記載しておきます

  • Development用のプロビジョニングファイルが必要で接続した実機にプロビジョニングファイル追加する必要がある
  • 実機へのプロビジョニングファイル追加はOrganizer -> Devices -> テストする実機のProvisoning Pfofiles を選択して左下の「Add」で追加することができる
  • プロビジョニングファイルが「Valid profile」にならないとテストできない、Xcodeの左上でデバイスを選択するときに該当のデバイスが表示されない

といった感じです

いざ公開へ

本当にこれで全部できているのかという不安しかないですがValidateが通れば一応公開できる準備はできました

あとはOrganizerでDistributeをクリックしiTunesConnectから「レビュー審査のために送信」をクリックすれば公開のための審査待ちになります
ドキドキしながら審査の結果がどうなるか待ちましょう
(iTunesConnect側で情報が下不足な場合は赤枠でエラーとなるので埋めてから申請してください)

最後に

だいぶ文字だらけでかつ長文になりましたが以上です
たぶんこれでもまだ抜けとか、つまづくポイントはあると思います
正直こんなに大変だとは思いませんでした。。。
感覚的には(iOSアプリに慣れてないのはありますが)Androidの10倍くらい大変な印象でした

あと自分も手探りな感じでやったので間違いがあったらバシバシご指摘いただけると助かります

2014年10月23日木曜日

Windows7で日本語キーボード配列を英語キーボード配列にする方法

概要

WindowsXP以前は「のどか」というソフトを使っていました
が、のどかがWindowsVista以降でサポートしなくなりどうしようと悩んでいたところ後継のソフトを発見したので紹介します

環境

  • Windows7 64bit
  • yamy 0.3

インストール

使用するソフトはyamy(Yet Another Mado tsukai no Yuutsu)というソフトでこれは

窓使いの憂鬱 -> のどか -> yamy

という感じで後継しています

使い方や設定方法はのどかとほぼ同じです
ダウンロードしたzipファイル内にあるyamy.exeを実行し
タスクトレイにあるアイコンを右クリックして

選択 -> 日本語109キーボード(104風)を選択

すればOKです
起動時に実行させたい場合にはスタートアップにでもショートカットを配置してください

あと自分のおすすめ設定ですが、日本語キーボードの場合スペースの右隣りが「変換」というなぞのキーがあります
使わないのでyamyで104に変更するとスペースになるのですが、スペースもまぁ小さいですが一応あるので
自分の場合は、変換 -> 半角/全角でIMEのトグルに変更しています

変更する方法は以下の通り

  1. 104on109.mayu を開く
  2. def subst *変換 = *Spacedef subst *変換 = $ToggleIME に変更
  3. 保存して終了
  4. タスクトレイのyamyを右クリック -> 再読み込み

で完了です

P.S 20150929
dot_mayu ファイルを .mayu ファイルに rename してあげてください
rename しないと起動時に .mayu ファイルがないというエラーが出るようです

2014年10月20日月曜日

ニフティクラウド上のCentOSでdroneが動くか試してみた

概要

droneはGo言語で書かれたCI・CDツールでJenkinsみたいなやつです
今回はニフティクラウド上のCentOSで動作するか試してみました

環境準備

  • CentOS 6.4 64bit
  • docker 1.1.2
  • drone 0.3-v
  • golang 1.3.3

インストール

各種インストールしていきます

GLIBC2.14のインストール

droneの動作にはGLIBCの2.14が必要になります
yumでインストールできるGLIBCのバージョンが2.12でダメなので公式のソースを持ってきてソースインストールします
makeでのビルド時間が非常に長いです

yum -y groupinstall "Development Tools"
wget http://ftp.gnu.org/gnu/glibc/glibc-2.14.1.tar.gz
tar zvxf glibc-2.14.1.tar.gz
mkdir build
cd build
../configure --prefix=/usr/glibc
make
make install

cd /usr/glibc/lib/
cp libc-2.14.1.so /lib64/
ln -nfs libc-2.14.1.so libc.so.6
ldconfig

で以下のような感じになっていればOKです

[root@localhost lib64]# pwd
/lib64
[root@localhost lib64]# ls -l libc.so.6 
lrwxrwxrwx 1 root root 14 Oct 20 00:22 libc.so.6 -> libc-2.14.1.so

dockerのインストール

rpm -Uvh http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
yum -y install docker-io

インストールが完了したらservice docker startでもして起動できることを確認してください

yum install時に以下のようなエラーが出る場合はepel.repoを編集してください

Error: Cannot retrieve metalink for repository: epel. Please verify its path and try again

vim /etc/yum.repos.d/epel.repo

#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=$basearch

baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch
#mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=$basearch

golangのインストール

yum -y install golang

golangはこれだけ

droneのインストール

wget downloads.drone.io/master/drone.rpm
sudo yum localinstall drone.rpm

droneはデフォルトでSQLiteを利用します
MySQLやPostgresを使う方法もあるようです
その場合は/etc/drone/drone.tomlを編集することで実現できるようです

droneもrpmでインストールできれば完了です

起動

docker, golang, droneがインストールできたら早速起動してみましょう

droned -config="/etc/drone/drone.toml"

で起動できます
ターミナルで起動すると標準出力には何も表示されませんが、起動しています
80番でLISTENしているので
http://hostname/にアクセスしてみましょう
まだ使うことはできませんが、起動していることは確認できると思います

セットアップ

http://drone.readthedocs.org/en/latest/setup.html
を元にセットアップしていきます

Githubにアプリを作成

droneと連携するためのアプリを1つ作成します
https://github.com/settings/applications
にアクセスし「Register new application」からアプリを作成します
以下のような感じで作成します
create_github_app.png
黒塗りしている部分はDroneが起動しているホストのIPやホスト名を入力してください

アプリの作成が完了したらアプリの詳細画面に戻りClient IDClient Secretをメモしておきましょう

そして、drone.tomlを編集します
vim /etc/drone/drone.toml

[github]
client="メモしておいたClient IDを入力"
secret="メモしておいたClient Secretを入力"

の部分を記載してdornedを再起動してください

認証

再度http://hostname/にアクセスするとGithubのアカウントを使って認証できるようになっています
enter image description here

Githubというボタンを押下しアプリからの認証を許可して、自分のGithubアカウントのパスワードを入力するとリポジトリが同期してdrone上に表示されます
enter image description here
※この辺りでうまくGithubのOAuthが発動しない場合は、構築したインスタンスのACL、FWの設定周りを確認してみてください

あとは、droneと連携したいリポジトリをアクティブにすればOKです
droneと連携してCIできるようにするには、travis.ciみたいにリポジトリのルートに.drone.ymlというファイルがないとダメなのでアクティブにする場合はファイルをあらかじめpushしておいてください

また、今回はGithubと連携しましたが、設定ファイルを見るとGithub_enterpriseやbitBucketなどとも連携できるので同じようにアプリを作って認証できるようにしてやれば、使えそうです

終わりに

結構長くなりましたが、今回の紹介は以上です
とりあえずCentOS上でも動作させることはできました
特につまるところはなかったですが、つまった場合はdronedでDebugログを出すといいと思います

参考リンク

2014年10月9日木曜日

Apacheをyum updateしたら.htaccessが効かなくなった

概要

自分の開発用のサーバをyum -y updateしたら対象にhttpdパッケージが入っいて、開発だから問題ないと思ってアップデートしたら、いつもBasic認証がかかるページに認証がかからなくなっていたので対処しました
結構焦りました

環境

  • CentOS 5.10 (Final)
  • Apache 2.2.3

各種設定内容

以下の設定として話を進めていきます
2.2.3のデフォルトで何もしていなければ以下の構成になっているかと思います
モジュール関連は今回関係ないですが、基本的なことなのでついでに記載しておきます

  • DocumentRoot
    /var/www/html
  • 設定ファイル配置パス
    /etc/httpd/sites-available
    /etc/httpd/sites-enabled
  • モジュール配置パス
    /etc/httpd/mods-available
    /etc/httpd/mods-enabled
  • モジュールパス
    /usr/lib64/httpd/modules/mod_rewrite.so

設定方法

.htaccess自体はディレクトリごとに設定を変えたいときに使う機能です
Apacheのルール設定は階層な構造的になっていて

システム全体の設定 -> VirtualHostごとの設定 -> ディレクトリパスごとの設定 -> ディレクトリごとの設定

みたいな感じで必要に応じて細かく設定することができるようになっています

で、その階層的な設定の可否を制御しているのがAllowOverrideディレクティブというやつで、こいつの設定次第で細かく設定できるかできないか決まってきます

今回の原因としてはこいつが細かく設定できないモードになっていてディレクトリごとに設定できるはずの.htaccessが使えない状態になっていました

修正した箇所は以下の通り

vim /etc/httpd/sites-enabled/000-default

#AllowOverride None

AllowOverride All

Noneになっているとどうやら細かい設定できないモード(というかここで設定した項目を上書いて変更することはできないモード)になっていたので.htaccessがうまく動いていませんでした
書き換えたあとはservice httpd restartとかでApacheを再起動してください

まぁ要するに大丈夫だろと思ってyum updateしたら設定ファイルまで書き換わってしまって大変なことになったということです

2014年10月7日火曜日

一人hubot

概要

Mac上にhubotをインストールしてhubotと1人で会話してみました
ついでに独自のスクリプトも作成してみました

環境準備

  • Mac OS X 10.8.5
  • Homebrew 0.9.5
  • node 0.10.32
  • redis 2.8.17

hubotインストール

hubotはnodeのパッケージ管理コマンドnpmを使ってインストールします
そして、nodeとnpmのインストールはHomebrewから行うので、まだHomebrewのインストールを行っていない方はこちらから

その前にredisインストール

hubotの動作ににredisが必要なのでインストールします
redisもHomebrewから

brew install redis

起動はredis-serverでOK

node&npmインストール

brew install node

その後hubotインストール

npmコマンドでインストールします

npm install -g hubot coffee-script

hubotのスクリプトはcoffee-scriptで記述するためcoffee-scriptもインストールします
インストールは「-g」を付けてグローバルインストールにします

試す

インストールは以上で完了です
まずredis-severを起動します

redis-server

次にhubotを起動します
適当なworkingディレクトリに移動して試します

cd work/hubot
hubot --create kaka_bot
cd kaka_bot
./bin/hubot

hubotという実行用のシェルを直接叩いて実行するとHubot>というインタラクティブモードになります

Hubot> hubot ping
Hubot> PONG
Hubot> hubo help
...ずらっとヘルプの一覧が表示されます...
Hubot> ping

実行する際はhubotというプレフィックス的なものをつける必要があります
helpを表示すれば実行できるコマンドがいろいろと表示されるので試してみてください
また余談ですが、この状態でredis-cliを使ってredisの中を覗いて見るとhubot:storageというkeyができておりJsonオブジェクトが格納されているのがわかります

カスタマイズしてみる

さて、普通にMac上でhubotを動作させることに成功したので次にカスタマイズしてみたいと思います

カスタマイズは独自のスクリプトを作成することで実現します
スクリプトを作成するには先ほど移動したworkingディレクトリに移動してscrpit配下に独自のスクリプトを配置するだけでOKです

cd work/hubot/kaka_bot/scripts
touch jibanyan.coffee
vim jibanyan.coffee

以下を貼り付けてください

# Description:
#   I love the YokaiWatch.
#
# Commands:
#   hubot jibanyan - Display the Jibanyan image that appear in the YokaiWatch.

module.exports = (robot) ->
  robot.respond /(jibanyan|jiba)$/i, (msg) ->
    imageMe msg, 'ジバニャン', (url) ->
      msg.send url

imageMe = (msg, query, animated, faces, cb) ->
  cb = animated if typeof animated == 'function'
  cb = faces if typeof faces == 'function'
  q = v: '1.0', rsz: '8', q: query, safe: 'active'
  q.imgtype = 'animated' if typeof animated is 'boolean' and animated is true
  q.imgtype = 'face' if typeof faces is 'boolean' and faces is true
  msg.http('http://ajax.googleapis.com/ajax/services/search/images')
    .query(q)
    .get() (err, res, body) ->
      images = JSON.parse(body)
      images = images.responseData?.results
      if images?.length > 0
        image = msg.random images
        cb ensureImageExtension image.unescapedUrl

ensureImageExtension = (url) ->
  ext = url.split('.').pop()
  if /(png|jpe?g|gif)/i.test(ext)
    url
  else
    "#{url}#.png"

スクリプトを配置して再度./bin/hubotで起動してhubot jibanyanとコマンドを実行するとURLが返却されると思います

Hubot> hubot jibanyan
Hubot> http://lohas.nicoseiga.jp/thumb/3949352i?#.png

と、こんな感じでcoffeeスクリプトを書いて配置して再起動するだけで簡単にカスタマイズすることができます

あとはheroku pushとかしてslackなどのチャットサービスと連携していればGUIでも操作できると思います(今回は割愛)

参考サイト

http://nanapi.co.jp/blog/2014/06/04/slack_with_hubot/

2014年10月5日日曜日

Windows7でiTunesがうまく動かない時の対処方法

概要

全く原因不明でWindows上のiTunesが動作しないときに試してみてください

環境

  • Windows7 64bit
  • iTunes 11.4.0.18

対処方法

特に順番等はないので該当しそうなやつを順番に試して貰えればと思います

ファイル削除

  • C:\Users\username\Music\iTunes\iTunes Media\Downloads\ 配下のファイルを全部削除しiTunesを再起動してみる
  • C:\Users\username\Music\iTunes\Album Artwork\Cache 配下のファイルを全部削除しiTunesを再起動してみる
  • C:\ProgramData\Apple Computer\iTunes\SC Info 配下のファイルを全部削除してiTunesを再起動してみる
  • C:\Users\username\AppData\Roaming\Apple Computer\itunes\ 配下のファイルを全部削除してiTunesを再起動してみる
  • C:\Users\username\AppData\Local\Apple Computer\itunes\ 配下のファイルを全部削除してiTunesを再起動してみる

再インストール

  • C:\ProgramData\Apple Computer\Installer Cache\iTunes 11.4.0.18 にあるインストーラを使って再インストール
    C:\Users\username\Music\iTunes\iTunes Music Library.xml をバックアップしておき再度配置すれば、前の設定情報をインポートすることもできる

起動プロセスの確認

  • msconfigのサービスタブでApple Mobile Deviceが有効になっていること
  • msconfigのサービスタブでBonjourサービスが有効になっていること
  • msconfigのサービスタブでiPod Serviceが有効になっていること

私の場合はプロセスが停止されていたのが原因でした
確かに初回インストールしてもう使わないと思ってmsconfigから設定を変更したようなしてないような。。。

2014年10月3日金曜日

ProductAdvertisingAPIで503が出た時の対処方法

概要

ProductAdvertisingAPIはAmazonアソシエイトが提供するAPIサービスです
APIの利用にはDeveloper登録が必要で、APIを使うとオリジナルの広告内容を作ったりすることができます

で、今回APIを実行していたら
Service returned HTTP response code 503
なるエラーが返ってきてうまく結果が取得できない現象が発生したのでその対処方法をご紹介します

環境準備

  • Java over 1.7
  • Product Advertision API 2011-07-27

対処方法

公式のドキュメントを調べてみたら以下を発見しました
https://images-na.ssl-images-amazon.com/images/G/09/associates/paapi/dg/index.html?ErrorNumbers.html

上記に

5XX エラーは、一時的なエラーではなく、Amazon 内部のエラーであることを示しています。503 エラーは、リクエストの送信回数が多すぎるため、リクエストが抑制されていることを示しています。この場合、リクエストの頻度を1秒あたり1回以下に下げる必要があります。

要はリクエストしすぎということです

更に調べると英語のドキュメントですが
https://affiliate-program.amazon.com/gp/advertising/api/detail/faq.html

New per Second 1
New Hourly 3,600

とありました
1秒間に1回しかリクエストしちゃダメ
1時間に3600回しかリクエストしちゃダメ
という制限があるようです
1時間に3600回もリクエストしていないので1秒間のリクエスト間隔が短かったのが原因でした

対処方法はダサいですが、Thread.sleep(5000)として5秒待機させてから次のリクエストをコールすることで対応しました
いちおう私の環境ではスリープを入れたあとは503エラーが発生しないようになりました

スリープ入れたけどダメという場合に503が帰ってきた時にリトライするようにしてください、5回くらいリトライするようにすればきっと大丈夫だと思います
(スリープといい、リトライといいワークアラウンドすぎますね。。。)

もし、制限がいやだという場合は上記リンクを見ればわかりますがお金を払えばlimit解除できるみたいです

2014年10月2日木曜日

【IBOutlet】error: property's synthesized getter follows Cocoa naming convention for returning 'owned' objects

概要

XcodeでInterfaceBuilderを使ってUIからヘッダーファイルに変数名を定義するときに発生しました

環境

  • Mac OS X 10.8.5
  • Xcode 5.1.1

原因と対処方法

XcodeのInterfaceBuilderでヘッダーファイルにIBOutletの変数を作るときに変数名を指定すると思います(UIコンポーネントを選択した状態でCtrlを押しながらヘッダーファイルにピーってやるやつ)
その変数名をつける際に変数名の先頭が「new」で始まっていたのが原因でした

  • アウト
@property (weak, nonatomic) IBOutlet UILabel *newLabel;
  • セーフ
@property (weak, nonatomic) IBOutlet UILabel *notnewLabel;

所謂、予約語ってやつでしょうか
他にも使えない単語があるようです
Objective-Cを普段から書いている人にとっては常識なのだろうか

2014年10月1日水曜日

Jenkinsのビルド出力結果にjava.nio.file.DirectoryNotEmptyException

概要

Jenkinsのビルドを実行した際にビルドは成功しているのだがビルドの出力結果に以下のような怪しいログが出ていることがわかりました

ユーザーanonymousが実行
ln builds\lastSuccessfulBuild C:\Program Files (x86)\Jenkins\jobs\job_name\lastSuccessful failed
java.nio.file.DirectoryNotEmptyException: C:\Program Files (x86)\Jenkins\jobs\job_name\lastSuccessful
     at sun.nio.fs.WindowsFileSystemProvider.implDelete(Unknown Source)
     at sun.nio.fs.AbstractFileSystemProvider.deleteIfExists(Unknown Source)
     at java.nio.file.Files.deleteIfExists(Unknown Source)
     at sun.reflect.GeneratedMethodAccessor110.invoke(Unknown Source)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
     at java.lang.reflect.Method.invoke(Unknown Source)
     at hudson.Util.createSymlinkJava7(Util.java:1194)
     at hudson.Util.createSymlink(Util.java:1112)
     at hudson.model.Run.createSymlink(Run.java:1838)
     at hudson.model.Run.updateSymlinks(Run.java:1819)
     at hudson.model.Run.execute(Run.java:1730)
     at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
     at hudson.model.ResourceController.execute(ResourceController.java:88)
     at hudson.model.Executor.run(Executor.java:234)
ln builds\lastStableBuild C:\Program Files (x86)\Jenkins\jobs\job_name\lastStable failed
java.nio.file.DirectoryNotEmptyException: C:\Program Files (x86)\Jenkins\jobs\job_name\lastStable
     at sun.nio.fs.WindowsFileSystemProvider.implDelete(Unknown Source)
     at sun.nio.fs.AbstractFileSystemProvider.deleteIfExists(Unknown Source)
     at java.nio.file.Files.deleteIfExists(Unknown Source)
     at sun.reflect.GeneratedMethodAccessor110.invoke(Unknown Source)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
     at java.lang.reflect.Method.invoke(Unknown Source)
     at hudson.Util.createSymlinkJava7(Util.java:1194)
     at hudson.Util.createSymlink(Util.java:1112)
     at hudson.model.Run.createSymlink(Run.java:1838)
     at hudson.model.Run.updateSymlinks(Run.java:1820)
     at hudson.model.Run.execute(Run.java:1730)
     at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
     at hudson.model.ResourceController.execute(ResourceController.java:88)
     at hudson.model.Executor.run(Executor.java:234)

ビルドは成功しているのにエラーが出ているという気持ち悪い状態になったので対応してみました

環境準備

  • Windows 2008 R2
  • Jenkins 1.5.73

対応方法

Jenkinsの公式で配布されている.exeファイルからインストールするとインストールディレクトリは
C:\Program Files (x86)\Jenkins\
となり
C:\Program Files (x86)\Jenkins\jobs\
がジョブを管理するディレクトリになると思うので上記にインストールされていることを想定して話を進めます

今回のエラーはlastSuccessfulBuildlastStableBuildというディレクトリが空じゃないよと言われて怒られているようです

確かにエクスプローラ等で
C:\Program Files (x86)\Jenkins\jobs\job_name\lastSuccessful
のディレクトリを見ると空ではないようでした
(archiveディレクトリやらbuild.xmlやらchangelog.xmlやらがありました)

なので素直にlastSuccessful配下にあるファイルをすべて削除してからビルドしてみたのですが、同様にエラーが発生しました
なので思い切ってlastSuccessfulのディレクトリごと削除してからビルドしてみたところ、なんとエラーが出なくなりました

エラーが出なくなったあとのディレクトリ構成を見てみると、これまでディレクトリになっていたlastSuccessfulがシンボリックリンクになっていました

どうやらlastSuccessfulは本来シンボリックリンクで管理されるものらしくbuilds/lastSuccessfulというディレクトリに対するシンボリックリンクになっていました

とりあえずこれでエラーは出なくなったのですが、ではなぜlastSuccessfulがシンボリックリンクではなくディレクトリになってしまっていたのでしょうか・・・

記憶しか遡れなかったのですが、自分がJenkinsをインストールした際に前に使っていたジョブの設定をそのままインポートしたくてjobs配下のファイルを事前にバックアップしておきました
そして、普通に.exeファイルからJenkinsをインストール後jobs配下のファイルやディレクトリをごそっとバックアップしておいたものに置き換えました
おそらく、この置き換えの動作を行ったときにシンボリックリンクからディレクトリに変わってしまったのだと思います
バックアップをzip圧縮で取っていたのでzipで圧縮した際にシンボリックリンクでは保存できずにディレクトリに置き換わったのだと思います

うーん、そもそもWindows使ってるほうが悪いとかそういう問題もありますが、公式でもディレクトリをコピーしてねって言ってるみたいだし、、、でもzip圧縮とかするとシンボリックリンクじゃなくなるみたいだし
zip圧縮しないでバックアップしとけってことですかね