2014年8月1日金曜日

Pleiades Luna 64bit Java 移行

Kepler から Luna へプロジェクトを移行する。

Pleiades Luna インストール

c:\pleiades を c:\pleiades.old にリネーム

Pleiades Luna 64bit Java Full Edition をダウンロード。
エクスプローラでダウンロードした zip ファイルを選択し、中にある pleiades ディレクトリを c:\ へD&Dして解凍。

STS インストール

「ヘルプ」「Eclipse マーケットプレース」
「検索」で「STS」を検索
「Spring Tool Suite (STS) for Eclipse Luna (4.4) 3.6.0.RELEASE」をインストール

Jacoco プラグインインストール


ADT/m2e-android インストール

「ヘルプ」「新規ソフトウェアのインストール」
「作業対象」
「Maven Eclipse用 Android」と「開発ツール」をインストール。

※開発ツールインストール時に、Android SDK をインストールし直したい場合、
c:\Users\ユーザ名\android-sdks
c:\Users\ユーザ名\.android
を削除しておく。

プロジェクト設定

「ファイル」「インポート」「一般」「既存プロジェクトをワークスペースへ」
「ルートディレクトリの選択」に「C:\pleiades.old\workspace」を指定、
sample、sampleAppプロジェクトを選択。
「プロジェクトをワーススペースにコピー」をチェックして「完了」

sample プロジェクトのプロパティ「プロジェクト・ファセット」を変更
Java 1.8
動的 Web モジュール 3.1

pom.xml を変更。
<java-version>1.8</java-version>
<org.springframework-version>4.0.5.RELEASE</org.springframework-version>
<org.aspectj-version>1.8.0</org.aspectj-version>
<org.slf4j-version>1.7.7</org.slf4j-version>

コンテキストで以下書き方だとエラーが発生するので修正。
root-context.xml
testApplicationContext.xml
<property name="dataSource">
<ref local="dataSource" />
</property>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

「ウィンドウ」「ビューの表示」「その他」「サーバー」で「サーバー」表示、
Tomcat8 サーバーを作成、sample プロジェクトを追加する。

sampleApp プロジェクトのプロパティで「Android」「プロジェクト・ビルド・ターゲット」で
Android 4.4.2 を選択する。

Tomcat サーバーを起動し、Android アプリで kepler 時と同動作をする事を確認。

2014年5月1日木曜日

Android SSL 接続設定

AVD 起動。4.4 の場合 Android 仮想デバイスマネージャーから起動。
4.3 の場合はオプションなしだと systemディレクトリの残量が無いので、パーティションサイズを指定する。
コマンドプロンプト(管理者)で以下を実行。
cd C:\Users\ユーザ名\android-sdks\tools
emulator -avd sample -partition-size 300

 「Setting」「Security」「Screen lock」を「PIN」に変更しておく。

hosts ファイルを準備。
127.0.0.1  localhost
10.0.2.2   www.s6131.jp

コマンドプロンプト(管理者)で以下を実行して hosts 転送。
AVD 起動毎に必要。
cd C:\Users\ユーザ名\android-sdks\platform-tools
adb remount
adb push hosts /system/etc/

Android ブラウザから PKCS#12 ファイルを読み込み。
http://www.s6131.jp/server.p12

インポート完了後、「Setting」「Security」「Trusted credentials」の「USER」一覧に追加されている事を確認。
4.4 の場合 Network may be monitored の警告が出る。
Android ブラウザから警告なしで SSL 要求が接続できる事を確認。
https://www.s6131.jp/

SampleApp Android プロジェクトのリクエスト URL を http から https に変更。

AsyncHttpRequest.java
String url = "https://www.s6131.jp/sample/sampleList?cd=" + searchText;

AsyncLoginRequest.java
URI url = new URI( "https://www.s6131.jp/sample/j_spring_security_check" );

SampleApp をデバッグ実行、問題なく接続できることを確認。

2014年4月30日水曜日

Apache HTTP Server 2.4 64bit SSL 設定

C:\Program Files\Apache Software Foundation\Apache24\conf\httpd.conf を編集
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
Include conf/extra/httpd-ssl.conf 
ServerAdmin admin@s6131.jp
ServerName www.s6131.jp:80

C:\Program Files\Apache Software Foundation\Apache24\conf\extra\httpd-ssl.conf を編集

"C:/Apache24" を "C:/Program Files/Apache Software Foundation/Apache24" に変更
ServerName www.s6131.jp:443
ServerAdmin admin@s6131.jp 
SSLCertificateFile "c:/Program Files/Apache Software Foundation/Apache24/conf/server.crt"
SSLCertificateKeyFile "c:/Program Files/Apache Software Foundation/Apache24/conf/server.key"
SSLCACertificateFile "c:/OpenSSL-Win64/bin/demoCA/cacert.pem"

DNS 無いので C:\Windows\System32\Drivers\etc\hosts を編集
127.0.0.1       www.s6131.jp

C:\OpenSSL-Win64\bin\openssl.cfg をopenssl.cfg.sav にバックアップしてから編集。
[usr_cert]
basicConstraints=CA:FALSE
nsCertType = server
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

秘密鍵(server.key)、公開鍵(server.csr)、証明書(server.crt)、PKCS#12(server.p12) の作成
CSR作成時の common name は www.s6131.jp を指定
cd C:\Program Files\Apache Software Foundation\Apache24\conf
openssl genrsa -out server.key 2048
openssl rsa -in server.key -out server.key
openssl req -new -days 365 -key server.key -out server.csr 
cd C:\OpenSSL-Win64\bin
openssl ca -days 365 -in "/Program Files/Apache Software Foundation/Apache24/conf/server.csr" -keyfile demoCA/private/cakey.pem -cert demoCA/cacert.pem -out "/Program Files/Apache Software Foundation/Apache24/conf/server.crt" 
cd C:\Program Files\Apache Software Foundation\Apache24\conf
openssl pkcs12 -export -in server.crt -inkey server.key -certfile /OpenSSL-Win64/bin/demoCA/cacert.pem -out server.p12 
copy server.p12 ..\htdocs

Apache サービスを再起動
ブラウザで  PKCS#12 ファイルを実行、証明書のインポート。
http://www.s6131.jp/server.p12
デフォルトのまま「次へ」を繰り返し、「完了」
(証明書ストアで「証明書の種類に基づいて、自動的に証明書ストアを選択する」を選択)

ブラウザで警告なしで SSL アクセスできることを確認。(Firefox 除く)

https://www.s6131.jp/


2014年4月28日月曜日

Windows 8.1 64bit OpenSSL インストール 認証局の作成

OpenSSL 64bit v1.0.1g をダウンロード。
http://slproweb.com/products/Win32OpenSSL.html
ダウンロードした Win64OpenSSL-1_0_1g.exe を実行し、インストール。
環境変数 Path に C:\OpenSSL-Win64\bin; を追加。
環境変数 OPENSSL_CONF に C:\OpenSSL-Win64\bin\openssl.cfg を設定。
認証局を作成する為、C:\OpenSSL-Win64\bin\CA.pl を動かしたい。
ActivePerl Community Edition 64bit をダウンロード。
http://www.activestate.com/
ダウンロードした ActivePerl-5.16.3.1604-MSWin32-x64-298023.msi を実行し、インストール。

C:\OpenSSL-Win64\bin\openssl.cfg をopenssl.cfg.sav にバックアップしてから編集。
[ req ]
default_bits = 2048
[usr_cert]
basicConstraints=CA:TRUE
nsCertType = sslCA, emailCA
keyUsage = cRLSign, keyCertSign
コマンドプロンプト(管理者)で以下を実行し、認証局を作成する。
CA.pl での common name は s6131.jp を指定
cd C:\OpenSSL-Win64\bin
perl CA.pl -newca
C:\OpenSSL-Win64\bin\openssl.cfg をバックアップから戻しておく。

2014年4月17日木曜日

Android Spring Security ログイン

ログイン状態(Cookie)を保持する為、HttpClient を Singleton で取得する。
保持している static が初期化された場合、再度ログインが必要。

jp.s6131.sampleApp.SampleHttpClient.java
public class SampleHttpClient {
    private static DefaultHttpClient httpClient = new DefaultHttpClient();
    private SampleHttpClient() {}
    public static DefaultHttpClient getInstance() {
        return httpClient;
    }
}

メイン画面にテキストボックスとボタンを配置、クリックで AsyncHttpRequest 実行、
同クラスから呼び出す setResult メソッドで GridView に結果を表示。

jp.s6131.sampleApp.HelloAndroidActivity.java
public class HelloAndroidActivity extends Activity {
    private static String TAG = "sampleApp";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "onCreate");
        setContentView(R.layout.main);
    }
    public void onClick(View v) {
        EditText searchText = (EditText) findViewById(id.search_text);
        Uri.Builder builder = new Uri.Builder();
        AsyncHttpRequest task = new AsyncHttpRequest(
                this,
                searchText.getText().toString()
                );
        task.execute(builder);
    }
    public void setResult(Code[] resList) {
        if (resList != null && resList.length != 0) {
            List<String> resultList = new ArrayList<String>();
            for (Code res : resList) {
                resultList.add((res.id == null ? "null" : res.id));
                resultList.add((res.code == null ? "null" : res.code));
                resultList.add((res.name == null ? "null" : res.name));
            }
            ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, R.layout.rowdata, resultList);
            GridView gridTable = (GridView) findViewById(id.grid_table);
            gridTable.setAdapter(arrayAdapter);
        }
    }
}

Spring Security 未ログインの場合、LoginActivity へ遷移。

jp.s6131.sampleApp.AsyncHttpRequest.java
public class AsyncHttpRequest extends AsyncTask<Uri.Builder, Void, Code[]> {
    private HelloAndroidActivity mainActivity;
    private String searchText;
    public AsyncHttpRequest(HelloAndroidActivity activity, String searchText) {
        this.mainActivity = activity;
        this.searchText = searchText;
    }
    @Override
    protected Code[] doInBackground(Uri.Builder... builder) {
        RestTemplate template = new RestTemplate();
        template.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        DefaultHttpClient httpClient = SampleHttpClient.getInstance();
        template.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
        String url = "http://10.0.2.2/sample/sampleList?cd=" + searchText;
        try {
            ResponseEntity<Code[]> responseEntity = template.exchange(url, HttpMethod.GET, null, Code[].class);
            Code[] resList = responseEntity.getBody();
            return resList;
        } catch (Exception e) {
            Log.d("Error", e.toString());
        }
        return null;
    }
    @Override
    protected void onPostExecute(Code[] resList) {
        if (resList != null && resList.length != 0) {
            if ("-1".equals(resList[0].getId())) {
                Intent intent = new Intent(mainActivity.getApplicationContext(), LoginActivity.class);
                mainActivity.startActivity(intent);
                return;
            }
        }
        mainActivity.setResult(resList);
    }
}

ログイン画面にユーザー、パスワードのテキストボックスとボタンを配置、クリックで AsyncLoginRequest 実行。

jp.s6131.sampleApp.LoginActivity.java
public class LoginActivity extends Activity {
    private static String TAG = "sampleApp";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "onCreate");
        setContentView(R.layout.login);
    }
    public void onClick(View v) {
        EditText useridText = (EditText) findViewById(id.userid_text);
        EditText passwordText = (EditText) findViewById(id.password_text);
        Uri.Builder builder = new Uri.Builder();
        AsyncLoginRequest task = new AsyncLoginRequest(
                this,
                useridText.getText().toString(),
                passwordText.getText().toString()
                );
        task.execute(builder);
    }
}

とりあえず無条件に HelloAndroidActivity に遷移

jp.s6131.sampleApp.AsyncLoginRequest.java
public class AsyncLoginRequest extends AsyncTask<Uri.Builder, Void, String> {
    private LoginActivity mainActivity;
    String userId;
    String password;
    public AsyncLoginRequest(LoginActivity mainActivity, String userId, String password) {
        this.mainActivity = mainActivity;
        this.userId = userId;
        this.password = password;
    }
    @Override
    protected String doInBackground(Uri.Builder... builder) {
        try {
            URI url = new URI( "http://10.0.2.2/sample/j_spring_security_check" );
            HttpPost request = new HttpPost( url );
            List<NameValuePair> post_params = new ArrayList<NameValuePair>();
            post_params.add(new BasicNameValuePair("j_username", userId));
            post_params.add(new BasicNameValuePair("j_password", password));
            request.setEntity(new UrlEncodedFormEntity(post_params, "UTF-8"));
            DefaultHttpClient httpClient = SampleHttpClient.getInstance();
            HttpResponse response = httpClient.execute(request);
            return null;
        }catch (Exception e){
            Log.d("Error", e.toString());
        }
        return null;
    }
    @Override
    protected void onPostExecute(String result) {
        Intent intent = new Intent(mainActivity.getApplicationContext(), HelloAndroidActivity.class);
        mainActivity.startActivity(intent);
    }
}

メイン画面

res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <EditText
        android:id="@+id/search_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text">
        <requestFocus />
    </EditText>
    <Button
        android:id="@+id/get_json_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="@string/json" />
    <GridView
        android:id="@+id/grid_table"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:numColumns="3" >
    </GridView>
</LinearLayout>

GridView の TextView を別ファイルで定義。

res/layout/rowdata.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
</TextView>

ログイン画面

res/layout/login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/userid"
    />
    <EditText
        android:id="@+id/userid_text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:inputType="textPersonName"
        >
        <requestFocus />
    </EditText>
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/password"
    />
    <EditText
        android:id="@+id/password_text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword"
    />
    <Button
        android:id="@+id/login_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="@string/login"
    />
</LinearLayout>

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="jp.s6131.sampleApp" android:versionCode="1" android:versionName="0.0.1-SNAPSHOT">
  <uses-sdk android:minSdkVersion="11"
            android:targetSdkVersion="19" />
  <uses-permission android:name="android.permission.INTERNET" />
  <application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".HelloAndroidActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <activity android:name=".LoginActivity"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:noHistory="true">
    </activity>
  </application>
</manifest>


人気ブログランキングへ

2014年4月13日日曜日

Spring for Android Json 取得

Spring Json 返却 VB.NET 表示 で作成した
jp.s6131.sample.controller.HomeController.sampleList
を android アプリから呼び出す。

jp.s6131.sampleApp.Code.java
getter/setter 無いと HttpMessageNotReadableException 発生
public class Code {
    String id;
    String code;
    String name;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

jp.s6131.sampleApp.HelloAndroidActivity.java
public class HelloAndroidActivity extends Activity {
    private static String TAG = "sampleApp";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "onCreate");
        Uri.Builder builder = new Uri.Builder();
        AsyncHttpRequest task = new AsyncHttpRequest(this);
        task.execute(builder);
        setContentView(R.layout.main);
    }
}

jp.s6131.sampleApp.AsyncHttpRequest.java
public class AsyncHttpRequest extends AsyncTask<Uri.Builder, Void, String> {
    private Activity mainActivity;
    public AsyncHttpRequest(Activity activity) {
        this.mainActivity = activity;
    }
    @Override
    protected String doInBackground(Uri.Builder... builder) {
        RestTemplate template = new RestTemplate();
        template.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        String url = "http://10.0.2.2/sample/sampleList?cd=cd1";
        try {
            ResponseEntity<Code[]> responseEntity = template.exchange(url, HttpMethod.GET, null, Code[].class);
            Code[] resList = responseEntity.getBody();
            for (Code res : resList) {
                Log.i("sampleApp id = ", (res.id == null ? "null" : res.id));
                Log.i("sampleApp code = ", (res.code == null ? "null" : res.code));
                Log.i("sampleApp name = ", (res.name == null ? "null" : res.name));
            }
            return resList.toString();
        } catch (Exception e) {
            Log.d("Error", e.toString());
        }
        return "";
    }
    @Override
    protected void onPostExecute(String result) {
    }
}

AndroidManifest.xml 追加
<uses-permission android:name="android.permission.INTERNET" />

LogCat ビュー(ログ)の表示
「ウィンドウ」「ビューの表示」「その他」「Android」「LogCat」

仮想デバイスの作成
「ウィンドウ」「Android仮想デバイス・マネージャ」「新規」
装置:Nexus 5
ターゲット:Android 4.4.2 - API Level 19
CPU/ABI:ARM (armeabi-v7a)
メモリー・オプション:RAM:1024
「ホスト GPU を使用する」にチェック
OKで保存、開始ボタンで AVD 開始。
sample プロジェクトサーバー(Tomcat)も起動。

android アプリプロジェクト右クリック、「デバッグ」「Androidアプリケーション」
Conversion to Dalvik format failed: Unable to execute dex: Multiple dex files define Lorg/springframework/core/ErrorCoded
エラーが発生。
Java のビルド・パス、順序およびエクスポートでMaven依存関係からチェックを外すとエラーが消えるが、
実行時に spring for android クラスが not found となってしまう。
pom.xml で exclusion 設定を行う。さらに
java.lang.NoClassDefFoundError: com.fasterxml.jackson.databind.ObjectMapper
が発生するので fasterxml も追加

pom.xml
<dependency>
    <groupId>org.springframework.android</groupId>
    <artifactId>spring-android-rest-template</artifactId>
    <version>1.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.android</groupId>
    <artifactId>spring-android-auth</artifactId>
    <version>1.0.1.RELEASE</version>
    <exclusions>
        <exclusion>
            <artifactId>spring-core</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.android</groupId>
    <artifactId>spring-android-core</artifactId>
    <version>1.0.1.RELEASE</version>
    <exclusions>
        <exclusion>
            <artifactId>spring-core</artifactId>
            <groupId>org.springframework</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.2.3</version>
</dependency>

LogCat ビュー で結果を確認。


人気ブログランキングへ

2014年4月10日木曜日

Eclipse Android プラグイン Spring for Android インストール

Eclipse Android プラグイン インストール


Eclipse「ヘルプ」「Eclipse マーケットプレース」
android で検索
Eclipse Android 開発ツール
をインストール。
インストール途中で SDK もインストールできる。
SDK マネージャで HAXM もインストールする。

Eclipse に SDK マネージャを表示させる


「ウィンドウ」「パースペクティブのカスタマイズ」「コマンド・グループ可用性」
「Android SDK および AVD マネージャー」にチェック。

Android Configurator for M2Eのインストール


Eclipse「ヘルプ」「Eclipse マーケットプレース」
m2e で検索
Android Configurator for M2E
をインストール。

Android Maven プロジェクトの作成


「ファイル」「新規プロジェクト」「MavenMaven プロジェクト」「アーキタイプの選択」
android で検索
「android-quickstart」
を選択。

project.properties の編集


target=android-16 を
target=android-19 に変更

Spring for Android インストール


pom.xml
<dependency>
    <groupId>org.springframework.android</groupId>
    <artifactId>spring-android-rest-template</artifactId>
    <version>1.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.android</groupId>
    <artifactId>spring-android-auth</artifactId>
    <version>1.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.android</groupId>
    <artifactId>spring-android-core</artifactId>
    <version>1.0.1.RELEASE</version>
</dependency>


人気ブログランキングへ

2014年4月1日火曜日

VB.NET Spring Security ログイン


  • VB.NET でクッキーを保持
  • Spring ログインリクエストでJSONを返却
  • VB.NET 画面からログインPOST


SavedRequest が取得出来ない為、ログイン後リダイレクトが不可。
ログイン後レスポンスを変更する場合は SavedRequestAwareAuthenticationSuccessHandler で指定

Form1.vb
Public cookieData As CookieContainer = New CookieContainer
Private Sub search_proc(cd As String)
    Dim url As String = "http://localhost/sample/sampleList?cd=" & cd
    Dim req As HttpWebRequest = HttpWebRequest.Create(url)
    req.CookieContainer = cookieData
    Dim res As HttpWebResponse = req.GetResponse()
    Dim reader As StreamReader = New StreamReader(res.GetResponseStream())
    Dim jsonString As String = reader.ReadToEnd()
    Dim jsonList As List(Of JToken) = JArray.Parse(jsonString).Children.ToList
    If (jsonList.Count <> 0) Then
        Dim id As String = jsonList(0).Item("id")
        If (id = "-1") Then
            Form2.ShowDialog()
            Exit Sub
        End If
    End If
    For Each itemClass As JObject In jsonList
        itemClass.CreateReader()
        DataGridView1.Rows.Add(itemClass("id"), itemClass("code"), itemClass("name"))
    Next
    res.Close()
End Sub

Form2 に以下コントロールを配置
  • TextBox1 : ユーザ名
  • TextBox2 : パスワード
  • Button1 : ログインボタン

Form2.vb
Imports System.Net
Imports System.IO
Imports System.Text
Public Class Form2
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim ht As Hashtable = New Hashtable
        ht.Add("j_username", TextBox1.Text)
        ht.Add("j_password", TextBox2.Text)
        Dim param As String = ""
        For Each key As String In ht.Keys
            param = param & String.Format("&{0}={1}", key, ht(key))
        Next
        Dim paramBytes As Byte() = System.Text.Encoding.ASCII.GetBytes(param)
        Dim req As HttpWebRequest = HttpWebRequest.Create("http://localhost/sample/j_spring_security_check")
        Form1.cookieData = New CookieContainer
        req.CookieContainer = Form1.cookieData
        req.Method = "POST"
        req.ContentType = "application/x-www-form-urlencoded"
        req.ContentLength = paramBytes.Length
        Dim reqStream As Stream = req.GetRequestStream()
        reqStream.Write(paramBytes, 0, paramBytes.Length)
        reqStream.Close()
        Dim res As HttpWebResponse = req.GetResponse()
        For Each cookie In res.Cookies
            Console.WriteLine("cookie name = " & cookie.Name & ", value = " & cookie.Value)
            Form1.cookieData.Add(cookie)
        Next
        Dim resStream As Stream = res.GetResponseStream()
        Dim sr As New StreamReader(resStream, Encoding.UTF8)
        Console.WriteLine("test = " & sr.ReadToEnd())
        sr.Close()
        Me.Close()
    End Sub
End Class

jp/s6131/sample/controller/HomeController.java
@RequestMapping(value = "/login", method = RequestMethod.GET)
@ResponseBody
public List<Sample> login(HttpServletRequest req, Locale locale, Model model) {
    List<Sample> sampleList = new ArrayList<Sample>();
    Sample sample = new Sample();
    sample.setId(new Long(-1));
    sampleList.add(sample);
    return sampleList;
}


人気ブログランキングへ

2014年3月29日土曜日

Spring Security 設定

pom.xml 設定


Spring Security 3.2.3.RELEASE を指定すると、
Spring Framework 3.1.1.RELEASE との組み合わせで deprecated 警告が発生。
Spring Framework を 3.2.8.RELEASE にバージョンアップ。
velocity 関連が context から context-support に移動したため、これも追加。
<org.springframework-version>3.2.8.RELEASE</org.springframework-version>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${org.springframework-version}</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>3.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>3.2.3.RELEASE</version>
</dependency>

MySQL テーブル作成


password フィールドの長さがマニュアル通りだと、sha256 暗号化した場合に足りないので変更する。
create table users(
    username varchar(50) not null primary key,
    password varchar(100) not null,
    enabled boolean not null
);
create table authorities (
    username varchar(50) not null,
    authority varchar(50) not null,
    constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);
create table persistent_logins (
    username varchar(64) not null,
    series varchar(64) primary key,
    token varchar(64) not null,
    last_used timestamp not null
);

users にレコード追加


password は SHA256 で暗号化、標準で
パスワード{ユーザ名}
例)pass{s6131}
を暗号化するので、ユーザ登録処理を自前で作るまでは、オンラインハッシュジェネレータ等で上記のハッシュを取得して指定する。
(大文字の場合、全て小文字に変換して登録する。)
username password enabled
s6131 da205f3776042925d23bd7d0ca4aae55bc4e17ef245743d5ab5e6c267a9b8504 1

autorities にレコード追加


username authority
s6131 ROLE_ADMIN
s6131 ROLE_USER

persistent_logins は Remember Me にチェックが入っていれば自動的にレコード作成する。

web.xml に追加

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

root-context.xml に追加


<bean id="myUserDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
    <property name="dataSource" ref="dataSource"/>
</bean>
<sec:authentication-manager>
    <sec:authentication-provider user-service-ref='myUserDetailsService'>
        <sec:password-encoder hash="sha-256">
            <sec:salt-source user-property="username"/>
        </sec:password-encoder>
    </sec:authentication-provider>
</sec:authentication-manager>
<sec:http auto-config="true">
    <sec:intercept-url pattern="/login*" access="ROLE_ANONYMOUS" />
    <sec:intercept-url pattern="/**" access="ROLE_USER" />
    <sec:form-login login-page="/login" default-target-url="/" authentication-failure-url="/login?error=true" />
    <sec:logout logout-url="/logout" logout-success-url="/login" />
    <sec:remember-me data-source-ref="dataSource"/>
</sec:http>

src/main/java/jp/s6131/sample/controller/HomeController.java に追加


@RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(HttpServletRequest req, Locale locale, Model model) {
return "login";
}

src/main/webapp/WEB-INF/views/login.jsp を作成


<html>
    <head>
        <title>Login</title>
    </head>
    <body>
        <form action="j_spring_security_check" method="post">
            <p>
                UserName :
                <input type="text" required="true" autofocus="true" name="j_username" />
            </p>
            <p>
                Password :
                <input type="password" required="true" name="j_password" />
            </p>
            <p>
                Remember Me :
                <input type="checkbox" name="_spring_security_remember_me" />
            </p>
            <button type="submit" name="login">Login</button>
        </form>
    </body>
</html>


人気ブログランキングへ

2014年3月26日水曜日

Spring Json 返却 VB.NET 表示

Spring で Json 形式でレスポンスを返却する。

pom.xml
<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-core-asl</artifactId>
    <version>1.9.13</version>
</dependency>
<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>1.9.13</version>
</dependency>

jp.s6131.sample.controller.HomeController.java

@RequestMapping(value = "/sampleList", method = RequestMethod.GET)
@ResponseBody
public List<Sample> sampleList(@RequestParam("cd") String cd) {
    return sampleService.selectByCode(cd);
}

ブラウザから以下URLを指定し、json を返却することを確認。
http://localhost/sample/sampleList?cd=cd1
[{"id":1,"code":"cd1","name":"名前1"},{"id":2,"code":"cd1","name":"名前2"},{"id":3,"code":"cd1","name":"名前3"}]

Visual Studio 2013 for Desktop を起動
「ファイル」「新しいプロジェクト」「Windows フォーム アプリケーション」

JSON.net の導入
「ツール」「ライブラリパッケージマネージャー」「パッケージマネージャーコンソール」
Install-Package Newtonsoft.Json

フォーム(Form1)にテキストボックス(TextBox1)、ボタン(Button1)、データグリッド(DataGridView1)を配置する。
デザイン画面で DataGridView1 を右クリック「列の追加」で id、cd、cdname (name とするとエラーとなる)を追加する。

Form1.vb
Imports System.Net
Imports System.IO
Imports System.Linq
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        DataGridView1.Rows.Clear()
        search_proc(TextBox1.Text)
    End Sub
    Private Sub search_proc(cd As String)
        Dim url As String = "http://localhost/sample/sampleList?cd=" & cd
        Dim req As WebRequest = WebRequest.Create(url)
        Dim res As WebResponse = req.GetResponse()
        Dim reader As StreamReader = New StreamReader(res.GetResponseStream())
        Dim jsonString As String = reader.ReadToEnd()
        Dim jsonList As List(Of JToken) = JArray.Parse(jsonString).Children.ToList
        For Each itemClass As JObject In jsonList
            itemClass.CreateReader()
            DataGridView1.Rows.Add(itemClass("id"), itemClass("code"), itemClass("name"))
        Next
        res.Close()
    End Sub
End Class

実行すると Form1 を表示、テキストボックスに cd1 を入力してボタン押下すると、データグリッドに
データを表示することを確認。


人気ブログランキングへ

2014年3月23日日曜日

Java コード カバレッジ Jacoco インストール

Eclipse プラグインインストール

Eclipse 「ヘルプ」「Eclipse マーケットプレース」
「EclEmma」で検索
「EclEmma Java コード・カバレッジ 2.3.0」をインストール

プロジェクトを右クリック「カバレッジ」「JUnitテスト」
カバレッジビューにカバレッジ率の表示、
Javaエディタでは実行未実行の色分け表示

カバレッジビュー「セッションの削除」
Javaエディタでの色分け表示取りやめ

Jenkins プラグインインストール

「Jenkins の管理」「利用可能」
「JaCoCo Plugin」
「ダウンロード後に再起動してインストール」
Jenkins 再起動
Sample プロジェクト「設定」「ビルド後の処理の追加」「JaCoCoカバレッジレポートを記録」「保存」

pom.xml 追加
<dependency>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.17</version>
</dependency>
・・・
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.7.0.201403182114</version>
    <executions>
        <execution>
            <id>default-prepare-agent</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>default-report</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

「ビルド実行」
実行したジョブを選択「カバレッジレポート」を参照する。


人気ブログランキングへ

2014年3月21日金曜日

Jenkins テスト設定


Eclipse では動作したテストが Jenkins でテスト実行するとエラーとなる。
前記事修正済み。

  • java.lang.IllegalStateException: Failed to load ApplicationContext

テスト用 ApplicationContext が見つからない。(testApplicationContext.xml)

src/test/java/jp/s6131/sample/service/testApplicationContext.xml を
src/test/resources/testApplicationContext.xml に移動。

src/test/java/jp/s6131/sample/service/SampleServiceTest.java
@ContextConfiguration(locations = {"testApplicationContext.xml"}) を
@ContextConfiguration(locations = {"classpath:**/testApplicationContext.xml"}) に変更


  • org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

mapper の select とか insert とか見つからない。

pom.xml に以下を追加して mapper の xml をリソース登録する。
<build>
    ・・・
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
    </resources>
</build>

Jenkins ビルドしてテスト正常終了を確認。


人気ブログランキングへ

2014年3月20日木曜日

Spring DbUnit 設定

サービスのテスト作成


  • テストデータは Excel で用意、サービス毎にディレクトリを分けて管理。
  • トランザクションにより、テスト後には Rollback してデータを元に戻す。

pom.xml に追加。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${org.springframework-version}</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.10-FINAL</version>
</dependency>
<dependency>
    <groupId>org.dbunit</groupId>
    <artifactId>dbunit</artifactId>
    <version>2.4.9</version>
</dependency>

Eclipse で SampleService.java を開き、Ctrl + 9 でテスティングペアを作成する。
src/test/java/jp/s6131/sample/service/SampleServiceTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration
@Transactional
@ContextConfiguration(locations = {"
classpath:**/testApplicationContext.xml"})
public class SampleServiceTest extends DataSourceBasedDBTestCase{
    private final String RESOURCE_DIR = "src/test/resources/SampleServiceTest/";
    @Autowired
    private TransactionAwareDataSourceProxy dataSourceTest;
    @Autowired
    SampleService sampleService;
    @Before
    public void setUp() throws Exception{
        super.setUp();
    }
    @Test
    public void testSelectByCode() throws Exception{
        List<Sample> sampleList = sampleService.selectByCode("cd1");
        ITable it = new XlsDataSet(new File(RESOURCE_DIR + "testSelectByCode.xls")).getTable("sample");
        if (it == null) {
            fail("test data table not found.");
        }
        assertEquals(it.getRowCount(), sampleList.size());
        for (int i = 0; i < sampleList.size(); i++) {
            assertEquals(it.getValue(i, "id"), sampleList.get(i).getId().toString());
            assertEquals(it.getValue(i, "code"), sampleList.get(i).getCode());
            assertEquals(it.getValue(i, "name"), sampleList.get(i).getName());
        }
    }
    @Test
    public void testInsertList() throws Exception{
        ITable insertTable = new XlsDataSet(new File(RESOURCE_DIR + "testInsertList.xls")).getTable("sample");
        if (insertTable == null) {
            fail("test data table not found.");
        }
        List<Sample> sampleList = new ArrayList<Sample>();
        for (int i = 0; i < insertTable.getRowCount(); i++) {
            Sample sample = new Sample();
            sample.setId(new Long((String)insertTable.getValue(i,  "id")));
            sample.setCode((String)insertTable.getValue(i,  "code"));
            sample.setName((String)insertTable.getValue(i,  "name"));
            sampleList.add(sample);
        }
        int cnt = sampleService.insertList(sampleList);
        assertEquals(cnt, insertTable.getRowCount());
        ITable filteredCompareTable = getConnection().createQueryTable("SAMPLE_ORDER", "SELECT code, name FROM sample ORDER BY id");
        ITable resultTable = new XlsDataSet(new File(RESOURCE_DIR + "testInsertListResult.xls")).getTable("sample");
        ITable filteredResultTable = DefaultColumnFilter.excludedColumnsTable(resultTable, new String[]{"id"});
        assertEquals(filteredCompareTable.getRowCount(), filteredResultTable.getRowCount());
        Assertion.assertEquals(filteredCompareTable, filteredResultTable);
    }
    @Override
    protected void setUpDatabaseConfig(DatabaseConfig config) {
        config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory());
    }
    @Override
    protected DataSource getDataSource() {
        return dataSourceTest;
    }
    @Override
    protected IDataSet getDataSet() throws Exception {
        return new XlsDataSet(new File(RESOURCE_DIR + "import.xls"));
    }
}

テスト用設定ファイル

  • src/main/webapp/WEB-INF/spring/root-context.xml を複写して作成。
  • データソースは TransactionAwareDataSourceProxy に変更する。
src/test/resources/testApplicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/jee
    http://www.springframework.org/schema/jee/spring-jee.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- Root Context: defines shared resources visible to all other web components -->
    <context:component-scan base-package="jp.s6131.sample.service"/>
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName">
            <value>com.mysql.jdbc.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:mysql://localhost/sample</value>
        </property>
        <property name="username">
            <value>s6131</value>
        </property>
        <property name="password">
            <value>パスワード</value>
        </property>
    </bean>
    <bean id="dataSourceTest"
        class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
        <constructor-arg ref="dataSource"/>
    </bean>
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref local="dataSourceTest" />
        </property>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSourceTest" />
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="jp.s6131.sample.mapper" />
    </bean>
</beans>

初期データ Excel

src/test/resources/SampleServiceTest/import.xls
シート sample
id code name
1 cd1 名前1
2 cd1 名前2
3 cd1 名前3
4 cd2 名前4
5 cd2 名前5
6 cd3 名前6
7 cd4 名前7
8 cd4 名前8
9 cd4 名前9

testSelectByCode 検索結果 Excel

src/test/resources/SampleServiceTest/testSelectByCode.xls
シート sample
id code name
1 cd1 名前1
2 cd1 名前2
3 cd1 名前3

testInsertList 挿入データ Excel

src/test/resources/SampleServiceTest/testInsertList.xls
シート sample
id code name
10 cd5 名前10
11 cd5 名前11
12 cd5 名前12
13 cd5 名前13

testInsertList 挿入結果 Excel

src/test/resources/SampleServiceTest/testInsertListResult.xls
シート sample
id code name
1 cd1 名前1
2 cd1 名前2
3 cd1 名前3
4 cd2 名前4
5 cd2 名前5
6 cd3 名前6
7 cd4 名前7
8 cd4 名前8
9 cd4 名前9
10 cd5 名前10
11 cd5 名前11
12 cd5 名前12
13 cd5 名前13

テストの実行

SampleServiceTest.java を右クリック、「デバッグ」「JUnit テスト」を選択。
JUnitビューでテスト結果を確認。

2014年3月15日土曜日

Jenkins ジョブ設定

Jenkins で Sample ジョブを作成する


「新規ジョブ作成」「ジョブ名」Sample、「Maven2/3プロジェクトのビルド」でOK。

Maven 設定が必要と表示される
  • 「Jenkinsの管理」「システム設定」「Maven」の名前に適当な名前を設定、
  • 「自動インストール」にチェック

「ソースコード管理」で「Git」を選択、「Repository URL」に C:\Users\ユーザ名\git\master を指定。

ビルド実行


JAVA_HOME が不正で(JRE を参照)tools.jar が無いとエラー発生。
  • 「Jenkinsの管理」「システム設定」「JDK」で C:\Program Files\Java\jdk1.7.0_51 を指定

以下エラー発生。
  • ブートストラップ・クラスパスが-source 1.6と一緒に設定されていません
  • この文字は、エンコーディングMS932にマップできません

pom.xml を編集
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.5.1</version>
    <configuration>
        <source>1.7</source>
        <target>1.7</target>
        <encoding>UTF-8</encoding>
        <compilerArgument>-Xlint:all</compilerArgument>
        <showWarnings>true</showWarnings>         <showDeprecation>true</showDeprecation>
    </configuration>
</plugin>

ビルドの成功を確認。作成した war はとりあえず保留。


人気ブログランキングへ

Jenkins Redmine Git 連携

Jenkins インストール


  • http://mirrors.jenkins-ci.org/war/latest/jenkins.war

ダウンロードした jenkins.war を適当なディレクトリにコピー。
独立したアプリケーションとして実行したい為、java コマンドで起動する形式とした。

  • http ポート 8080 がデフォルトだが、Eclipse の Tomcat で使用するのでオフ(-1)にする。
  • ajpポート 8009 も Eclipse の Tomcat で使用するので 8010 に変更する。
  • デフォルトだとサブディレクトリが無いので /jenkins に指定する。


C:\Program Files\Apache Software Foundation\Apache24\conf\extra\proxy-ajp.conf を管理者で編集
ProxyPass /jenkins/ ajp://localhost:8010/jenkins/

コマンドプロンプト(管理者)で実行
java -Dfile.encoding=UTF-8 -jar jenkins.war --httpPort=-1 --ajp13Port=8010 --prefix=/jenkins

http://localhost/jenkins/ で動作確認。

Jenkins に Git プラグインを追加する


「Jenkinsの管理」「プラグインの管理」「利用可能」タブから「Git Server Plugin」を選択
「ダウンロードして再起動後にインストール」を選択
(「再起動せずにインストール」するとclasses.jar にアクセス出来ないというエラーになる)
Git Plugin
Git Server Plugin
Git Client Plugin
がインストールされる。

Redmine に Jenkins プラグインを追加する


  • https://bitbucket.org/nobiinu_and/redmine_hudson/downloads

ダウンロードした redmine_hudson-2.1.2.zip の中の redmine_hudson フォルダを C:\redmine-2.5.0\plugins に解凍
rake redmine:plugins:migrate RAILS_ENV=production

redmine でプロジェクトを選択してから「設定」タブ「モジュール」で「Hudson」のチェックを入れる。
「Hudson」タブで「設定」を選択、「URL」に http://localhost/jenkins/ を指定、「保存」

「Hudson」で Jenkins ジョブが表示されるのを確認。


人気ブログランキングへ

2014年3月13日木曜日

Redmine Git 設定

Redmine の参照する Git リポジトリは bare でローカルなリポジトリである必要がある。
bare リポジトリは作成していなかったので Eclipse から作成した。

  • Eclipse で「Git リポジトリー・エクスプローラー」パースペクティブに切り替え。
  • 「Gitリポジトリーを作成し、このビューへ追加」アイコンから、「Bare リポジトリーとして作成」チェックを入れ、新規リポジトリーを作成。デフォルトだと「c:\Users\ユーザ名\git\リポジトリ名」。
  • sample リポジトリーのリモートアイコンを右クリック「リモートの作成」リモート名「origin」でOK。
  • 「URI」の変更ボタン押下、ロケーションのURIで「ローカル・ファイル」ボタン押下で先ほど作成したリポジトリを指定。プロトコルは「file」で完了。
  • Javaパースペクティブに戻り、プロジェクト右クリック「チーム」「リモート」「プッシュ」
  • 構成済みリモート・リポジトリーに「リモートの作成」で作成したリポジトリーが選択されている。
  • 「次へ」ボタン押下、「Add All Branches Spec」ボタン押下「完了」でプッシュ終了。


Redmine の設定
  • Sample プロジェクトを作成
  • Sample プロジェクトを選択後、「設定」タブで「リポジトリ」「新しいリポジトリ」を選択
  • 「バージョン管理システム」に「Git」を選択、「識別子」に sample、「リポジトリのパス」に「c:\Users\ユーザ名\git\リポジトリ名」として保存。
  • 再度「設定」タブで「リポジトリ」作成したリポジトリの「ユーザー」選択、Redmineログインユーザとリポジトリユーザを関連付ける。
  • 「活動」タブに履歴、「リポジトリ」タブにリポジトリ情報が表示されることを確認。

2014年3月12日水曜日

Ruby 64bit Thin サーバーインストール

EventMachine インストール


Thin サーバーの動作には EventMachine が必要。
しかし Ruby 64bit 2.0 では導入時にコンパイルエラーが発生する。
いずれ修正されるかもしれないが、現在の所


のカスタマイズした gem を使わせて頂いた。

gem カスタマイズ方法参考:http://higelog.brassworks.jp/?p=2212

Apache HTTP Server 設定


  • C:\Program Files\Apache Software Foundation\Apache24\conf\httpd.conf

コメント外す
LoadModule proxy_http_module modules/mod_proxy_http.so

追加
<Location /redmine>
    ProxyPass http://localhost:3000/redmine
    ProxyPassReverse http://localhost:3000/redmine
</Location>

Redmine 設定


  • C:\redmine-2.5.0\config\environment.rb

スタイルシート、Javascript のパスを通知
Redmine::Utils::relative_url_root = "/redmine"

Thin サーバーの起動

thin -e production -p 3000 start --prefix /redmine

エラー発生:rack のバージョンが違う
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/bundler-1.5.3/lib/bundler/runtime.rb:34:in `block in setup': You have already activated rack 1.5.2, but your Gemfile requires rack 1.4.5. Prepending `bundle exec` to your command may solve this. (Gem:
:LoadError)

1.5.2 をアンインストールする
gem uninstall rack
Select gem to uninstall:
 1. rack-1.4.5
 2. rack-1.5.2
 3. All versions
> 2

エラー発生:繋がらない
C:/Ruby200-x64/lib/ruby/gems/2.0.0/gems/thin-1.6.2/lib/thin/backends/tcp_server.rb:16:in `connect': cannot load such file -- thin/connection (LoadError)

C:\redmine-2.5.0\Gemfile に追加
gem thin
http://localhost/redmine でアクセスを確認


人気ブログランキングへ

2014年3月10日月曜日

Windows 8.1 64bit Ruby 64bit Redmine インストール

Ruby 64bit windows インストール


Ruby のダウンロード
Ruby 2.0.0-p451 (x64)
DEVELOPMENT KIT For use with Ruby 2.0 (x64 - 64 bits only)
ダウンロードした rubyinstaller-2.0.0-p451-x64.exe を選択し、インストール。

ダウンロードした DevKit-mingw64-64-4.7.2-20130224-1432-sfx.exe を選択し、
c:\devkit に解凍

devkit をインストールする為、コマンド プロンプト(管理者)で以下を実行。
cd c:\devkit
ruby dk.rb init
ruby dk.rb install

Rails のインストール
gem install rails

Redmine のインストール


MySQL Workbenchでデータベース作成 SQL 発行
create database redmine character set utf8;
create user 'redmine'@'localhost' identified by 'パスワード';
grant all privileges on redmine.* to 'redmine'@'localhost';

Redmine のダウンロード
ダウンロードした redmine-2.5.0.zip を選択し、
c:\redmine-2.5.0 に解凍

C:\redmine-2.5.0\config\database.yml.sample を database.yml にコピーしてから編集
production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  password: "パスワード"
  encoding: utf8

MySQL のライブラリが mingw64-gcc コンパイラと互換性が無い為、ライブラリの作成し直しが必要となる。
参考:http://www.unknownerror.org/Problem/index/-731657380/gem-mysql2-segmentation-fault-on-win7x64-ruby-20-mysql-56/

Mingw のインストール(dlltool)

「Files」「Toolchains targetting Win64」「Personal Builds」「sezero_4.5_20111101」
ダウンロードした mingw-w64-bin_x86_64-mingw_20111101_sezero.zip を選択、
c:\mingw に解凍。

DEFファイル抽出ツールのインストール(gendef.exe)

ダウンロードした svm-map-win.zip を選択、
c:\svm-map に解凍。

MySQL コネクターインストール

MySQL をインストールした際のコネクターが
C:\Program Files\MySQL\MySQL Connector C 6.1.3
に既に存在するが、別にインストールする。
Windows (x86, 64-bit), ZIP Archive
ダウンロードした mysql-connector-c-6.1.3-winx64.zip を選択、
c:\mysql-connector-c-6.1.3-winx64 に解凍。

ライブラリの再作成

コマンド プロンプト(管理者)で以下を実行
cd \mysql-connector-c-6.1.3-winx64\lib
\svm-map\python-mingw-lib\gendef.exe libmysql.dll
\mingw\bin\dlltool -v --dllname libmysql.dll --def libmysql.def --output-lib libmysql.lib

作成した libmysql.lib と libmysql.dll を C:\Ruby200-x64\bin へコピーする。(rake 時に必要)

bundle インストール、実行。
cd c:\redmine-2.5.0
gem install bundler
bundle install --without development test

c:\redmine-2.5.0\Gemfile を編集
gem "mysql2", "~> 0.3.11", :platforms => [:mri, :x64_mingw]

mysql2 インストール
gem install mysql2 -- --with-mysql-dir=c:/mysql-connector-c-6.1.3-winx64

mysql2 bundle 更新
bundle update

セッションデータ暗号化キーの生成
rake generate_secret_token

Redmine DB作成
set RAILS_ENV=production
rake db:migrate
rake redmine:load_default_data

WEBrickで redmine をテスト起動
ruby script/rails server webrick -e production

http://localhost:3000/ で redmine トップ画面を表示。


人気ブログランキングへ

2014年3月7日金曜日

Eclipse Git ローカルリポジトリ設定

ローカルでバージョン管理する為に Git を導入する。
その為、ローカルリポジトリの作成以外、今回は不要。

プロジェクトの「プロパティ」「チーム」「プロジェクトの共用」「Git」
「プロジェクトの親フォルダー内のリポジトリーを使用または作成」チェックを入れる。
チェックを入れないと、プロジェクト自体がデフォルトリポジトリディレクトリに移動してしまうので注意。
「リポジトリーの作成」ボタン選択、「完了」

プロジェクトの「プロパティ」「チーム」「コミット」
「コミットメッセージ」にメッセージを入力。
「全て選択」アイコンを選択し、すべてのファイルを対象としてコミットする。

その後は変更がある度にコミットを繰り返す。
プロジェクトの「プロパティ」「チーム」「ヒストリーに表示」
でバージョン管理を行う。


人気ブログランキングへ

2014年3月4日火曜日

Windows 8.1 64bit Apache HTTP Server 2.4 64bit インストール

Apache 2.4 win64 binary をダウンロード
  • http://www.apachelounge.com/download/win64/

エクスプローラでダウンロードした httpd-2.4.7-win64.zip を選択し、以下ディレクトリに解凍。
  • C:\Program Files\Apache Software Foundation\Apache2.4\

C:\Program Files\Apache Software Foundation\Apache2.4\conf\httpd.conf を管理者権限で編集
  • ファイル中の c:\Apache2.4 を C:\Program Files\Apache Software Foundation\Apache2.4 に変更

  • 以下のコメントを外す
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so

  • ServerName 設定
ServerName localhost:80

  • proxy-ajp.conf インクルード追加
Include conf/extra/proxy-ajp.conf

C:\Program Files\Apache Software Foundation\Apache24\conf\extra\proxy-ajp.conf を管理者権限で作成
ProxyPass /sample/ ajp://localhost:8009/sample/

コマンドプロンプト(管理者)からコマンド実行
  • bin ディレクトリに移動
cd "c:\Program Files\Apache Software Foundation\Apache24\bin"

  • コンフィグチェック
httpd -t

  • サービス登録
httpd -k install

  • サービス起動
httpd -k start

eclipse でサーバー起動しておき、ブラウザから http://localhost/sample/ でアクセス確認。


人気ブログランキングへ

2014年3月1日土曜日

Spring Batch へ処理の移動

前回の Velocity 変換処理を Spring Batch に移動する。

pom.xml へ Spring Batch 設定追加
<dependency>
    <groupId>org.springframework.batch</groupId>
    <artifactId>spring-batch-core</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

Spring Batch 制御テーブル作成
eclipse のパッケージ・エクスプローラーから
プロジェクト中のMaven依存関係ツリーを開き、
spring-batch-core-2.2.5.RELEASE.jar の
org.springframework.batch.core.schema-mysql.sql を
MySQLWorkbench で実行、制御テーブルを作成する。

src/main/webapp/WEB-INF/spring/root-context.xml へバッチ定義追加
<bean id="jobOperator" class="org.springframework.batch.core.launch.support.SimpleJobOperator">
    <property name="jobLauncher" ref="jobLauncher"/>
    <property name="jobExplorer" ref="jobExplorer"/>
    <property name="jobRepository" ref="jobRepository"/>
    <property name="jobRegistry" ref="jobRegistry"/>
</bean>
<bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean">
    <property name="dataSource" ref="dataSource" />
</bean>
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
    <property name="jobRegistry" ref="jobRegistry"/>
</bean>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
    <property name="jobRepository" ref="jobRepository" />
    <property name="taskExecutor">
        <bean class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
    </property>
</bean>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="transactionManager" ref="transactionManager"/>
    <property name="maxVarCharLength" value="2000"/>
    <property name="isolationLevelForCreate" value="ISOLATION_SERIALIZABLE"/>
</bean>
<bean id="sampleTasklet" class="jp.s6131.sample.batch.SampleTasklet" />
<batch:job id="sampleJob">
    <batch:step id="sampleTaskletStep">
        <batch:tasklet ref="sampleTasklet" />
    </batch:step>
</batch:job>
<bean id="sampleLauncher" class="jp.s6131.sample.batch.SampleLauncher">
    <property name="jobLauncher" ref="jobLauncher"/>
    <property name="job" ref="sampleJob"/>
</bean>

サンプルバッチラウンチャ
jp.s6131.sample.SampleLauncher.java
package jp.s6131.sample.batch;
・・・
public class SampleLauncher {
    private static final Logger logger = LoggerFactory.getLogger(SampleLauncher.class);
    private JobLauncher jobLauncher;
    private Job job;
    public void launch(String param) {
        JobParameters jobParameters = new JobParametersBuilder()
            .addLong("time", System.currentTimeMillis())
            .addString("param", param)
            .toJobParameters();
        try {
            JobExecution execution = jobLauncher.run(job, jobParameters);
            logger.debug("Exit Status : " + execution.getStatus());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    public JobLauncher getJobLauncher() {
        return jobLauncher;
    }
    public void setJobLauncher(JobLauncher jobLauncher) {
        this.jobLauncher = jobLauncher;
    }
    public Job getJob() {
        return job;
    }
    public void setJob(Job job) {
        this.job = job;
    }
}

サンプルバッチタスクレット
jp.s6131.sample.SampleTasklet.java
package jp.s6131.sample.batch;
・・・
public class SampleTasklet implements Tasklet {
    private static final Logger logger = LoggerFactory.getLogger(SampleTasklet.class);
    @Autowired
    VelocityEngine velocityEngine;
    @Override
    public RepeatStatus execute(StepContribution paramStepContribution,
            ChunkContext paramChunkContext) throws Exception {
        Map<String, Object> param = new HashMap<String, Object>();
        Map<String, String> data = new HashMap<String, String>();
        for (int i = 0; i < 10; i++) {
            data.put(String.format("%05d", i), String.format("名称%02d", i));
        }
        param.put("datalist", data);
        try {
            String outputFile = paramChunkContext.getStepContext().getStepExecution().getJobParameters().getString("param");
            logger.debug("output html = " + outputFile);
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(outputFile), "utf-8");
            VelocityEngineUtils.mergeTemplate(velocityEngine, "sample.vm", param, osw);
            osw.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return RepeatStatus.FINISHED;
    }
}

バッチ起動コントローラ
jp.s6131.sample.controller.HomeController.java
@Autowired
SampleLauncher sampleLauncher;
・・・
sampleLauncher.launch(req.getSession().getServletContext().getRealPath("/html/sample.html"));

http://localhost:8080/sample 実行後、バッチテーブルにログが出力されている事を確認。


人気ブログランキングへ

2014年2月27日木曜日

Velocity HTML 出力

resolver で vm 指定するのではなく、通常の html としてファイル出力し、ブラウザから静的にロードする。

pom.xml へ Velocity 設定追加
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.7</version>
</dependency>

src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml へリソースディレクトリ定義追加
<resources mapping="/html/**" location="/html/" />

src/main/webapp/WEB-INF/spring/root-context.xml へ velocityEngine 定義追加
<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
    <property name="resourceLoaderPath" value="/WEB-INF/velocity" />
    <property name="velocityPropertiesMap">
        <map>
            <entry key="input.encoding" value="UTF-8" />
            <entry key="output.encoding" value="UTF-8" />
        </map>
    </property>
</bean>

Velocityテンプレートファイル
/WEB-INF/velocity/sample.vm
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>サンプルタイトル</title>
</head>
<body>
    <ul id="data">
    #foreach ($data in $datalist.entrySet())
        <li>
        $data.key
        $data.value
        </li>
    #end
    </ul>
</body>
</html>

コントローラ
jp.s6131.sample.controller.HomeController.java
@Autowired
VelocityEngine velocityEngine;
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(HttpServletRequest req, Locale locale, Model model) {
・・・
Map<String, Object> param = new HashMap<String, Object>();
Map<String, String> data = new HashMap<String, String>();
for (int i = 0; i < 10; i++) {
    data.put(String.format("%05d", i), String.format("名称%02d", i));
}
param.put("datalist", data);
try {
    String outputFile = req.getSession().getServletContext().getRealPath("/html/sample.html");
    logger.debug("output html = " + outputFile);
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(outputFile), "utf-8");
    VelocityEngineUtils.mergeTemplate(velocityEngine, "sample.vm", param, osw);
    osw.close();
} catch (Exception ex) {
    ex.printStackTrace();
}

http://localhost:8080/sample アクセス後、
http://localhost:8080/sample/html/sample.html にアクセスし、作成済 HTML を確認。


人気ブログランキングへ

2014年2月25日火曜日

MyBatis 3 テーブル書込、トランザクション

src/main/webapp/WEB-INF/spring/root-context.xml へトランザクション設定追加
<bean id="transactionManager"    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource">
        <ref local="dataSource" />
    </property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>

サービスinterface
jp.s6131.sample.service.SampleService.java
package jp.s6131.sample.service;
・・・
public interface SampleService {
    ・・・
    public int insertList(List<Sample> sampleList);
}

サービス
jp.s6131.sample.service.impl.SampleServiceImpl.java
package jp.s6131.sample.service.impl;
・・・
@Service("sampleService")
public class SampleServiceImpl implements SampleService {
    @Autowired
    SampleMapper sampleMapper;
    ・・・
    @Override
    @Transactional
    public int insertList(List<Sample> sampleList) {
        int rtnTotal = 0;
        for (Sample sample : sampleList) {
            int rtn = sampleMapper.insert(sample);
            logger.debug("insert rtn = " + rtn);
            rtnTotal += rtn;
        }
        return rtnTotal;
    }
}

コントローラ
jp.s6131.sample.HomeController.java
@Autowired
SampleService sampleService;
・・・
List<Sample> sampleList = new ArrayList<Sample>();
for (int i = 0; i < 10; i++) {
    Sample sample = new Sample();
    sample.setCode("000000000" + (i + 1));
    sample.setName(String.format("Name%04d", i));
    sampleList.add(sample);
}
int rtn = sampleService.insertList(sampleList);
logger.debug("rtn = " + rtn);

実行ログ
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@61fc1ea]
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ==>  Preparing: insert into sample (id, code, name ) values (?, ?, ? )
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ==> Parameters: null, 0000000001(String), java.io.StringReader@4b9bc91d(StringReader)
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - <==    Updates: 1
DEBUG: jp.s6131.sample.service.impl.SampleServiceImpl - insert rtn = 1
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@10cd263e]
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ==>  Preparing: insert into sample (id, code, name ) values (?, ?, ? )
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ==> Parameters: null, 0000000002(String), java.io.StringReader@25d4cfde(StringReader)
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - <==    Updates: 1
DEBUG: jp.s6131.sample.service.impl.SampleServiceImpl - insert rtn = 1
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@2c4f00de]
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ==>  Preparing: insert into sample (id, code, name ) values (?, ?, ? )
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ==> Parameters: null, 0000000003(String), java.io.StringReader@3bfa2596(StringReader)
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - <==    Updates: 1
取得するコネクションが毎回違い、トランザクションが効いていない。
  • log4j.xml で Spring のログレベルを全て debug にしてもエラー等無く、原因が不明。
  • ネットで検索するとサービスが Spring に認識されていない事が原因らしいが、具体的な対応方法は不明。

対応した方法
servlet-context.xml でデフォルトで
<context:component-scan base-package="jp.s6131.sample>
となっていたのを
<context:component-scan base-package="jp.s6131.sample.controller>
とし、controller パッケージに HomeController.java を移動。

root-context.xml に
<context:component-scan base-package="jp.s6131.sample.service"/>
を追加し、サービスを具体的に検索対象に指定。

変更後の実行ログ
DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@1042ce9b]DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ==>  Preparing: insert into sample (id, code, name ) values (?, ?, ? ) DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ==> Parameters: null, 0000000001(String), java.io.StringReader@2fc64742(StringReader)DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - <==    Updates: 1DEBUG: jp.s6131.sample.service.impl.SampleServiceImpl - insert rtn = 1DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@1042ce9b]DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ==>  Preparing: insert into sample (id, code, name ) values (?, ?, ? ) DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ==> Parameters: null, 0000000002(String), java.io.StringReader@b3a0261(StringReader)DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - <==    Updates: 1DEBUG: jp.s6131.sample.service.impl.SampleServiceImpl - insert rtn = 1DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@1042ce9b]DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ==>  Preparing: insert into sample (id, code, name ) values (?, ?, ? ) DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - ==> Parameters: null, 0000000003(String), java.io.StringReader@1997ce1a(StringReader)DEBUG: jp.s6131.sample.mapper.SampleMapper.insert - <==    Updates: 1
コネクションが同じものを使用する様になり、トランザクションが効くようになった。


人気ブログランキングへ