きっと何者にもなれないSEの記録帳

日々で得たIT技術を記録していきます。

社内コミュニティのすゝめ

レガシーな環境でも社内コミュニティを作るとなかなか良いよって話です。

f:id:hi1280:20161230151047p:plain

自分の環境について

そこそこ大規模なSIerのSEです。
SIerというとブラックなイメージがつきまとうはず。
ITエンジニアなのにほとんど触っているのはExcelであったり、GitHubといったITエンジニアならば既に常識になりつつあるようなクラウドサービスが利用できないなど、ネットでネタにされるような労働環境がリアルにあったりする場所だったりします。

そんな労働環境に置かれながらも、もがいていたりします。
そんな一つのもがきとして、社内コミュニティの活動を行なっています。

社内コミュニティとは

社内コミュニティとはなんでしょう?

まずIT界隈でコミュニティと呼ばれるものを定義すると以下の定義になります。

「同じ興味・関心・利害を共にし、情報の共有・問題の解決などを行う共同体」

会社内でこのようなコミュニティを作るのが社内コミュニティです。
同じ興味・関心・利害を共にするというところがいわゆる官僚主義的な会社組織と異なる部分になり、社内コミュニティの面白いところです。

当方の行動指針の一つになっているFearless Changeでは勉強会をするのが新しいアイデアを広める際の仲間を集める良いパターンとして紹介されています。
興味や関心を共にすることができるということから、社内で行う勉強会というのが一つの社内コミュニティではないかと思います。

仕事に役立つような勉強を業務活動として行う場合にはどうなるでしょう?
おそらく義務感が発生してしまい、興味や関心、利害を共にすることはできずにポジティブな雰囲気にはならないはずです。
自分の興味がないことを勉強したとしてもすぐに忘れてしまうでしょう。

社内コミュニティの中で勉強するとどうでしょう?
コミュニティの参加者は学びたいという意思が明確です。
興味や関心がないと自らの時間を費やしてまで勉強会に参加しようなどと思わないはずです。
この場所に集まった人たちは同じ会社にいながら、官僚主義的な利害関係がない仲間となる人たちでしょう。

得られたもの

こういった仲間が得られるとどうなるでしょうか?
勉強会内では積極的な意見交換が行われます。これにより、興味深い気づきが得られます。

例えば、私のコミュニティでは、アジャイル開発の勉強を行なっています。
ある回では、プランニングポーカーをテーマに扱いました。
プランニングポーカーにはカードの種類がいくつかあり、カードによっては説明しないと意味が分からないようなカードも存在します。
カードの意味が分からないというのは想定外の状況でしたが、参加者の中の有識者がフォローしてくれました。お互いに手助けする雰囲気があるというのがコミュニティの良い点であると感じました。

参加者の意見を聞くと楽しかったといったポジティブな意見を聞くことが多く、これもコミュニティとして行うことのメリットであると感じています。
仕事の中では楽しかったという意見を聞いたことがありませんでした。
この楽しい感情から生まれるポジティブな意見というのは貴重です。

会社という組織にいながら互いを認め合う雰囲気を感じることができます。
社内コミュニティを作ることで、良い仲間を得ることができたと思います。

モチベーション、コミュニケーションといった点で、ここは居心地が良い場所だなと実感しています。

今後に向けて

コミュニティを介することで、したいからするという前提のもとで活動が行われます。
モチベーションを高く保てるため理想的な環境です。
コミュニティのように利害関係なしに楽しみながら活動することがビジネスでも必要になります。
Googleが良い例です。

特にSIerのような利害関係にまみれている場所にとって、このような文化を取り入れることは良い変化につながると思っています。
今後はこのような社内コミュニティのエネルギーを会社にアピールしていければと思っています。

組織として常に学習する文化が根付くことが理想だと感じる、今日この頃です。
来年はそんな理想に近づけるように活動していきます。

参考

Fearless Change アジャイルに効く アイデアを組織に広めるための48のパターン

Fearless Change アジャイルに効く アイデアを組織に広めるための48のパターン

https://blog.sixapart.jp/2015-10/what-is-community.html

Java SE 8のパフォーマンスを簡単に測定してみた

Java SE 8が最近リリースされました。
Java SE 8でJVMに対してパフォーマンスチューニングがされたということを聞き、簡単にパフォーマンスを測定してみました。

・測定方法
100万個のBeanをListに格納する時間を計測

・実行環境
JRE
 Java SE 6U45
 Java SE 7u51
 Java SE 8
IDE
 NetBeans IDE 8.0

・結果
Java SE 6U45 :平均113ms
Java SE 7U51 :平均174ms
Java SE 8 :平均346ms

SE 7から倍くらい遅いという結果になりました。なぜに!?
IDEでやるのがまずいのかと思って、コンソールでも試してみましたが変わらずでした。

測定プログラムは以下のとおり。

package sample;
import java.util.ArrayList;

public class Benchmark1 {

     public static void main(String[] args) {
          for(int j=0;j<10;j++){
              long currentTimeMillis = System.currentTimeMillis();
              ArrayList<User> list = new ArrayList<User>();
              Benchmark1 benchmark1 = new Benchmark1();
              for(int i = 0; i < 1000000; i++){
                  list.add(benchmark1.new User("テスト", "テスト県テスト市", "123456"));
              }
              System.out.println(list.size());
              System.out.println(System.currentTimeMillis() - currentTimeMillis);
          }
     }

     public class User{
          private String name;
          private String address;
          private String id;

          public User(String name, String address, String id) {
               super();
               this.name = name;
               this.address = address;
               this.id = id;
          }
          public String getName() {
               return name;
          }
          public void setName(String name) {
               this.name = name;
          }
          public String getAddress() {
               return address;
          }
          public void setAddress(String address) {
               this.address = address;
          }
          public String getId() {
               return id;
          }
          public void setId(String id) {
               this.id = id;
          }
     }

}


Java SE 8ということで、StreamAPIを使う方法でもやりました。結果は変わりませんでした。
mainメソッドを以下のように書き換え。

for(int j=0;j<10;j++){
      long currentTimeMillis = System.currentTimeMillis();
      Benchmark2 benchmark2 = new Benchmark2();
            Stream<User> generate = Stream.generate(() ->; benchmark2.new User("テスト","テスト県テスト市","123456"));
            List<User> list = generate.limit(1000000).collect(Collectors.toList());
            System.out.println(list.size());
      System.out.println(System.currentTimeMillis() - currentTimeMillis);
}

ただ、一概に今回の結果を捉えてはいけないかなと思います。
Java SE 8からJVMのメモリの利用効率を改善したということで、もっと複雑にJVMが動作するプログラムに対してパフォーマンス計測を行った場合にはどうなるかを計測しないといけないと思います。
例えば、Webアプリケーションとか。

時間があれば、別の形でパフォーマンス計測したいと思います。
Javaのバージョンを上げれば、パフォーマンスが上がりますと言えれば、Java8が業務で使える日も近づくかもしれませんので。

Spring MVC Validationを適用する

Spring MVCでValidationを適用する手順を記します。
 
pom.xml
Hibernate Validatiorを使用するためにライブラリをインポートします。
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.0.3.Final</version>
</dependency>
 
Controllerクラス
画面からの値を受け取るエンティティクラスに@Validアノテーションを適用します。
BindingResultクラスの変数にValidationを行った結果が入ります。
bindingResult.hasErrors()で入力チェックエラーが存在するかを判断しています。エラーが存在する場合、上記メソッドの結果がtrueになります。
@RequestMapping(value="/user/create", method=RequestMethod.POST)
public String create(@Valid @ModelAttribute("userModel") UserModel userModel,BindingResult bindingResult , Model model) {

    if(bindingResult.hasErrors()){

        model.addAttribute("userModel", userModel);
        model.addAttribute("status", NEW);
        return "user";
    }
    userService.createUser(userModel);
    model.addAttribute("userModel",userModel);
    model.addAttribute("status", EDIT);
    return "user";
}

 

画面の入力値を扱うエンティティクラスにValidation用のアノテーションを指定します。
下記では@NotEmpty(必須入力用アノテーション)を使用しています。

public class UserModel {
    @NotEmpty
    private String username;
    private String mailaddress;

    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getMailaddress() {
        return mailaddress;
    }
    public void setMailaddress(String mailaddress) {
        this.mailaddress = mailaddress;
    }
}

 

ValidationMessages.properties
エラーメッセージの内容を日本語にするために編集します。
Hibernate Validatiorのjarファイル内にValidationMessages.propertiesが存在します。
デフォルトではこの内容が表示されます。

@NotEmptyは以下のように定義されています。

org.hibernate.validator.constraints.NotEmpty.message = may not be empty
 
デフォルトを上書きする形で、ValidationMessages.propertiesを新たに定義し、プロジェクトのクラスパス内に配置します。
org.hibernate.validator.constraints.NotEmpty.message = 必須入力です。
 
form:errorsタグでエラーメッセージを表示します。
<form:input path="username"/><form:errors path="username"/>
 

Spring MVC + MirageSQL CRUD画面作成

前回のSpring MVC + MirageSQL環境構築に続き、CRUDを行える画面を作成しました。
 
CRUDとは、「作成(Create)」「読み出し(Read)」「更新(Update)」「削除(Delete)」が行えることです。
 
Serviceクラス
前回までで読み出し(Read)は行えていたので、CUDを追加します。
基本のコードは下記のとおりです。
エンティティクラス(Users)を用意し、画面からの値をエンティティクラスにセットします。
sqlManagerクラスのCUDに対応するメソッドを呼び出します。
読み出しの時のように、SQLファイルを用意する必要はありません。
@Transactional
public void createUser(UserModel userModel){
     Users user = new Users();
     user.username = userModel.getUsername();
     user.mailaddress = userModel.getMailaddress();
     sqlManager.insertEntity(user);
}
 
データベースへの操作 呼び出しメソッド
Create insertEntity
Update updateEntity
delete deleteEntity
 
エンティティクラスは下記のように作成します。
@PrimaryKeyアノテーションで主キーになるプロパティを指定します。
public class Users{

     @PrimaryKey(generationType=GenerationType.APPLICATION)
     public String username;

     public String mailaddress;

}
 
Controllerクラス
更新を行うメソッドを例に説明します。
①モデルクラスの初期化
②のupdateメソッドよりも前にこのメソッドの処理は実行されます。
画面からのリクエストパラメータを扱うにはJavaBeansのようなクラスを定義する方法が簡単です。それがここでいうところのモデルクラスです。
updateメソッドでモデルクラスにあたるUserModelクラスを扱うには@ModelAttributeを指定したメソッドの定義が必要です。①の記述のようにモデルクラスを初期化しています。
 
②サービスクラスの呼び出し
①の実装によって、モデルクラス(UserModelクラス)が扱えるようになっています。userModelの値をそのままサービスクラスに渡すだけです。
 
③モデルクラス定義
画面の値に対応したクラスです。
@NotEmptyはバリデーションを行うための記述です。詳しい内容については別の機会に記したいと思います。
 

// ①モデルクラスの初期化
@ModelAttribute("userModel")
public UserModel initUserModel() {
     return new UserModel();
}

// ②サービスクラスの呼び出し
@RequestMapping(value="/user/update", method=RequestMethod.POST)
public String update(@ModelAttribute("userModel") UserModel userModel, Model model) {
     userService.updateUser(userModel);
     model.addAttribute("userModel",userModel);
     model.addAttribute("status", EDIT);
     return "user";
}

// ③モデルクラス定義
public class UserModel {
     @NotEmpty
     private String username;
     private String mailaddress;

     public String getUsername() {
          return username;
     }
     public void setUsername(String username) {
          this.username = username;
     }
     public String getMailaddress() {
          return mailaddress;
     }
     public void setMailaddress(String mailaddress) {
          this.mailaddress = mailaddress;
     }
}
 
 
View(JSP)
View側でもモデルクラスを使用する指定を行う必要があります。
Spring MVCで用意されているformタグを使用します。modelAttribute属性に使用するモデルのキーを指定します。
<form:form modelAttribute="userModel" action="update" method="POST">
・・・入力項目の記述・・・
</form:form>
 
入力項目を表示するために、formタグ内で使用するタグはSpring MVCで用意されているタグを使用する必要があります。
詳細は以下の記事が参考になります。
 
次回は入力チェック(Validator)についての記事を書きたいと思います。
ここまでのサンプルソースを下記に挙げています。
 
参考
 

マシン性能に異常が見られないパフォーマンス問題(Java製Webアプリ+MySQL)

Java製Webアプリ+MySQL上で発生したパフォーマンス問題についてです。
負荷分散を行うシステム構成で、APサーバが2台の場合、性能に問題なし。APサーバが1台の場合、性能が悪化するという問題が起こりました。ただし、CPU使用率は限界に達していない。JVMのメモリ量は余裕がある。NW帯域は問題なしという状態。
 
結論からいうと、DBのコネクションプールの処理でスレッドに対して同期処理を行っていて、多くのスレッドで待ちが発生していたためでした。
単純にスレッドが待っている状態なので、CPUもメモリも問題ないわけです。
 
この問題にどのように対応したかについての記録です。
 
1.スレッドダンプを取りました。
2.JDBCドライバの設定をいじり、性能が向上することを確認しました。
 
スレッドダンプを取る
JDK標準で用意されているjstackコマンドを使用しました。
 
今回の場合、tomcatを使用しています。tomcatのプロセスIDを調べて、下記のコマンドでスレッドダンプを取りました。
 
jstack プロセスID

スレッドダンプの解析には下記を使用しました。
 
 
侍を使うと同期待ちを行っているスレッドがひと目でわかります。また、スタックトレースからDBのコネクションプールを行っている処理で多くのスレッドが同期待ちしていました。

JDBCドライバの設定
スレッドダンプを取り、多くのスレッドが同期待ちを起こしていることは確認できました。しかし、それが性能悪化の原因であるかは性能を改善させることで証明するしかありません。
 
アプリケーションとDBとのやりとりを速めることができれば、各スレッドの処理を速くすることができるので、性能を改善することができると判断しました。
 
色々調べてみると、DB提供元から用意されているJDBCドライバにパフォーマンスに関わる設定が存在することを知りました。
 
今回はMySQLを使用しています。性能に直結したJDBCドライバの設定は下記です。
 
トランザクション分離レベルの設定を毎回行わない。autocommitの命令を排除するということを行いました。
プロパティ名 デフォルト値 設定値
alwaysSendSetIsolation true false
elideSetAutoCommits false true

MySQLJDBCドライバ設定は下記を参考にしました。
 
 
 
上記設定を行うことで、APサーバ1台でもAPサーバ2台と同等の性能を発揮することができました。
 
所感
今回の調査をするにあたり、スレッドダンプを取るということを始めて行いました。意外に簡単に取得できるものだということを実感しました。また、スレッドダンプを取ることが原因を突き止めるきっかけにもなったので、今後はスレッドダンプを取るということも初期段階の調査に加えるべきと感じました。

Spring MVC + MirageSQL 環境構築とサンプルづくり

仕事でSeasar2を主に使用してきましたが、Seasar2コミュニティの衰退から今後Seasar2を使っていくのは難しいと判断しました。Seasar2に変わるフレームワークを模索する必要があるなと。
 
フレームワークを選定するに当たって、Seasar2の良さである2WaySQLは継続して利用したいと考えました。
Javaで業務システムを構築する場合にSQLのチューニングは不可欠だと思います。Seasar2に備わる2WaySQLはSQLのメンテナンスをより良く行える手段と考えています。
 
2WaySQLを前提に考えた場合に対象になったライブラリがMirage-SQLです。

GitHub - mirage-sql/mirage: The SQL Centric Database Access Library for Java

ここでは、Spring MVC + MirageSQLという構成で検証してみます。データベースからデータを取得し、画面に表示するという単純なサンプルを作成します。
 
なお、筆者はSpring framework自体使うのが始めてなので、間違いがあるかもしれません。
 
Spring MVCのプロジェクトを作成します。
以下を参考に作成しました。非常に分かりやすくまとめられています。
 
Spring MVCを使用する準備ができたので、Spring MVC + MirageSQLの構成を作っていきます。
 
mavenへの依存ライブラリ定義追加
ポイントとしてはspring-jdbcを追加することです。詳細は後ほど。
jstlは画面表示のために使いたかっただけなので、本質的には不要です。
 
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>3.2.6.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>3.2.6.RELEASE</version>
</dependency>
<dependency>
  <groupId>jp.sf.amateras</groupId>
  <artifactId>mirage</artifactId>
  <version>1.2.2</version>
</dependency>
<dependency>
  <groupId>jstl</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>
DispatcherServletの設定
dispatcher-servlet.xmlを編集します。
Springのトランザクション管理を使用する必要が有るため、<tx:annotation-driven />が必要です。
<?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:context="http://www.springframework.org/schema/context"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:mvc="http://www.springframework.org/schema/mvc"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

  <mvc:annotation-driven />
  <tx:annotation-driven />

  <context:component-scan base-package="sample.controller" />
  <context:component-scan base-package="sample.service" />

  <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/views/" p:suffix=".jsp" />

</beans>
 
以下は、MirageSQL向けの設定内容です。データベースへの設定は適宜変更して下さい。
本内容では、MySQLを使用しています。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://localhost:3306/test" />
  <property name="username" value="mysql_user" />
  <property name="password" value="password" />
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="connectionProvider" class="jp.sf.amateras.mirage.integration.spring.SpringConnectionProvider">
  <property name="transactionManager" ref="transactionManager" />
</bean>

<bean id="dialect" class="jp.sf.amateras.mirage.dialect.MySQLDialect" />

<bean id="sqlManager" class="jp.sf.amateras.mirage.SqlManagerImpl">
  <property name="connectionProvider" ref="connectionProvider" />
  <property name="dialect" ref="dialect" />
</bean>
 
Controllerクラス
@RequestMapiingアノテーションによって、「/users」というURLからusersメソッドを実行するという設定が行われています。
データベースから取得した値をViewに渡すためにModelクラスを介する必要があります。
データベースへの問い合わせを行う処理はServiceクラスに持つことにしています。
@Controller
public class UserController {

     @Autowired
     UserService userService;

     @RequestMapping(value="/users", method=RequestMethod.GET)
     public String users(Model model) {
          model.addAttribute("userList",userService.findAll());
          return "users";
     }
}
 
Serviceクラス
データベースへの問い合わせを行う処理をしています。MirageSQLが関係する部分です。
SqlManagerクラスがMirageSQLで用意されているクラスです。このクラスを介して、データベースへの問い合わせを行うことが可能です。
 
「dispatcher-servlet.xml」に記述した設定によって、SqlManagerをDIすることが可能になっています。@AutowiredアノテーションでSqlManagerを利用することが可能です。
 
@Transactionalアノテーションを指定することで、指定されたメソッドの単位でトランザクション管理がなされます。SqlManagerクラスを使用するためにはトランザクション管理内である必要があります。
mavenへspring-jdbcを追加することで、@Transactionalアノテーションを使用できます。
@Component
public class UserService {
     @Autowired
     SqlManager sqlManager;

     @Transactional
     public List<Map> findAll() {
          return sqlManager.getResultList(Map.class, new ClasspathSqlResource("select-user.sql"));
     }

}
 
View(JSP
データベースから取得したデータをそのまま一覧表示しています。
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>サンプル</title>
</head>
<body>
<div>ユーザ一覧</div>
<table border="1">
  <tr>
    <th>ユーザ名</th>
    <th>メールアドレス</th>
    <th></th>
  </tr>
  <c:forEach var="user" items="${userList}">
  <tr>
    <td>${user.username}</td>
    <td>${user.mailaddress}</td>
    <td><a href="<c:url value="/user" ><c:param name="username" value="${user.username}" /></c:url>">表示</a></td>
  </tr>
  </c:forEach>
</table>
</body>
</html>
 
補足
SQLファイル
SELECT
  *
FROM
  users

/*BEGIN*/
WHERE

  /*IF username != null */
  username = /*username*/'satou'
  /*END*/

/*END*/
ORDER BY username
 
・テーブル構造
テーブル名 users
カラム名 属性
username VARCHAR(32) 主キー
mailaddress VARCHAR(30)  
 
所感
簡単なサンプルですが、Seasar2と遜色ない形で作れるフレームワークだと思います。DBへの問い合わせ部分についてはS2JDBCの2WaySQLの考え方を踏襲しているようなので、ほぼ違和感なく使えそうです。今回のサンプルを作るにあたってはむしろSpringの部分で苦労しました。
値を画面に渡すやり方がしばらくわかりませんでした。。。
Springも使いこなせれば、楽に書けそうな気がします。基本から勉強してみたいと思います。
 
今回のサンプルはGitHub上に挙げています。
 
参考