課題管理システムの勘所(3)

しばらく間が開いてしまったが。いよいよログインアラートのご紹介。

下記のようなJellyスクリプトを用意し、JIRAが動いているサーバの適当なディレクトリ(JIRAのプロセスからアクセスできる所)に配置する。/var/lib/jira/jelly とかで良いんじゃないかな。

ただし、最初の設定の部分は、環境にあわせて適宜修正すること。

<JiraJelly
      xmlns:jira="jelly:com.atlassian.jira.jelly.enterprise.JiraTagLib"
      xmlns:core="jelly:core"
      xmlns:email="jelly:email">
<!--
Copyright 2009 Miyamoto Daisuke (dai.0304_at_gmail.com).

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language
governing permissions and limitations under the License.
-->
  <!-- ◆◆◆◆設定 -->
  <core:set var="expDays1" value="3"/><!-- expDays1 : この日数の間ログインしなかったら、毎日メール通知(1〜) -->
  <core:set var="expDays2" value="4"/><!-- expDays2 : この日数の間ログインしなかったら、1時間おきにメール通知(1〜) -->
  <core:set var="emailFrom" value="jira@example.com"/><!-- emailFrom : メールヘッダのFromアドレス -->
  <core:set var="emailSubject" value="[EXAMPLE-JIRA] ログインアラート"/><!-- emailSubject : メールヘッダのSubject -->
  <core:set var="emailServer" value="localhost"/><!-- emailServer : SMTPサーバのホスト名 -->
  <core:set var="signature"><!-- signature : メールの最後につけるシグネチャ -->
----
EXAMPLE-JIRA
http://jira.example.com/jira/
  </core:set>
  <!-- ◆◆◆◆設定おわり -->
  
  
  <core:getStatic var="dateField" className="java.util.Calendar" field="DATE"/>
  <core:getStatic var="hourOfDayField" className="java.util.Calendar" field="HOUR_OF_DAY"/>
  
  <!-- ◆◆◆◆時刻の処理 -->
  <!-- Calendar exp1 = Calendar.getInstance(); -->
  <core:invokeStatic className="java.util.Calendar" method="getInstance" var="exp1"/>
  <!-- exp1.add(Calendar.DATE, -1 * expDays1); -->
  <core:invoke on="${exp1}" method="add">
    <core:arg type="int" value="${dateField}"/>
    <core:arg type="int" value="${-1 * expDays1}"/>
  </core:invoke>
  
  <!-- Calendar exp2 = Calendar.getInstance(); -->
  <core:invokeStatic className="java.util.Calendar" method="getInstance" var="exp2"/>
  <!-- exp2.add(Calendar.DATE, -1 * expDays2); -->
  <core:invoke on="${exp2}" method="add">
    <core:arg type="int" value="${dateField}"/>
    <core:arg type="int" value="${-1 * expDays2}"/>
  </core:invoke>
  
  <!-- Calendar now = Calendar.getInstance(); -->
  <core:invokeStatic className="java.util.Calendar" method="getInstance" var="now"/>
  
  
  <!-- ◆◆◆◆LoginManagerの取得 -->
  <!-- Class clazz = Class.forName("com.atlassian.jira.security.login.LoginManager"); -->
  <core:invokeStatic className="java.lang.Class" method="forName" var="clazz">
    <core:arg type="java.lang.String" value="com.atlassian.jira.security.login.LoginManager"/>
  </core:invokeStatic>
  
  <!-- LoginManager loginManager = ComponentManager.getComponentInstanceOfType(clazz); -->
  <core:invokeStatic className="com.atlassian.jira.ComponentManager" method="getComponentInstanceOfType" var="loginManager">
    <core:arg type="java.lang.Class" value="${clazz}"/>
  </core:invokeStatic>
  
  
  <!-- ◆◆◆◆UserManagerの取得 -->
  <!-- UserManager userManager = UserManager.getInstance(); -->
  <core:invokeStatic className="com.opensymphony.user.UserManager" method="getInstance" var="userManager"/>
  
  <!-- List<User> users = userManager.getUsers(); -->
  <core:invoke on="${userManager}" method="getUsers" var="users"/>
  
  
  <!-- ◆◆◆◆User毎のループ -->
  <!-- for (User user : users) { -->
  <core:forEach var="user" items="${users}">
    <!-- ◆◆◆◆ログイン情報の取得 -->
    <!-- LoginInfo loginInfo = loginManager.getLoginInfo(user); -->
    <core:invoke on="${loginManager}" method="getLoginInfo" var="loginInfo">
      <core:arg type="com.opensymphony.user.User" value="${user}"/>
    </core:invoke>
    
    <!-- Long lastLoginTimeLong = loginInfo.getLastLoginTime(); -->
    <core:invoke on="${loginInfo}" method="getLastLoginTime" var="lastLoginTimeLong"/>
    
    <core:set var="send1" value="false"/>
    <core:set var="send2" value="false"/>
    <core:set var="send3" value="false"/>
    
    <!-- ◆◆◆◆未ログインであれば、メール送信する -->
    <core:if test="${lastLoginTimeLong == null}">
      <core:set var="send1" value="true"/>
      <core:set var="diff1" value="-1"/>
      <core:set var="diff2" value="-1"/>
    </core:if>
    <core:if test="${lastLoginTimeLong != null}">
      <!-- Calendar lastLoginTime = Calendar.getInstance(); -->
      <core:invokeStatic className="java.util.Calendar" method="getInstance" var="lastLoginTime"/>
      
      <!-- lastLoginTime.setTimeInMillis(lastLoginTimeLong); -->
      <core:invoke on="${lastLoginTime}" method="setTimeInMillis">
        <core:arg type="long" value="${lastLoginTimeLong.longValue()}"/>
      </core:invoke>
      
      <core:set var="diff1" value="${exp1.getTimeInMillis() - lastLoginTimeLong.longValue()}"/>
      <core:set var="diff2" value="${exp2.getTimeInMillis() - lastLoginTimeLong.longValue()}"/>
      
      <!-- ◆◆◆◆expDays2超過であれば、メール送信する -->
      <!-- if (diff2) > 0) send2 = true; -->
      <core:if test="${diff2 > 0}">
        <core:set var="send2" value="true"/>
      </core:if>
      
      <!-- ◆◆◆◆expDays1超過で、かつ、超過量が1時間以内であれば、メール送信する -->
      <!-- if (diff1 > 0 && (diff1 % 86400000) < 3600000) send3 = true; -->
      <core:if test="${diff1 > 0 &amp;&amp; (diff1 % 86400000) &lt; 3600000}">
        <core:set var="send3" value="true"/>
      </core:if>
    </core:if>
    
    <!-- ◆◆◆◆メール送信処理 -->
    <core:set var="send" value="${send1 || send2 || send3}"/>
    <!-- if (send || debug) { -->
    <core:if test="${send}">
      <core:set var="emailTo" value="${user.email}"/>
      <core:if test="${debug}">
        <core:set var="debugStr">

--- debug information
lastLoginTimeLong : ${lastLoginTimeLong}
send1 : ${send1}

exp1 : ${exp1.getTime().toString()}
diff1 : ${diff1}
send2 : ${send2}

exp2 : ${exp2.getTime().toString()}
diff2 : ${diff2} # ${diff2 % 86400000}
send3 : ${send3}

send : ${send}
emailTo : ${emailTo}
        </core:set>
        <core:set var="emailTo" value="admin_of_jira@example.com"/>
      </core:if>
      <core:set var="emailMessage">
${user.name} さん

${expDays1}日間以上、JIRAにログインしていないのでお知らせします。
なるべく頻繁に、自分が担当するチケットを確認するようにしましょう。

Last Login: ${lastLoginTime.getTime().toString()}

${signature}
${debugStr}
      </core:set>
      <email:email
          server="${emailServer}"
          from="${emailFrom}"
          to="${emailTo}"
          subject="${emailSubject} ${now.getTime().toString()}"
          message="${emailMessage}"/>
    </core:if>
    <!-- } /* if */ -->
  </core:forEach>
  <!-- } /* for */ -->
</JiraJelly>

上記スクリプトは、表示の通りApache License 2.0でオープンソースとします。ライセンスの範囲内でご自由にカスタマイズして使ってください。

一応、Jellyタグの前に、それに対応するJavaコードを対訳っぽくつけてみた。実際にコンパイルしてる訳じゃないので、ミスってるかもしれないけど。あと、IDEを使ったりリファクタリングとかかましてないので、意外と稚拙なロジックですまんです。デバッグコードとかも残ってる。

そんな訳で、まぁこのファイルをサーバに配置する。次の手順はこれ。

管理メニューのシステム>サービスを選択し、図のように設定して「サービスの追加」をクリック。

さらに設定があるので、「Jellyスクリプトのパス」と「結果ログの出力先ファイル」を指定して完了である。これで、1時間に1度このスクリプトが周り、最終ログインから一定時間経過している人にメールが飛ぶ。