2012年6月17日日曜日

エビデンス!エビデンス!!エビデンス!!! VBAでクリップボード監視

ひとつとってはSEのため
ふたつとっては客のため
みっつとっては・・・誰のため?

さぁテストだ、となると、
ブラウザで画面表示 → PrintScreen → Excelに貼り付け
を延々と繰り返して、テストのエビデンスをとるわけですよ。

まあ非生産的なこと、誰の役に立つのでしょう、しかも表計算のExcelがお絵かき帳だなんて、と思っても口には出さないわけですが、
仕事で作んなくちゃいけない、と決まっているので作るわけです。

にしても、もう少しましにならないか・・・。

たとえば、PrintScreenを押すと、勝手にExcelに貼りつけてくれる、とか。
と思って、「VBA クリップボード 監視」で検索してみると・・・
やっぱり先人はいるのですね、たとえばここ
(興味を持たれた方は、(1)~(4)まであるので、全部読むことをお勧めします)

あとはこの仕組みに、ワークシートへの図のペーストを入れればオシマイ!なのですが、
ついでにちょっと気になるところがあったので、そこをちょっといじってみたりしました。

こんな感じです。適当なExcelブックを作成して、標準モジュールのModule1に書きます。

これは、64bit版のWindows 7でExcel 2010を使ったのでこういう書き方になったのですが、そうでなければ、たとえばExcel 2003とかなら、

LongPtr → Long
上のほうでDeclare FunctionしているところのPtrSafeを削除

とすればOKです。というか、別に2010でも同じようにやっても多分動くと思います。LongPtr使ってみたかっただけ。
そして、ユーザフォームを作成します。フォームにはチェックボックスをひとつだけ。


こんな感じです。

コードはこうします。ユーザフォームの名前は"UserForm1"、チェックボックスの名前は"CheckBox1"にします。また、ユーザフォームはモードレス、つまり、ShowModalをFalseにしておきます。

さて、説明は後回しにして、先に使い方を言いますと、

1. ユーザフォームを実行するなりして、 ユーザフォームを表示します。
2. フォームの中のチェックボックスをクリックします。
3. PrintScreenやAlt+PrintScreenするなどして、とにかくクリップボードに図を入れます。
4. すると、そのブックの1シート目に、クリップボードに入れた図がどんどん貼りついていくではないですか!

さて、次回は、このコードの解説と、自分的に気になったポイントを書こうと思います。

2012年3月7日水曜日

Struts2 + Spring + mybatis! 準備ができたところでGitHubに上げてみた

ここまでで、アプリを作成する準備段階は整ったと思うので、せっかくだからと、GitHubというものにあげてみることにした。

mavenで作ったこのプロジェクト全体をどうすればよろしくアップできるのか分からなかったので、とりあえずsrc下のみをあげることにした。
URLはこちら。
https://github.com/ymotchi/TagDiary

ただ、準備段階だからと言って、まったく動かないソースもアレだったので、トランザクション管理がちゃん動くかのテストも兼ねて、超簡単な画面をつけてみた。

まず、それ用のテーブル「testtable」を用意しておく。

testtable
idtesttableint(11) PK
testcolumnvarchar(32)

そしてTomcatなどのサーバでアプリを起動させ、「http://localhost:ポート/コンテキストルート/index」にアクセス。

で、「」の欄に

1) 適当な文字列を入れた場合
2) 「throwable」と入れた場合
3) 「exception」と入れた場合

で、挙動が変わってくる。

1)の場合は、『「(入力した文字列)」が入力されました』と表示され、testtableに入力した文字列が書き込まれる。
2)の場合は、エラーが画面に表示され、testtableに「throwable」が書き込まれる。
3)の場合は、エラーが画面に表示され、testtableに何も書き込まれない。

これは、applicationContext.xmlのトランザクションの設定のところで、
「rollback-for="java.lang.Exception"」という記述をしたため。
2)の場合は、サービスクラスのメソッドでThrowableが投げられるが、これはExcpetionの親クラスなので、ロールバックはしない。
一方3)の場合は、Exceptionが投げられるため、ロールバックされる。

この設定の効果が分かりやすく表れた。

細かいところは、実際にソースを見ていただくのが良いと思う。


忘れてた・・・。
このソースは、Java SE 7(以上)じゃないと動かない。
新機能を使ってみたくて、文字列のswitch文を使ってみたから。

2012年2月27日月曜日

Struts2 + Spring + mybatis! jspの最初の部分を1つのファイルにまとめる

web.xmlの中で、こんな風に書いた。
  <jsp-config>
    <jsp-property-group>
      <url-pattern>*.jsp</url-pattern>
      <el-ignored>false</el-ignored>
      <page-encoding>UTF-8</page-encoding>
      <scripting-invalid>false</scripting-invalid>
      <include-prelude>/WEB-INF/include/jsp_header.jsp</include-prelude>
    </jsp-property-group>
  </jsp-config>

ここで、"include-prelude"とは、すべてのJSPファイルの先頭に挿入したいコードを記述したファイルを指定する。pageやtaglib宣言なんかにうってつけ。
そこで、 このファイルをこのようにした。

jsp_header.jsp
<%@ page contentType="text/html; UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="f" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
コンテンツタイプをtext/html、出力文字コードをUTF-8にする宣言と、あとはよく使うJSTLとStruts2のタグのtaglib宣言。これで、各JSPファイルにいちいち書いて回る必要がなくなる。

ちなみに、すべてのJSPの末尾に特定のコードを挿入したい場合は、同じようなノリで"include-coda"を使う。
"prelude"に"coda"。このネーミングセンスはよいなぁと思う。

2012年1月6日金曜日

Struts2 + Spring + mybatis! applicationContext.xmlに必要なあれこれ

という訳で、次はapplicationContext.xml。これは、web.xmlで指定したとおり、WEB-INFの下に置く。
<?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:jee="http://www.springframework.org/schema/jee"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/jee
      http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
      http://www.springframework.org/schema/tx
      http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.0.xsd"
 default-autowire="byName">

 <context:annotation-config/>
 <context:component-scan base-package="com.motchi.tagdiary"/>

 <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:3306/mytest?useUnicode=true&amp;characterEncoding=utf8</value>
  </property>
  <property name="username">
   <value>mytest</value>
  </property>
  <property name="password">
   <value>mytestpass</value>
  </property>
 </bean>

 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
 </bean>

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

 <aop:config proxy-target-class="true">
  <aop:advisor advice-ref="txAdvice" pointcut="@within(org.springframework.stereotype.Service)"/>
 </aop:config>
 <tx:advice id="txAdvice">
  <tx:attributes>
   <tx:method name="insert*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
   <tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
   <tx:method name="delete*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
   <tx:method name="*" propagation="REQUIRED" read-only="true"/>
  </tx:attributes>
 </tx:advice>

 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
  <property name="basePackage" value="com.motchi.tagdiary"/>
  <property name="annotationClass" value="com.motchi.tagdiary.common.annotation.SqlMapper"/>
 </bean>

</beans> 
まず、"@Resource"でいろいろインジェクトできるようにするため、context:annotation-configを使用。
そして、そのインジェクトするBeanも、やはりXMLではなくアノテーションを書くことでSpringに見つけてもらいたいので、context:component-scanも使った。

データベースの設定は、「bean id="dataSource"」のところにユーザ名やパスワードまでベタ書き。今回はDBにMySQLを使うので、ドライバはcom.mysql.jdbc.Driver。普通DBの設定は、外部のXMLファイルやpropertiesファイルに出すところだが、そんなのは後回し。まずはひと通り動くところまで作ってしまおう。

続く「bean id="sqlSessionFactory"」が、mybatisとSpringの架け橋。ここで先ほど定義したデータソースを使う。

そして、ここからが凝りがいのある(かもしれない)ところ。トランザクション境界の設定だ。
今回のアプリのDBアクセスは、こんな風にしたい。

・アクションは、直接DAOを触らず、必ずサービスを介する。
・サービスのメソッドをトランザクション境界とする。
んで、更新系のメソッドは、チェック例外だろうが非チェック例外だろうが、Exceptionの子クラスの例外が発生したらロールバックしたい。
メソッドが更新系かどうかは、メソッド名でわかるようにする。
それ以外のメソッドはSelectするだけ。
・DAOは、サービスから呼ばれて、ただただSQLを実行する。

まず、「bean id="transactionManager"」のところで、データソースを指定し、DataSourceTransactionManagerを作る。これは普通だと思われる。ちなみに、idを「transactionManager」にしておくと、Springが「あーはいはい、例のアレね」と認識してくれて、その下のトランザクションの設定で名前を出す必要がないらしい。

そして次に、aop:config。proxy-target-class="true"にしているのは、サービスはインタフェースではなくクラスにするから。
ああ分かるさ。オブジェクト指向的にはインタフェースの方が望ましいさ。
でもね、多態性を使うわけじゃなし、

HogeHogeServiceってインタフェースにHogeHogeServiceImplってクラスが、
または、
IHogeHogeServiceってインタフェースにHogeHogeServiceってクラスが、

どーせ一対一で対応すんでしょ!

いいじゃんクラス1個で。

で、クラスは「@Service」アノテーションでSpringに認識してもらうので、pointcutは「@within(org.springframework.stereotype.Service)」で決まり。これは、「@Serviceがついたクラスの中のメソッドが対象」という意味らしい。サービスは全部トランザクショナルになってもらいます。

更新系のメソッドは、安直だけど、プレフィックスにinsert, update, deleteを持つものとし、これらの中でExceptionが起こればロールバックをするようにする。これが「tx:advice id="txAdvice"」の中身。

最後の「bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"」で、DAOを自動的に探してもらう。
このMapperScannerConfigurerの使い方、ちょっとぐぐってみたところ、sqlSessionFactoryとbasePackageを指定する例しか出てこなかった。
まあ、普通はそれで事足りるよね。
DAOをどこかのパッケージに集めて、そのパッケージをbasePackageで指定すればいいんだから。
でもね。
パッケージが画面単位や機能単位に分かれていて、それぞれにDAOがあったりすることもあるじゃないですか。
っていう理由を置いといても、サービスを探す方法がアノテーションなんだから、DAOもアノテーションで探してみたい!という好奇心からAPIを調べると、・・・あるじゃないですか、
setAnnotationClass(Class<? extends Annotation> annotationClass)
なんてメソッドが。
そこで、DAOを識別するための俺々アノテーションSqlMapperを作成(中身は空っぽでOK)し、こいつをannotationClassに指定。basePackageはどどーんとルートパッケージにして完成。

SqlMapper.java
package com.motchi.tagdiary.common.annotation;

public @interface SqlMapper {

}
これで準備完了。あとはクラスやらJSPやらを書くだけだ。

2011年11月6日日曜日

Struts2 + Spring + mybatis! web.xmlとstruts.xml

できる限りXMLレスで行こうという今回のアプリ作成。
でも、やっぱり必要なXMLは書かなきゃね。

まずは、web.xml。当然。
<?xml version="1.0" encoding="UTF-8" ?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
 version="3.0">
 <display-name>TagDiary</display-name>

 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/applicationContext.xml</param-value>
 </context-param>

 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>

 <filter>
  <filter-name>struts2</filter-name>
  <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
 </filter>

 <filter-mapping>
  <filter-name>struts2</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>

 <jsp-config>
  <jsp-property-group>
   <url-pattern>*.jsp</url-pattern>
   <el-ignored>false</el-ignored>
   <page-encoding>UTF-8</page-encoding>
   <scripting-invalid>false</scripting-invalid>
   <include-prelude>/WEB-INF/include/jsp_header.jsp</include-prelude>
  </jsp-property-group>
 </jsp-config>
</web-app>
context-paramとlistenerがSpringのため。
filterとfilter-mappingがStruts2のため。

で、JSPではJSTLとStruts2のカスタムタグをよく使う予定なのですが、
いちいち<%@ taglib ... %>を書くのが嫌なので、
JSPに共通の宣言は、 jsp_header.jsp ファイルに押しやって、
そいつをinclude-prelude。
これはほんとうに便利だ・・・。

次は、struts.xml。これは、クラスパスが通っているところに置くと、勝手に読み込んでくれるらしいので便利。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
 "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
 "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
 <constant name="struts.objectFactory" value="org.apache.struts2.spring.StrutsSpringObjectFactory"/>
</struts>
org.apache.struts2.spring.StrutsSpringObjectFactoryを使わないと、ActionにサービスをInjectできないっぽい。
でも、基本的にアノテーションでやりたいので、struts.xmlには全然書きません。

次のエントリでは、Springとmybatisの設定であるところの、
applicationContext.xml を書くと思います。

2011年11月2日水曜日

Struts2 + Spring + mybatis! ライブラリをmavenで揃える

Struts2, Spring, mybatis。
これでアプリを作りたくなったのです。

以前、Sturts1 + Spring + iBATIS という、わりとありがちなフレームワークでアプリを作ったことがあったのですが、

やっぱりXML地獄は嫌だ・・・。

XMLそのものが嫌いではなく、いや、むしろ、XML自体は好きで、Ajaxなど通信で取り交わされるテキストはXMLがいいなーと思うのですが、
これらのフレームワークのXMLはどうも好きになれません。

これらのXMLの利点は、ある設定(アクションと遷移先だとか、Springが管理するBeanだとか、SQL文だとか)を一元的に管理するという利点があるとは思うのですが、
でも、まとめたいことって人それぞれなわけで、
「アクションのクラスとコードとそのアクションが関連する設定をひとつにまとめたい!」
という要望には、XMLは答えてくれないのです。

そこで、今回のやつらを使ってみようというわけです。

mybatisも、アノテーションにより、インタフェースとSQLががっつり結びつくので好きです。
ただ、動的SQLになるとXMLしかないので、実際にそれなりのアプリを作るなら、SQLはXMLに統一すべきですが。

作ってみるアプリは、まあお遊び的ですが、付箋をペタペタ貼る日記というものをWebアプリで作りたいと思います。
jQuery UIとか使って。

まずは、環境準備。

DBはMySQL。とってきてインストール。

次は、eclipseだ。現時点で最新っぽい3.7.1を持ってくる。

今まで全然使ったことなくて、さっぱりわからないけど、いろいろダウンロードするのが面倒なので、
mavenを使ってみる。
最新は3らしいので、そいつをダウンロード(現時点の最新は3.0.3)。
http://maven.apache.org/download.html

次の、m2eclipse・・・なんか、m2eという名前になったらしい。
eclipseから
http://download.eclipse.org/technology/m2e/releases
を使ってインストール。
で、Window →Preferences → Maven → Installations で、さっき入れたmaven3.0.3を指定。
してみたら、Embeddedのバージョンが3.0.2だった。あんま意味ないかなぁ。

しかし、これだけでは足りない。
Mavenプロジェクトを作成したときに、Webプロジェクトっぽくするために、WTP連携を入れる必要があるらしい。ここが、日本語のページをぐぐってもなかなか分からず、詰まったところ。
英語のページ読んでやっと分かった。
eclipseのWindow →Preferences → Maven → Discovery → Open Catalog
で、「m2e-wtp」をインストール。

そしてやっと、Mavenプロジェクトを作成。
Archetypeはmaven-archetype-webapp、Artifact Idは、今回のアプリ 「付箋をペタペタ貼る日記」ということで、「TagDiary」、Group Idは、ルートパッケージにしようと思っている、「com.motchi.tagdiary」で決定。

できたら、とりあえずライブラリを入れてしまいたいので、pom.xmlを編集。
こうなった。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.motchi.tagdiary</groupId>
  <artifactId>TagDiary</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>TagDiary Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
     <groupId>org.apache.struts</groupId>
     <artifactId>struts2-core</artifactId>
     <version>2.2.3.1</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-core</artifactId>
     <version>3.0.6.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.mybatis</groupId>
     <artifactId>mybatis</artifactId>
     <version>3.0.6</version>
    </dependency>
    <dependency>
     <groupId>org.mybatis</groupId>
     <artifactId>mybatis-spring</artifactId>
     <version>1.0.2</version>
    </dependency>
    <dependency>
     <groupId>org.apache.struts</groupId>
     <artifactId>struts2-convention-plugin</artifactId>
     <version>2.2.3.1</version>
    </dependency>
    <dependency>
     <groupId>jstl</groupId>
     <artifactId>jstl</artifactId>
     <version>1.2</version>
    </dependency>
    <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.18</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-web</artifactId>
     <version>3.0.6.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>1.6.11</version>
    </dependency>
    <dependency>
     <groupId>cglib</groupId>
     <artifactId>cglib</artifactId>
     <version>2.2.2</version>
    </dependency>
    <dependency>
     <groupId>org.apache.struts</groupId>
     <artifactId>struts2-spring-plugin</artifactId>
     <version>2.2.3.1</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>TagDiary</finalName>
  </build>
</project>
中身としては、
strus2, spring, mybatisを持ってきているのは当然として、

mybatis-spring名前の通り、mybatisとspringを連携させるため
struts2-convention-pluginstruts2のアクションの設定をアノテーションでできるようにするため
jstljspでJSTLを使うため。<c:forEach>とかよく使いそうだから
mysql-connector-javaDBにMySQLを使うため
aspectjweaver, cglibAspectJを利用してAOPしまくるため
struts2-spring-pluginstruts2とspringを連携させるため

後のものは・・・記憶にございません。思い出したら書いてみます。

長くなったので、続きはエントリを改めて。