まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

タグ:apache

この記事の続きです:


IBM LinuxONE コミュニティクラウド上に作った仮想サーバーにいわゆる "LAMP"(=Linux + Apache HTTPD + MySQL + PHP) 環境を構築してみます。まずは上記記事を参考に仮想サーバーを作り、SSH でリモートログインします:
2017010403


ミドルウェアの導入作業を伴うため、ルート権限を持ったユーザーにスイッチしておきます:
$ sudo /bin/bash
#


LAMP 環境に必要なミドルウェアや言語環境をまとめて導入します(赤字はコメント):
# yum install httpd -y (Apache HTTP サーバー)
# yum install mysql-server mysql -y (MySQL)
# yum install php php-mbstring php-mysql php-gd php-pear php-xml php-devel -y (PHP)

また以下は LAMP 環境構築においては必須ではありませんが、使うことも多いというか、あると便利だと思うので必要に応じて導入しておいてください:
# yum install screen -y (screen)
# yum install git -y (git)
# yum install java-1.8.0-ibm-devel -y (JDK 1.8)

ミドルウェアを起動する前にファイアウォールの設定を行います。デフォルトの LinuxONE では iptables によるファイアウォールが有効になっており、このままでは http(s) によるアクセスができません。今回の環境では iptables を無効にしておきましょう:
# /etc/init.d/iptables stop
# chkconfig iptables off

あらためて各ミドルウェアを起動し、また自動起動設定をしておきます:
# /etc/init.d/httpd start
# /etc/init.d/httpd mysqld
# chkconfig httpd on
# chkconfig mysqld on
# exit
$

この時点で Apache HTTP サーバーが動いています。iptables の解除が成功していれば http://(IPアドレス)/ にアクセスすることができるようになっているはずです:
2017010601


さて、MySQL に関しては root のパスワードを設定しておきましょう。この例では P@ssw0rd というパスワードにしていますが、ここは必要に応じて変えてください:
$ mysql -u root

mysql> set password for root@localhost=PASSWORD('P@ssw0rd');
mysql> exit

これで LinuxONE 上でも LAMP の環境が作れました! ちなみに PHP のバージョンは 5.3.3 が導入できます:
$ php -v
PHP 5.3.3 (cli) (built: Dec 15 2015 04:50:47)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies

Apache Derby(或いは "Cloudscape")という RDB をご存知でしょうか?
derby-logo-web


最近は SQLite や HTML5 のローカルデータストアの台頭であまり名前を聞かなくなりましたが、Pure Java で記述された軽量の RDB です。元々は Cloudscape Inc. によって開発されましたが、Informix Software を経て IBM 製品として扱われていた時代もあります。その影響もあってか "DB2 互換 SQL" に対応し、DB2 の SQL が動く軽量の Java RDB という立ち位置でした。軽量であるが故に組み込み系のアプリケーション内で使われることが多いようです。歴史的には 2004 年に IBM から Apache 財団へソースコードが寄贈され、現在の Apache Derby という名称のプロジェクトになりました。また Oracle JDK 1.6 以降に(オプションとして)組み込まれている JavaDB の実装はこの Apache Derby です。

私自身は "Cloudscape" と呼ばれていた頃に使ったことがありました。今回、久しぶりに Apache Derby を使ってみました。

JDK のオプションに組み込まれているとはいえ、せっかくなので最新版を使ってみることにしました。まずは Apache Derby のダウンロードページから最新バージョン(2016/Oct/07 時点では 10.12.1.1)のリンクをクリックします:
2016100601


最新版のアーカイブファイル:db-derby-(バージョン番号)-bin.zip をクリックしてダウンロードします:
2016100602


ダウンロードした zip ファイルを展開し、lib フォルダ内の必要な JAR ファイルを取り出します。今回は本体である derby.jar と、日本語ロケールが含まれた derbyLocale_ja_JP.jar の2ファイルを取り出します:
2016100603


この2ファイルを Java の開発環境から使えるようにします。J2SE/EE プロジェクトであれば、WebContent/WEB-INF/lib 以下にコピーするなどして、コンパイル/実行時に参照できるようにしておきます:
2016100604


試しに以下のような index.jsp ファイルを用意してみました:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>
<%@ page import="java.sql.*" %>
<%@ page import="me.juge.derby.*" %>
<%
  request.setCharacterEncoding("utf-8");
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>Derby JDBC Sample</title>
</head>
<body>

<table border="1">
<tr><th>ID</th><th>NAME</th><th>PRICE</th></tr>
<%
final String driverName = org.apache.derby.jdbc.EmbeddedDriver.class.getCanonicalName();
final String dbName = "derbydb";
final String connURL = "jdbc:derby:" + dbName + ";create=true";  //. DBが存在していない場合は作成するオプション

try{
  Class.forName( driverName );
  Connection conn = null;

  try{
    conn = DriverManager.getConnection( connURL );
  }catch( Exception e ){
  }

  if( conn != null ){
    ResultSet rs = null;
    //. 初期化
    try{
      //. 試しに items テーブルにアクセス
      Statement s0 = conn.createStatement();
      rs = s0.executeQuery( "select count(*) from items" );
    }catch( SQLException e ){
      //e.printStackTrace();

      String state = e.getSQLState();
      if( state.equals( "42X05" ) ){
        //. テーブルが存在しない
        try{
          Statement s1 = conn.createStatement();
          s1.execute( "create table items("
            + " id int generated always as identity primary key"  //. この辺りが DB2 互換 SQL
            + ", name varchar(100)"
            + ", price int"
            + " )");

          PreparedStatement s2 = conn.prepareStatement( "insert into items( name, price ) values( ?, ? )" );
          s2.setString( 1, "シャンプー" );
          s2.setInt( 2, 1000 );
          s2.execute();

          PreparedStatement s3 = conn.prepareStatement( "insert into items( name, price ) values( ?, ? )" );
          s3.setString( 1, "石鹸" );
          s3.setInt( 2, 500 );
          s3.execute();
        }catch( Exception e1 ){
        }
      }else if( state.equals( "42X14" ) || state.equals( "42821" ) ){
        //. テーブル定義が不正

      }else{
        //. その他の想定外の例外

      }
    }finally{
      if( rs != null ) rs.close();
    }

    //. レコード表示
    try{
      rs = conn.createStatement().executeQuery( "select * from items" );
      while( rs.next() ){
        int id = rs.getInt( 1 );
        String name = rs.getString( 2 );
        int price = rs.getInt( 3 );
%>
<tr><td><%= id %></td><td><%= name %></td><td><%= price %></td></tr>
<%
      }
    }catch( SQLException e ){
    }finally{
      if( rs != null ) rs.close();
    }
  }
}catch( Exception e ){
  e.printStackTrace();
}
 %>
</table>
</body>
</html>

コードそのものは一般的な JDBC プログラミングです。DB を開く際に目的の DB が存在していない場合はその場で作成するようなオプション("create=true")を付与しています。また items というテーブルのレコード数を取得して、エラーが出るようであればテーブルが存在していないと判断し、新規に items テーブルを定義して作成し、2つほどデータを insert するようにしています。 最終的には items テーブル内の全レコードを画面に出力する、という内容にしています。


このプロジェクトを実行して、index.jsp にブラウザでアクセスすると以下のような表形式で2つのレコード内容が確認できます:
2016100701

まあ普通に RDB として使うこともできますが、組み込み系以外であればフットプリントの小ささからローカルレプリカ DB として利用する、というケースも考えられます。特にサーバー側が DB2 だったりすると SQL 互換が便利に使えたりしますね。

久しぶりに Cloudscape を使ってみました。相変わらずですが( Java が動いている前提はありますが)JAR ファイル置くだけで使える RDB は便利ですね。



今まで Tomcat を使う場合は使い慣れた(理由はそれだけじゃないけど) Tomcat6 を使っていました。試しに Tomcat7 を導入する機会があったので、その導入手順を紹介します。環境はいつもの CentOS 6 です。


まずは Java/JDK 環境が必要です。今回は OpenJava JDK 1.7 を導入することにしました:
# yum install java-1.7.0-openjdk-devel

さて Tomcat7 です。実は CentOS 6 の標準リポジトリには Tomcat7 は含まれていません。標準で含まれているのは Tomcat6 ですが、yum でインストールする場合は
# yum install tomcat6

のように、わざわざ「6」を指定して導入する必要があります。この辺りの導入手順の詳細はこちらを参照してください:
CentOS に Apache Tomcat を導入する

さて Tomcat7 です。Tomcat6 は「6」を指定する必要がありました。Tomcat7 も「7」を指定する必要があるかというと、これがありません(笑)。コマンドとしては
# yum install tomcat

で導入されるのですが、普通にそのまま実行しても「見つからない」というエラーになるだけだと思います。Tomcat7 を yum でインストールするには事前に EPEL リポジトリの登録が必要です。つまり、まずはこのコマンドを実行します:
# rpm -Uvh http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm

その後に yum で install です。tomcat-admin-webapps パッケージを指定して、管理コンソール画面ごとインストールします:
# yum install tomcat tomcat-admin-webapps

管理コンソール画面にアクセスするための設定を追加します。/etc/tomcat/tomcat-users.xml をテキストエディタで編集し、以下の3行を追加します:
<role rolename="admin-gui"/>
<role rolename="manager-gui"/>
<user username="admin" password="P@ssw0rd" roles="admin-gui,manager-gui"/>
   ↑この例だとユーザー名: admin、パスワード: P@ssw0rd でアクセスするユーザーを作成


インストール後は "tomcat" というサービス名で起動したり、再起動したり、自動実行指定ができます:
# /etc/init.d/tomcat start
# chkconfig tomcat on

管理コンソール画面ごとインストールした場合は、http://(IPアドレス:8080)/manager/html で管理画面にアクセスできます。Basic 認証でユーザー名とパスワード(上記設定の場合であれば admin と P@ssw0rd)を指定してログインできます:
2015122801


CentOS6 で Tomcat7 を使う場合は EPEL リポジトリの登録、が肝です。


話題の分散演算環境である Apache Spark を1台の CentOS 6 に導入する手順を紹介します。今回紹介する内容はプレビルド版を使った方法なので、おそらく CentOS の他バージョンや、RHEL, Ubuntu といった他の Linux ディストリビューションでもほぼ同様の方法で導入できると思います。また分散環境で Spark をインストールするのが本来の使い方だと理解した上で、ここではシェルなどを自由に気軽に試せる環境を作りたいので、1台の CentOS を使う、という方法を紹介します。


Apache Spark は Java VM 上で動きます。なので前提として、Java のランタイム環境が必要です。 まだ導入されていない場合はあらかじめ Java を導入しておいてください。CentOS で OpenJDK 1.7.0 を使う場合であれば、このコマンドで導入できます:
# yum install java-1.7.0-openjdk

Java の導入までできてしまえば後はどの Linux ディストリビューションでも動くと思います。次に Apache Spark 本体をダウンロードします。このブログエントリを書いている時点での最新版は 1.5.2 なので、このバージョンのビルド済みバイナリを入手して、/opt/spark 以下に展開します:
# cd /tmp
# wget https://archive.apache.org/dist/spark/spark-1.5.2/spark-1.5.2-bin-cdh4.tgz
# cd /opt
# tar xzvf /tmp/spark-1.5.2-bin-cdh4.tgz
# mv spark-1.5.2-bin-cdh4 spark

これでインストール完了です。動作確認の意味で Spark-Shell と呼ばれるシェルインターフェースを起動してみましょう:
# cd /opt/spark/bin
# ./spark-shell
2015111001


"Spark" と書かれたそれっぽい画面がバージョン名とともに表示され、各種ライブラリのロードが行われます(Spark context が sc という変数で使える、というメッセージも表示されています)。Spark-Shell のロードが完了すると、 "scala>" というプロンプトが表示されます:

2015111002


では Spark の機能を使って Spark の README.md ファイルの文字数を カウントしてみます。README.md ファイルは Spark ディレクトリ(今回の例では /opt/spark/)の直下にあるので、sc 変数を使って以下のようにプロンプトに入力します。
scala> sc.textFile( "/opt/spark/README.md" ).count()

すると Spark の機能で指定したファイルが読み込まれ、その文字数(以下の例では 98)がカウントされて表示されます:

2015111003


正しく動作していることが確認できました。exit と入力すると、Spark-Shell を終了します。

IBM Bluemix でも Apache Spark を使う機会があると思うのですが、自分の手元に1個インスタンスがあると色々試せて便利ですよね。この後もちょっとした応用的なブログを書くつもりなので、それらはこのインスタンスを使って書いていこうと思ってます。


ウェブサービスを開発・公開する上で、URL のリライトの問題があります。

例えば、アプリケーションとしては
 http://xxx.myserver.com/abc.php?country=Japan&pref=Chiba&city=Funabashi
というパラメータを受け取って処理するようなページやサービスがあったとします。

単に動かすだけなら、このパラメータ処理でいいのですが、SEO 対策や、単に長くて見難いという理由から、これを
 http://xxx.myserver.com/abc.php/Japan/Chiba/Funabashi
のような URL パターンで受け取りたいことが出てきます。

この例であれば、
 http://xxx.myserver.com/abc.php/AAA/BBB/CCC
という URL パターンを有効な URL として認識し、実際に処理する際には
 http://xxx.myserver.com/abc.php?country=AAA&pref=BBB&city=CCC
と内部的に書き直し(ReWrite)してから処理をする、ということになります。これが URL リライトです。

HTTP サーバーに Apache HTTPD を使っている場合は、この URL リライト機能が標準モジュールになっていて、mod_rewrite モジュールを有効にして、httpd.conf や .htaccess に変換ルールを記述するだけで URL リライトが実現できます。 ではウェブサーバーに Apache Tomcat など Apache HTTPD を経由しない Java アプリケーションサーバーを使っている場合のリライト処理はどうすればいいでしょう??


その答の1つが UrlRewriteFilter です。jar ファイル1つをプロジェクトに組み込み、専用の XML ファイルに変換ルールを記述することで、Java アプリケーションサーバーでも mod_rewrite のようなリライト処理を実現できるようになります:
urlrewritefilter00


実際に使ってみたサンプルを紹介します。まずは公式ページから urlrewritefilter-4.0.3.jar ファイルと、サンプルの urlrewrite.xml ファイルをダウンロードして保存します。
urlrewritefilter01


Java アプリケーションのプロジェクト内にこれらのファイルを配置します。まず urlrewritefilter-4.0.3.jar は WEB-INF/lib/ に、urlrewrite.xml は WEB-INF にそれぞれ保存します。次に web.xml を編集します:
urlrewritefilter02

WEB-INF/web.xml の </web-app> の直前に次の内容を追加して、このウェブアプリケーション内の全ての URL パターン(/*)に UrlRewriteFilter フィルターを適用することを宣言します:
  :
  :
<filter>
  <filter-name>UrlRewriteFilter</filter-name>
  <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>UrlRewriteFilter</filter-name>
  <url-pattern>/*</url-pattern>
  <dispatcher>REQUEST</dispatcher>
  <dispatcher>FORWARD</dispatcher>
</filter-mapping>

</webapp>

実際の変換パターンは WEB-INF/urlrewrite.xml ファイルの <urlrewrite> 要素内に記述します。以下の例では2つの変換パターンを記載しています:
<urlrewrite>
  :
  :
  <rule>
    <from>/urlrewrite/(.*)/(.*)/(.*)</from>
    <to>/urlrewrite?x=$1&y=$2&z=$3</to>
  </rule>
  <rule>
    <from>/urlrewrite/(.*)/(.*)</from>
    <to>/urlrewrite?a=$1&b=$2</to>
  </rule>
</urlrewrite>

前者では /urlrewrite/ 以下に / をセパレータとして3つのパラメータを受け取る、つまり
 /urlrewrite/XXX/YYY/ZZZ 
のような URL 指定があった場合に、内部的に
 /urlrewrite?x=XXX&y=YYY&z=ZZZ
のようにリライトして処理する、という変換パターンを記載しています。

後者では同様に2つのパラメータを受け取った時、つまり
 /urlrewrite/AAA/BBB
のような URL 指定があった場合に、内部的に 
 /urlrewrite?a=AAA&b=BBB
のようにリライトして処理する、という変換パターンを記載しています。

#上記例ではパラメータが2つの時と3つの時とで、異なるパラメータ変数に渡すようにしています。
 もちろんこの部分は定義次第です。


後はこれらのパラメータ(リライト後のパラメータ)に対応して処理するような /urlrewrite を(サーブレットなどで)実装すればよい、ということになりますね。




 

このページのトップヘ