【Java】JavaでXML解析

JavaでXMLを解析します。

  • XMLファイル

    <?xml version="1.0" encoding="UTF-8"?>
    <sample>
    	<location>
    		<area name="area1">
    			<shop type="kanto_shopName1" required="111" />
    		</area>
    		<area name="area2">
    			<shop type="sikoku_shopName1" required="222" />
    			<shop type="sikoku_shopName2" required="333" />
    		</area>
    		<area name = "area3">
    			<shop type="kansai_shopName1" required="444" />
    			<shop type="kansai_shopName2" required="555" />
    			<shop type="kansai_shopName3" required="666" />
    		</area>
    	</location>
    </sample>
    
  • XMLファイル読取

    /*
     * メイン処理を実行します。
     * @param args 引数
     */
    public static void main(String[] args) {
    
        try {
            File fileObject = new File("src/conf/test.xml");
            DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document document = docBuilder.parse(fileObject);
            NodeList nodeList = document.getChildNodes();
            scanNodes(nodeList);
            System.out.println("マップサイズ:" + resultMap.size());
    
            for (String key : resultMap.keySet()) {
                List<Z_XmlAnalyzerBean> list = resultMap.get(key);
                System.out.print("全体キーは" + key + "@");
                for (int i=0; i<list.size(); i++) {
                    Z_XmlAnalyzerBean bean = list.get(i);
                    System.out.print(bean.getKey() + ":" + bean.getValue() + ", ");
                }
                System.out.println("");
            }
    
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
  • XMLファイル解析

    /*
     * ノードリストを再帰的に解析します。
     * @param nodeList ノードリスト
     */
    public static void scanNodes(NodeList nodeList) {
    
        for (int i=0; i<nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            if (Node.ELEMENT_NODE == node.getNodeType()) {
                NamedNodeMap nodeMap = node.getAttributes();
                if (nodeMap != null) {
                    for (int j=0; j<nodeMap.getLength(); j++) {
                        if (nodeMap.item(j).getNodeName().equals("name")) {
                            String mapKey = nodeMap.item(j).getNodeValue();
                            List<Z_XmlAnalyzerBean> list = new ArrayList<Z_XmlAnalyzerBean>();
                            resultMap.put(mapKey, list);
                        }
    
                        if (nodeMap.item(j).getNodeName().equals("type")) {
                            String mapValueKey = nodeMap.item(j).getNodeName();
                            String mapValueValue = nodeMap.item(j).getNodeValue();
                            System.out.println(mapValueKey + ":" + mapValueValue);
    
                            Node parentNode = node.getParentNode();
                            NamedNodeMap parentNodeMap = parentNode.getAttributes();
                            String parentZokuseiName = parentNodeMap.item(0).getNodeName();
                            String parentZokuseiValue = parentNodeMap.item(0).getNodeValue();
                            System.out.println("親:" + parentZokuseiName + "," + parentZokuseiValue);
    
                            List<Z_XmlAnalyzerBean> list = resultMap.get(parentZokuseiValue);
                            Z_XmlAnalyzerBean bean = new Z_XmlAnalyzerBean();
                            bean.setKey(mapValueKey);
                            bean.setValue(mapValueValue);
                            list.add(bean);
                        }
                    }
                }
            }
    
            if (node.hasChildNodes()) {
                scanNodes(node.getChildNodes());
            }
        }
    }
    
  • XML要素格納Bean

    public class Z_XmlAnalyzerBean {
    
        private String key = null;
        private String value = null;
    
        public String getKey() {
            return key;
        }
        public void setKey(String key) {
            this.key = key;
        }
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
    }
    

【Java】Javaからシェル呼び出し

JavaからWindowsのコマンドプロンプトを呼び出して、
ファイルコピーは実施したことありましたが、
現場の実作業でLinuxのシェルを呼び出して欲しいと言われました。
普通はナイナイと話していたのに実際にあるとは驚きました。

自宅で試してみました。

  • 呼ばれる側

    cat /home/ユーザ名/java/シェル名.sh
    #!/bin/sh
    echo "Hellow World !!"
    cd aaaa
    

    標準出力とエラー出力の両方があった場合を試したかったので、
    わざと存在しないディレクトリに移動しています。
    実行権限をつけておきます。

    chmod -x シェル名
    
  • 呼び出し側

    /**
     * メイン処理
     * @param args 引数
     */
    public static void main(String[] args) {
    
        ArrayList<String> cmd = new ArrayList<String>();
        cmd.add("/home/ユーザ名/java/シェル名.sh");
    
        try {
                ProcessBuilder processBuilder = new ProcessBuilder(cmd);
                processBuilder.redirectErrorStream(true);
                Process process = processBuilder.start();
                InputStream inputStream = process.getInputStream();
                printInputStream(inputStream);
                System.out.println(process.waitFor());
        } catch (Exception e) {
                e.printStackTrace();
        }
    }
    
    /**
     * 入力ストリームの内容を出力する。
     * @param inputStream 入力ストリーム
     * @throws IOException [入出力エラーが発生した場合]
     */
    public static void printInputStream(InputStream inputStream) throws IOException {
            try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));) {
                for (;;) {
                    String line = bufferedReader.readLine();
                    if (line == null) {
                        break;
                }
                    System.out.println(line);
            }
        }
    }
    
  • 実行結果

    javac クラス名.java
    java クラス名
    
    Hellow World !!
    /home/ユーザ名/java/シェル名.sh: line 3: cd: aaa: そのようなファイルやディレクトリはありません
    1
    

    ProcessBuilderクラスのredirectErrorStreamをtrueにしたので、
    標準出力とエラー出力が同時に出力されています。
    ただしこのバッファは1024バイトまでらしいです。
    ⇒echoで1050バイトほど出力するようなシェルにしてみましたが、
     出力できてしまいました。謎です。

    また、異常終了したので戻り値も「1」になっています。

  • 恥ずかしながらはまったこと
    シェル名.shをWindowsで作成していたため、改行コードがCRLFのままでした。
    改行コードがCRLFのままシェルを実行すると、下記エラーが出力されます。

    -bash: ./シェル名.sh: /bin/bash^M: bad interpreter: そのようなファイルやディレクトリはありません
    

    「^M」が出ているのがわかります。
    sedコマンドでLFに置換しました。

    sed -i 's/r//' /home/ユーザ名/java/シェル名.sh
    

【CentOS】初期設定

  • パッケージ最新化
    最初にyumで利用可能なパッケージを最新化しておきます。

    yum -y update
    
    Loaded plugins: fastestmirror, security
    Determining fastest mirrors
    Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=6&arch=x86_64&repo=os error was
    14: PYCURL ERROR 6 - "Couldn't resolve host 'mirrorlist.centos.org'"
    Error: Cannot find a valid baseurl for repo: base
    

    DNS1がないためエラーになってました。名前解決できなかった模様。
    DNS1を設定し再度実行すると成功しました。

    最小構成でインストールした分を最新化。

    yum -y groupinstall "Base" "Development tools" "Japanese Support"
    
  • cron導入
    次にcronをインストールし、起動しておきます。

    yum -y install yum-cron
    /etc/rc.d/init.d/yum-cron start
    

    さらにchkconfigでサーバ起動時に自動起動できるように登録しておきます。

    chkconfig yum-cron on
    chkconfig --list yum-cron
    
    yum-cron        0:off   1:off   2:on    3:on    4:on    5:on    6:off
    

    数字のメモ。

    0:システム停止モード
    1:セーフモード
    2:マルチユーザーモード(NFSマウントなし)
    3:X Windows以外のデーモンのほとんどを起動する動作モード
    4:未使用
    5:X Windowsのデーモンも起動する動作モード
    6:システム再起動モード
    

    「/etc/rc.d」を確認すると下記構成。
    init.d ディレクトリ:各機能の起動ファイルあり
    rc[0-6].d ディレクトリ:ランレベルに応じた起動ファイルあり
    rc.local ファイル:起動時に実施したい外部ファイルを指定
    rc ファイル:中身確認してません
    rc.sysinit ファイル:中身確認してません

  • ホストネーム変更
    ホストネームを変更しておきます。

    vi /etc/hosts
    
    下記内容に変更
    localhost.localdomain
    ↓
    任意のホスト名
    
    vi /etc/sysconfig/network
    
    下記内容を追加
    HOSTNAME=任意のホスト名
    

    私の環境ではインストール時にホスト名を入力したので、自動設定されたようです。

  • 文字コード
    文字コードがUTF-8になっていることを確認します。

    cat /etc/sysconfig/i18n
    
    LANG="ja_JP.UTF-8"
    
  • nkfの導入
    nkfで文字コードや改行コードの確認や変換で便利なので導入します。

    yum -y install nkf
    

    インストール後に確認を行います。

    nkf -g /var/log/secure
    
    ASCII (LF)
    

再起動、シャットダウンはRaspberry Piと同じコマンドでした。

【CentOS】有線LAN設定

家庭の事情で無線LANになりそうですが、初期設定を行うまでは
ひとまず有線LANで繋ぐので、有線LANの設定を行います。
NICがちゃんと認識されているか確認。

ifconfig
lo 略
eth0 略
wlan0 略

eth0が有線用、wlan0が無線用。
eth0の設定を行います。

cd /etc/sysconfig/network-scripts
cp ifcfg-eth0 ifcfg-eth0.org
vi ifcfg-eth0
下記内容に変更
DEVICE=eth0
HWADDR=初期設定
TYPE=Ethernet
UUID=初期設定
ONBOOT=yes
NM_CONTROLLED=yes
BOOTPROTO=static
NETWORK=192.168.XXX.0
NETMASK=255.255.255.0
GATEWAY=192.168.XXX.1
IPADDR=192.168.XXX.AAA
BROADCAST=192.168.XXX.255
DNS1=192.168.XXX.1

私の環境ではDNS1の設定がないと動きませんでした。
上記設定を行い、ネットワーク再起動します。

/etc/rc.d/init.d/network restart

これで他の端末からTeraTermで接続できます。

CentOSのネットワークの設定ファイルについてメモ。

  • /etc/sysconfig/network
    共通のネットワーク設定ファイル。
  • /etc/sysconfig/network-scripts/ifcfg-eth[0-9]
    個別のネットワーク設定ファイル。共通のネットワーク設定内容を継承している。
  • /etc/resolv.conf
    DNSサーバのIPを記述するファイル。
    DNS1の設定を行い、ネットワークを再起動すると自動で設定される。
  • /etc/hosts
    IPアドレスとホスト名をマッピングするファイル。
    DNSを参照する前にこっちを参照する。
  • /etc/host.conf
    「/etc/hosts」やDNSのどれから参照するかの順番を定義するファイル。

【CentOS】NUC(DN2820FYKH)にCentOS6.5導入

USBの準備ができたので、CentOS 6.5をインストールします。
起動後にログが出たらF2を押下し、
BIOSのBootOrderのAdvancedを選択します。
UFFIのチェックを外し、LegacyのUSBを選択してEnterを押下すると、
インストールが始まります。

  • 言語
    Japaneseを選択します。
    テキストモードでは日本語表示できないから、英語で続けるとか注意書きが出ますが、
    OKを選択して進めます。
  • キーボード
    jp106を選択します。
  • InstallationMethod
    USBなのでHard driveを選択します。
  • Select Partition
    どのディスクパーティションからインストールするかを選択します。
    下記のような選択肢がありましたが、「/dev/sdb1」を選択。

    /dev/sda1
    /dev/sdb1
    Direcory holding image:ブランク
    

    1つ目がHDD、2つ目がUSBかな。
    ここで1つ目がUSBになっている場合、かつ、
    以降のインストールもテキストモードでインストールする場合、
    インストーラーがブートローダーを自動的に設定するので、
    ブートローダーがUSBに作られてしまうことがあるらしいです。

    私の環境では以降のインストールがグラフィックモードで、
    ブートローダーを選択できたため、問題は発生しませんでした。

グラフィックモードでインストール中は基本デフォルト設定で
変更したのは下記の通り。

  • タイムゾーンの設定
    「システムクロックでUTCを使用」はチェックを外します。
  • CentOSのインストールタイプ
    Minimalと今すぐカスタマイズを選択し、下記の3つをチェックします。
    言語/日本語のサポート
    開発/開発ツール
    ベースシステム/ベース

これでCentOS 6.5がインストール完了です。

【CentOS】NUC(DN2820FYKH)にCentOS6.5導入準備

  • CentOS 6.5の選択
    DN2820FYKHでは、CentOS 7.0のインストール時の動作が怪しいらしく、
    先人が実施済のCentOS 6.5を下記の公式サイトから入手し、インストールします。

    http://wiki.centos.org/Download

    i386は32bit版、x86_64版は64bit版なので、x86_64を選択。
    ミラーサイトがいくつかあるので適当に。
    種類がいくつかあって、「0_README.txt」を読むと違いが確認できます。

    ファイル名用途概要
    CentOS-6.5-x86_64-LiveCD.isoインストールせずにCDから起動用(機能少)
    CentOS-6.5-x86_64-LiveDVD.isoインストールせずにCDから起動用(機能多)
    CentOS-6.5-x86_64-bin-DVD1.isoインストール用(機能多)
    CentOS-6.5-x86_64-bin-DVD2.isoインストール用(追加)DVD1のみでも可
    CentOS-6.5-x86_64-minimal.isoインストール用(機能最少)
    CentOS-6.5-x86_64-netinstall.isoインストール用(ネットワーク)

    「torrent」ファイルはネットワーク共有ソフトを使ってインストールする際に利用、
    「sum」ファイルは正しくダウンロードされたかのチェックに利用なので、無視。
    「CentOS-6.5-x86_64-bin-DVD1.iso」を選択してみました。

  • LiveUSB Creator
    DN2820FYKHにCentOSを導入するにあたり、
    USB光学ドライブあると思い込んでいたら、
    自宅にあったのはUSBフロッピーディスクドライブでした。

    新規にUSB光学ドライブ購入するのも嫌。
    とはいえ、フロッピーじゃどうにもならないので、グーグル先生に聞いてみたところ、
    USBからOSがインストールできると。
    microSDカード8GにCentOSのイメージをツールで焼いて、
    USB接続のSDカードリーダにmicroSDカードをセットして利用しました。

    ツールは「liveusb-creator-3.12.0-setup.exe」を利用しました。
    USBが接続されているドライブと焼き付けるファイルを選択するだけ。

    しかし、ここに罠があってこれで作成したUSBを使ってインストールしても
    最後の最後で下記エラーで失敗します。
    「Unable to read package metadata」

  • Unable to read package metadata 解決策
    原因は一部ファイルが正しいファイル名にならなかったから。

    USBの中を確認すると「repodata」ディレクトリがあり、その中のファイル名を修正します。
    修正対象は拡張子がない8ファイル、「repomd.xml」には修正前のファイル名と修正後のファイル名が
    ちゃんとかかれているので、それを見ながら修正します。

    修正後はgzファイルやxmlファイルになるはず。
    「TRANS.TBL」はゴミファイルなので削除。
    再度インストールを行います。

【CentOS】NUC(DN2820FYKH)入手

  • NUCのDN2820FYKH入手
    Corei3のD34010WYKHと悩みましたが、お財布都合などいろいろありDN2820FYKHにしました。
    一緒にCFD ElixirのDDL3Lの4Gメモリを購入。
    2.5インチSSDはメインPCで全く使ってなかったものを利用。
    HDMI接続なのでケーブル類も不要。
    約2万円で収まったのがありがたいです。

  • メモリとSSD装着
    裏蓋のネジは裏蓋にくっついてるタイプで
    ドライバーで回しまくってもとれないタイプでした。
    開けると中蓋があってそこに2.5インチSSDを入れるようです。
    中蓋も本体にケーブルでくっついてるので、引っ張りすぎ注意。
    中蓋をあけるとメモリとNICをつけられる場所がありました。

    NICは交換する気はないので、メモリだけ装着。

  • BIOS更新
    OSはLinuxを入れるつもりですが、DN2820FYKHにLinuxを入れる場合、
    BIOSの更新が必須なようで、まずはBIOSの更新です。

    手順は公式の「F7 BIOS Flash アップデート」で行いました。
    http://www.intel.com/jp/support/motherboards/desktop/sb/cs-034499.htm

    奇跡的に残っていた128MBのUSBメモリを利用。
    容量少ないのでフォーマットも一瞬でした。
    最新BIOSの「FY0039.BIO」をダウンロードしてコピー。

    NUCにHDMI、キーボード、USBだけをつけて起動。
    ロゴが出たらF7押下して、BIOS画面に入りました。
    BIOS画面が無駄に格好良い。

    無事に更新完了。次はOSです。
    OSはCentOS 6.5を入れる予定。

【Eclipse】Eclipse再設定

Eclipseで色々いじりすぎて復元できなくなった場合の再設定メモ。

EclipseはIndigoの3.7.2を利用しています。

  • 壊れたEclipse
    【モジュール】
    EclipseのworkplaceディレクトリからSVN管理されていないプロジェクトをコピーしてどこかに保存する。

    【設定】
    「ファイル」-「エクスポート」を選択する。
    「一般」-「設定」を選択し、「次へ」ボタンを押下する。
    全てをエクスポートにチェックを入れ、「宛先設定ファイル」を指定し、「完了」ボタンを押下する。

  • 新しいEclipse
    【モジュール】
    どこかにコピーしておいたプロジェクトを新しいworkplaceに配置。
    「ファイル」-「インポート」を選択する。
    「一般」-「既存プロジェクトをワークスペースへ」を選択し、「次へ」ボタンを押下する。
    ルート・ディレクトリの選択にチェックを入れ、プロジェクトを選択し、「完了」ボタンを押下する。
    SVN管理されているプロジェクトはチェックアウトする。

    【設定】
    「ファイル」-「インポート」を選択する。
    「一般」-「設定」を選択し、「次へ」ボタンを押下する。
    ソース設定ファイルを選択し、「完了」ボタンを押下する。

【Oracle】SQLPlusで空白除去

OracleのSQLPlusでSELECT結果をspoolでファイル出力した際、
CHAR型やVARCHAR2型の出力結果がサイズにあうように自動で空白埋めされてしまい、
想定結果ととても比較し難い。

「SET TRIMSPOOL ON」にしても空白が削除されないのは、
これが「行末のトリム処理を制御する」からのようです。
「項目ごとのトリム処理を制御する」なら便利だったのに。

色々調べてもSQLPlusの設定だけでやるのは無理なようで、
結局下記のようにパイプでつなげることにしました。

SELECT 項目1 || 't' || 項目2 || 't' || 項目3 FROM テーブル名;

TeraTeramで自動ログイン

TeraTermで接続情報を毎回手打ちして、
ログインするのが面倒なので、自動ログインします。

デスクトップでも好きな場所に「ファイル名.ttl」ファイルを作成します。
ファイルが「ttpmacro.exe」に関連付けられていない場合、
TeraTermのインストールディレクトリに存在する「ttpmacro.exe」に関連付けを行います。

ファイルの中身を記載します。

hostname = 'IPアドレスまたはホスト名'
username = 'ユーザ名'
passfile = 'D:/任意のディレクトリ/任意のファイル名'
keyfile = 'D:/任意のディレクトリ/秘密鍵のファイル名'

getpassword passfile username userpass

command = hostname
strconcat command ':22 /ssh /KR=UTF-8 /KT=UTF-8 /auth=publickey /user='
strconcat command username
strconcat command ' /passwd='
strconcat command userpass
strconcat command ' /keyfile='
strconcat command keyfile

connect command

end

初回ログインで1度パスワードを聞かれますが、
2回目からはファイルを自動参照します。