backlog4jで課題を登録してみる

今度は、backlogへの課題登録を自動化したいという話があり、前回のredmineと似通った要件なので再利用箇所も多く、redmine java api部分をヌーラボ公認のbacklog4jに置き換えて実装してみました。
redmineとの違いはそれなりにありますが、backlog4jの方はKeyとIDを混同しやすい感じがします。例えば、課題のKeyはURLを見ればすぐ解りますが、IDの方は内部的なユニークな数値です。これに注意しながらbacklog用に前回のラッパークラスを置き換えてみました。

import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import com.nulabinc.backlog4j.BacklogClient;
import com.nulabinc.backlog4j.BacklogClientFactory;
import com.nulabinc.backlog4j.Category;
import com.nulabinc.backlog4j.Issue;
import com.nulabinc.backlog4j.IssueComment;
import com.nulabinc.backlog4j.IssueType;
import com.nulabinc.backlog4j.Project;
import com.nulabinc.backlog4j.Status;
import com.nulabinc.backlog4j.api.option.AddIssueCommentParams;
import com.nulabinc.backlog4j.api.option.CreateIssueParams;
import com.nulabinc.backlog4j.api.option.GetIssuesParams;
import com.nulabinc.backlog4j.api.option.UpdateIssueParams;
import com.nulabinc.backlog4j.conf.BacklogConfigure;
import com.nulabinc.backlog4j.conf.BacklogJpConfigure;
import com.nulabinc.backlog4j.conf.BacklogPackageConfigure;

public class BacklogRegist {
	
	private BacklogClient backlog;
		
	public BacklogRegist(String url, String apikey) throws MalformedURLException  {
		
		BacklogConfigure conf = null;
		//オンプレ環境
		if(url.startsWith("http")){
			conf = new BacklogPackageConfigure(url);
		}
		//クラウド
		else{
			conf = new BacklogJpConfigure(url);
		}
		conf.apiKey(apikey);
 		backlog = new BacklogClientFactory(conf).newClient();
				
		
	}
	/**
	 * 課題を取得する
	 * @param issueKey
	 * */
	public Issue getIssue(String issueKey){
		
		if(issueKey == null){
			return null;
		}
		return getIssue = backlog.getIssue(issueKey);
	}
	/**
	 * 課題を作成する
	 * @param projectKey
	 * @param  issueTypeName 種別名称
	 * @param categoryName カテゴリ名称
	 * @param summary 件名
	 * @param description 詳細
	 * @return ticketID 0は登録失敗
	 * */
	public long regist(String projectKey, String issueTypeName, String categoryName, 
						String summary, String description){
		
		CreateIssueParams param = new CreateIssueParams(
				getProjectID(projectKey), summary,
				getIssueTypeId(projectKey, issueTypeName), Issue.PriorityType.Normal);
		
		param.description(description);
		
		if(StringUtils.isNotEmpty(categoryName)){
			List<String> categoryIds = new ArrayList<String>();
			categoryIds.add(getCategoryId(projectKey, categoryName));		
			param.categoryIds(categoryIds);
		}
		
		Issue issue = backlog.createIssue(param);
		if(issue != null){
			return issue.getId();
		}
		return 0;
	}
	
	/**
	 * 課題を更新する(コメントの追加も可能)
	 * @param projectKey プロジェクトKey
	 * @param  issueIdorKey 課題IDまたはKey 
	 * @param status ステータス名称(更新しなければセットしない)
	 * @param addComment  更新なければセットしない
	 * @return commentID 0は登録失敗
	 * */
	public long update(String issueIdorKey, String status, String addComment){
		long ret = 0;
		Issue issue = null;
		
		if(StringUtils.isNotEmpty(status)){
			UpdateIssueParams param = new UpdateIssueParams(issueIdorKey);
			for( Status Entity: backlog.getStatuses()){
				if( Entity.getName().equals(status)){
					param.status(Entity.getStatusType());
				}
			}
			issue = backlog.updateIssue(param);
		}
		else{
			issue = backlog.getIssue(issueIdorKey);
		}
		if(StringUtils.isNotEmpty(addComment)){
			AddIssueCommentParams params = new AddIssueCommentParams(issue.getIssueKey(), addComment);

			IssueComment cmt = backlog.addIssueComment(params);
			ret = cmt.getId();
		}
		else{
			ret = issue.getId();
		}
		return ret;
	}

	/**
	 * 課題にコメントを追加する
	 * @param projectKey
	 * @param  issueKey
	 * @param addComment
	 * @return commentID 0は登録失敗
	 * */
	public long addComment(String issueKey, String addComment){
		Issue issue = backlog.getIssue(issueKey);
		
		if( issue != null){
			AddIssueCommentParams params = new AddIssueCommentParams(issue.getIssueKey(), addComment);

			IssueComment cmt = backlog.addIssueComment(params);
			return cmt.getId();
			
		}
		return 0;
	}

	/**
	 * 課題のタイプ(種別)を取得する
	 * @param prjKey プロジェクトキー
	 * @param name 種別名称
	 * */
	public String getIssueTypeId(String projectKey, String name){
		for( IssueType type: backlog.getIssueTypes(projectKey)){
			if( type.getName().equals(name)){
				return type.getIdAsString();
			}
		}
		return null;
	}
	
	/**
	 * カテゴリを取得する
	 * @param prjKey プロジェクトキー
	 * @param name 種別名称
	 * */
	public String getCategoryId(String projectKey, String name){
		for( Category type: backlog.getCategories(projectKey)){
			if( type.getName().equals(name)){
				return type.getIdAsString();
			}
		}
		return null;
	}
	/**
	 * プロジェクトIDを取得する
	 * @param prjKey プロジェクトキー
	 * @return Projectid
	 * */
	public String getProjectID(String projectKey){
		for( Project type: backlog.getProjects()){
			if( type.getProjectKey().equals(projectKey)){
				return type.getIdAsString();
			}
		}
		return null;
	}
}

上記を呼び出すのはこんな感じ

BacklogRegist backlog = new BacklogRegist("{URLまたはスペースID}","{apiKey}");

//チケット取得
Issue issue = backlog.getIssue("PROJECT-3");

//チケット登録
long issueId = backlog.regist("PROJECT", "要望", "設定変更",  
	"ユーザーの追加について", "○○さんを追加してください");

後日談
GetIssieで3ヶ月経って問題発生・・・たまに課題の取得に失敗するようになりました・・・
下記のようにしましたが、最初からそうしろ!って事ですね。でも、3ヶ月は何も問題なかったのですが・・
public Issue getIssue(String issueKey)

Issue getIssue = backlog.getIssue(issueKey);

redmine java api でチケットを取得してみる

 前回の続きで、今度はチケットを取得してみます。単純にチケットIDを指定して一つのチケットを取得するのでは無く、条件を指定し一括してチケットを取得して何かをするような要件への対応です。

 下記サンプルでは、クエリーを使用せずに、
ステータスID=2 かつ トラッカーID=10または11 かつ 題名に「テスト」を含む
という条件に一致するチケットを全て取得する内容です。
100件ずつチケットを取得して、100件以上のチケットがあればページ番号を変えて全チケットを取得します。

String apikey = "APIキー値";
String uri = "redmineサイトURI";

String TICKET_LIMIT = "100";

Map<String,String> params = new HashMap<String,String>();

// リミット
params.put("limit",TICKET_LIMIT);

params.put("status_id","2");

params.put("tracker_id","10|11");
//題名に「テスト」を含む
params.put("subject", "~テスト");

RedmineManager mgr = RedmineManagerFactory.createWithApiKey(uri, apikey);

boolean iscontinue = true;
int page = 1;
while(iscontinue){
	
	List<Issue> issues = mgr.getIssueManager().getIssues(params);
	
	for(Issue issue : issues){
		System.out.println(issue.getSubject());
	}
	if(issues.size() < Integer.valueOf(TICKET_LIMIT)){
		iscontinue = false;
	}
	else{
		page++;
		params.put("page",String.valueOf(page));
	}
}

redmine java api でチケットを登録してみる

 redmineへのチケット登録を自動化したいという話があり、内容的には常時トリガーを拾って登録という感じだったので、javaの常駐プロセスでトリガーを拾う事を前提として、こちらを使用して実装してみました。
https://github.com/taskadapter/redmine-java-api
なお、登録対象のredmineは2.5系でしたが、それほどバージョンを意識しなくても大丈夫そうです。

事前準備として、対象のredmineへapi登録用のユーザーを用意し、登録対象のプロジェクトへの権限を付与、個人設定画面からapiキーを取得します。

その他、プロジェクトidやトラッカーid等のredmine内部で持っているidの値はブラウザからapiで下記のようにidの値を確認してセットとなります。ただ、redmine java apiには、様々なマネージャーがあるので、id指定でなくても都度問い合わせしてidを取得できそうです。

ソース的には下記のようなラッパークラスを作って、登録や更新を呼び出し側で簡略化出来るようにしてます。

package jp.eosoro.redmine;

import java.util.ArrayList;
import java.util.List;

import com.taskadapter.redmineapi.Include;
import com.taskadapter.redmineapi.RedmineException;
import com.taskadapter.redmineapi.RedmineManager;
import com.taskadapter.redmineapi.RedmineManagerFactory;
import com.taskadapter.redmineapi.bean.CustomField;
import com.taskadapter.redmineapi.bean.Issue;
import com.taskadapter.redmineapi.bean.ProjectFactory;
import com.taskadapter.redmineapi.bean.TrackerFactory;
import com.taskadapter.redmineapi.bean.User;
import com.taskadapter.redmineapi.bean.UserFactory;

/**
 * Redmine登録クラス
 * */
public class RedmineRegist {

	RedmineManager mgr;
	
	private Issue issue;
	private List<CustomField> customeField;
	
	/**
	 * コンストラクタ
	 * @param String RedmineサイトURI
	 * @param String APIキー
	 */
	public RedmineRegist(String uri, String apiAccessKey) {
		mgr = RedmineManagerFactory.createWithApiKey(uri, apiAccessKey);

		issue = new Issue();
		customeField = new ArrayList<CustomField>();		
	}
	/**
	 * チケット登録
	 * @param subject タイトル
	 * @param description 詳細
	 * @param assigned_toId 担当者を指定する時はユーザーid 指定しない場合は0
	 * @param assigned_toGroupId グループを指定するときはグループid 指定しない場合は0
	 * @param parentid 親チケットを指定する時はチケットid 指定しない場合は0
	 * @param projectId
	 * @param trackerId 
	 * @param priorityId
	 * @param statusId
	 * @return int 登録チケットID 登録失敗時はゼロ
	 * */ 
	public int regist(String subject, String description, int assigned_toId, int assigned_toGroupId, int parentid, int projectId, int trackerId, int priorityId, int statusId){
		
		getIssue().setSubject(subject);
		
		getIssue().setDescription(description);

		//プロジェクト指定
		getIssue().setProject(ProjectFactory.create(projectId));

		//トラッカー指定
		getIssue().setTracker(TrackerFactory.create(trackerId));
		
		//ステータス/優先度指定
		getIssue().setPriorityId(priorityId);
		getIssue().setStatusId(statusId);
		
		// カスタムフィールド設定
		getIssue().addCustomFields(getCustomeField());
		
		try {
			//担当者指定
			if(assigned_toId > 0){
				getIssue().setAssignee(getMgr().getUserManager().getUserById(assigned_toId));
				
			}
			//グループ
			else if(assigned_toGroupId > 0){
				User u = UserFactory.create(assigned_toGroupId);
				getIssue().setAssignee(u);
			}
			//親チケット
			if(parentid > 0){
				getIssue().setParentId(parentid);
			}
			return getMgr().getIssueManager().createIssue(getIssue()).getId();

		} catch (RedmineException e) {
			return 0;
		}
	}
	/**
	 * 関連するチケットをセットする
	 * @param int チケットID
	 * @throws RedmineException 
	 * */
	public void setRelation(int id, int relateid) throws RedmineException{
		getMgr().getIssueManager().createRelation(id, relateid, "relates");
		
	}
	/**
	 * ジャーナル追加
	 * @param id チケットID
	 * @param Notes コメント
	 * @throws RedmineException 
	 * */
	public int addJournal(int id, String Notes) throws RedmineException{
		Issue issue =  getMgr().getIssueManager().getIssueById(id, Include.journals);
		
		issue.setNotes(Notes);

		getMgr().getIssueManager().update(issue);

		return issue.getId();
	}

	public RedmineManager getMgr() {
		return mgr;
	}
	private Issue getIssue() {
		return issue;
	}
	public List<CustomField> getCustomeField() {
		return customeField;
	}
	public void setCustomeField(List<CustomField> customeField) {
		this.customeField = customeField;
	}
}

下記の呼び出し側では、親チケットを作成してジャーナルを追加、子チケットを2つ作成し関連付けてます。

       RedmineRegist  mine = new RedmineRegist (url, apikey);
       
       int parentId = mine.regist("親チケット", "親チケット本文", 0, 0, 0, 4, 4, 2, 1);
       
       mine.addJournal(parentId, "ジャーナル");

       int childId = mine.regist("子チケット", "子チケット本文",  0, 0, parentId, 4, 4, 2, 1);
       
       int relationId = mine.regist("関連チケット", "関連チケット本文",  0, 0, parentId, 4, 4, 2, 1);
       
       
       mine.setRelation(childId, relationId);

いろいろやってみましたが、redmineに対して殆どの事は出来そうです。

JAX-RSでWeb画面にドラッグアンドドロップされたファイルを読み込む

テキストファイルをサーバへアップロードしたいという要件があり、Web画面からファイルをドラッグアンドドロップできるようにして、それをJAX-RSで処理する事にしてみました。

下記の参考にさせて頂き、それらを組合わせただけと言えばだけですが。。

Jersey(JAX-RS)でファイルアップロード
HTML5 の File API でドラッグ&ドロップする

まず、JAX-RSのルートパスを指定します

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/api")
public class RestApplication extends Application {
//何も書く事はありません・・・
}

次にファイルを処理するJAX-RS部分を作ります

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Stream;

import javax.enterprise.context.RequestScoped;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.jersey.multipart.FormDataParam;

@RequestScoped
@Path("/upload")
public class FileUploader {
	
	@POST
	@Consumes(MediaType.MULTIPART_FORM_DATA)
	public Response post(@FormDataParam("file") InputStream fileStream,
					@FormDataParam("file") FormDataContentDisposition fileDisposition) {

		int statusCode = 200;
		String out = "";

		//テキストファイルの読み込み
		try (BufferedReader br = new BufferedReader(new InputStreamReader(fileStream))) {
			try(Stream<String> lines = br.lines()){
				//1行毎の処理は省略
			}
        } catch (IOException e) {
			statusCode = 400;
			out = e.getMessage();
		}
		return Response.status(statusCode).type("text/html;charset=Shift-JIS").
			entity(out).
			build();
	}
}

これでファイルアップローダーのJAX-RSパスは、{コンテンツルート}/api/uploadになりました。

続いてJavaScriptの部分(殆ど上記参考から持ってきただけです。。)

$(function() {
   var droppable = $("#droppable");

    // イベントをキャンセルするハンドラです.
    var cancelEvent = function(event) {
        event.preventDefault();
        event.stopPropagation();
        return false;
    }

    // dragenter, dragover イベントのデフォルト処理をキャンセルします.
    droppable.bind("dragenter", cancelEvent);
    droppable.bind("dragover", cancelEvent);

    // ドロップ時のイベントハンドラを設定します.
    var handleDroppedFile = function(event) {

	    var dropfile = event.originalEvent.dataTransfer.files[0];
	    
	    var formData = new FormData();
	    formData.append( 'file', dropfile );
	      
	    var hostUrl= 'api/upload';
	    $.ajax({
	       url: hostUrl,
	       method: 'post',
	       dataType: 'json',
	       data: formData,
	       processData: false,
	       contentType: false,
	       timeout:100000
	    }).done(function(data) {
        	alert( '正常に終了しました');
	    }).fail(function( jqXHR, textStatus, errorThrown ) {
	         alert( 'エラーが発生しました\n'+ jqXHR.responseText);
	    });
	
	    // デフォルトの処理をキャンセルします.
	    cancelEvent(event);
	    return false;
    }
    // ドロップ時のイベントハンドラを設定します.
    droppable.bind("drop", handleDroppedFile);
});

最後にHTML部分のドラッグアンドドロップ部分です。
ここにドロップされたファイルがJAX-RSの処理箇所のInputStream に繋がります。

<div class="droppable" id="droppable" style="border:gray solid 1em; width:100px; padding:2em;">アップロードするファイルはココにドロップしてください。</div>      

これでとりあえずはJAX-RSでファイル処理が出来ることが確認出来ました。

最後にPOM。

<dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.sun.jersey.contribs/jersey-multipart -->
<dependency>
    <groupId>com.sun.jersey.contribs</groupId>
    <artifactId>jersey-multipart</artifactId>
    <version>1.19.3</version>
</dependency>

bootstrap2 datetimepickerを1分単位にする

ちょっと前に作ったJavaEEアプリでbootstrap2のdatatimepickerを使ったのだが、
?p=698
5分刻みは使いにくいという話が出てきて、ググッてみたところ下記を発見。
http://www.malot.fr/bootstrap-datetimepicker/
オプションのminuteStepで値を変えられるみたいなのでやってみました。
1分刻みにするとこうなります。

xhtml側は下記minuteStepのオプションを追加するだけ

<h:outputScript name="bootstrap/js/bootstrap-datetimepicker.min.js" />
<h:outputScript name="bootstrap/js/bootstrap-datetimepicker.ja.js" />
<h:outputStylesheet name="bootstrap/css/bootstrap-datetimepicker.min.css" />

$(function() {
  $('.datetimepicker').datetimepicker({
    format: 'yyyy/mm/dd hh:ii:ss',
    autoclose: true,
    todayBtn: true,
    pickerPosition: "bottom-left",
    minuteStep: 1,  
    language: 'ja'
   });
});

デフォルトだと5分刻みなのでこんな感じ

ダイアログが長くなって画面ギリギリだけど、この方が便利ということでこちらでリリース
次作るものはbootstrap3にしないとなあ。。

OracleDBで文字化け発生

今構築中のPHP+apache+OracleDB構成のWeb業務アプリですが、
テスト環境を作ってもらったので動かしてみたところ、全角文字が??に化けて困ったことに。。

アプリケーションの要件上、2つのOracleDB(どちらも12c)に同時接続する必要があり、
一方の文字コードが
NLS_LANG=Japanese_Japan.JA16SJISTILDE で、
もう一方が、
NLS_LANG=Japanese_Japan.AL32UTF8 となっています。

とは言え、DBクライアントとなるphp側としては、Japanese_Japan.AL32UTF8でいいはず。

apacheのhttpd.confを見ると、
setEnv NLS_LANG Japanese_Japan.AL32UTF8
としているのですが、どうも有効にならない模様。

結果、apacheの起動シェルに
export NLS_LANG=Japanese_Japan.AL32UTF8
を入れてもらって解消。
念の為、はしご高とかを画面から入れてみましたが問題無し。

昔(Oracle8とか)は第2水準以上の漢字はトラブルの元だったとの記憶がありますが、今は便利になりましたね。

POIでエクセルファイルを読むにはStreamingReaderが必須っPOI

エクセルファイル内のシートをDBに取り込む要件があり、DBサーバであるlinux上で取り込む処理をする必要が出てきました。
とりあえずCSVにでも変換してしまえば後はどうにでもなるので、Apache POIを使って汎用で使えそうな簡易CSV変換ツールを作ってみました。簡単にテストした後、いざ対象となるエクセルファイルで実行してみたところいつまでたっても終わらないし、メモリを数G食うという異常な状態に。
 テストした時のエクセルは数千程度で、いざ実行したエクセルは数万規模の違いがあり、大量データが含まれるエクセルシートの場合にPOIでは問題があるみたいです。で、こりゃ何とかしないとと調べていくと、それを解消してくれる素晴らしいライブラリを発見。
https://github.com/monitorjbl/excel-streaming-reader

上記サイトのREADMEに従い、下記のようにWorkbookFactory.createの箇所を置き換えるのみでした。(inputはエクセルファイルのInputStream)

        Workbook wb;
		try {
//POI標準ではダメ			wb = WorkbookFactory.create(input);
			wb = StreamingReader.builder()
			        .rowCacheSize(1000)    
			        .bufferSize(4096)     
			        .open(input);
・・・以下略

変えた後は何事もなかったかのようにちゃんと終わりました。
それにしてもPOIは非推奨メソッドをいつ置き換えてくれるのだろう?

追伸
sheet.getNumMergedRegions() 等、サポートしていないメソッドが多数あるようです。
結合セルなどを意識しないで、単純にエクセルファイルを行毎に順次読み取るような処理には適してます。

カテゴリー: Java

EJBタイマーサービスを使ってみることにした

WebLogicを動かしているサーバ上で数分間隔にとある機能を動作させたいという話があり、常駐プロセス起動とかにしたくないので、EJBタイマーサービスを使ってみることにしました。
タイマーサービスの使用は初めてなので、WebLogic12Cの挙動を調べながらの作りこみです。

結果的に下記のようになりました。
5分間隔(0分2秒,5分2秒,10分2秒,15分2秒・・・)でタイマー処理を実行しています。
なお、トランザクション処理は必要無し、処理結果のエラーハンドリングも不要、指定の間隔で単純に起動されればよいような機能です。

@Singleton
public class ChkTimer{
	@Schedule(hour="*", minute="*/5", second="2", persistent=false)
	public void chkProc(){
		ExecutorService service = Executors.newSingleThreadExecutor();
				
		service.submit(new Runnable() {
			@Override
			public void run() {
				//ここに処理を書く
			}
		}
	}
	@Timeout
	public void ChkTimeout(){
		//何もしないけど・・・
	}
}

WebLogicにおけるEJBタイマーの挙動として確認できたのは、

①タイムアウトを実装しておかないと30秒以上経つとエラーを吐いて、次のタイマーまでは動くのだが、その次以降はまったく動かなくなる

Caused By: weblogic.transaction.internal.TimedOutException: Transaction timed out after 30 seconds
中略・・
Truncated. see log file for complete stacktrace

なぜ30秒なのかというとトランザクションタイムアウト値がそうなっているっぽい。。
https://docs.oracle.com/cd/E28613_01/web.1211/b65951/ejb_jar_ref.htm#i1506703

何をするわけでもないが、とりあえず@Timeoutのアノテーションをつけたメソッドを追加

②処理時間が次回のタイマー処理開始を超えるとタイマーが止まる

例えば、5分間隔で動作させる場合、処理が5分以上掛かってしまうと、次のタイマー実行時にエラーが吐かれて動かない

<2016/11/15 16時55分07秒 JST> <Error> <EJB> <BEA-011088> <The following error occurred while invoking the ejbTimeout(javax.ejb.Timer) method of EJB ChkTimer(Application: appmgr, EJBComponent: /appmgr).
以下、略・・

なので、処理自体をExecutorServiceでスレッド処理にし、処理遅延が起きないようにしてみました

③永続化はしない方がよい

とりあえずサーバ上で空回りするようタイマー稼動テストを数日間したところ、ちゃんと指定間隔通りに動作していたので、機能を概ね実装していざデプロイしてみると、2重起動している状態に、、
なぜかといろいろ調べた結果、空回り開始時にpersistent=falseを指定していなかったので、ずっと残り続けていた模様。
一度、タイマー起動を無しにしたモジュールでデプロイした後、再度タイマーを有効にしてデプロイすることで、やっと2重起動が止まりました。

実行したいスケジュールや処理内容によっては、結構制御が難しいように思いますが、今回の要件ならOKという事で。

Oracle DB のダンプファイルがインポート出来ない

 今やってる案件で、別ベンダーさん側がメイン部分を作って筆者はその周辺を作っているのですが、そろそろデータが欲しくなったので提供を依頼したところ、ダンプファイルが送られてきました。
 さっそく開発端末のOracleDB11Expressにインポートしてみようとしたところ
>imp
Import: Release 11.2.0.2.0 – Production on 火 10月 4 10:48:15 2016
・・・中略

IMP-00401: ダンプ・ファイル”data_20160928.dmp”
は、データ・ポンプ・エクスポート・ダンプ・ファイルの場合があります
IMP-00000: エラーが発生したためインポートを終了します。

ん?何でかダメみたいとググっていくと、どうやら今時はimpdpを使うらしい。。
筆者はOracle8から9辺りまでは実際の開発に従事してましたが、10以降は社内SEであまり具体的な作業をやる機会が無かったので知りませんでした。

で、impdpとやらでインポートしてみます。
>impdp
・・・中略

接続先: Oracle Database 11g Express Edition Release 11.2.0.2.0 – Production
ORA-39006: 内部エラーが発生しました。
ORA-39065: DISPATCHでマスター・プロセスの予期しない例外が発生しました
ORA-04063: package body “SYS.DBMS_METADATA_UTIL”にエラーがあります。
ORA-06508: PL/SQL: コールしているプログラム単位が見つかりませんでした: “SYS.DBMS
_METADATA_UTIL”

ORA-39097: データ・ポンプ・ジョブで予期しないエラー-6508が見つかりました

あれ?パッケージが無いの?
じゃあ、探そうとOracleインストールディレクトリより、GREPで上記のUTILを探すと下記を発見。
早速実行

>sqlplus sys/**** as sysdba

@C:\oraclexe\app\oracle\product\11.2.0\server\rdbms\admin\dbmsmetu.sql

続けてINVALIDのパッケージをしつこくコンパイルしたり、データ・ディクショナリ系SQLを実行したり・・・
http://otndnld.oracle.co.jp/document/products/oracle11g/111/doc_dvd/server.111/E05771-04/scripts.htm

気を取り直して再度impdp実行
>impdp
・・・中略

ORA-39000: ダンプ・ファイル指定が無効です
ORA-31640: ダンプ・ファイル”C:\oraclexe\app\oracle\admin\xe\dpdump\expdat.dmp”を
読取りのためにオープンできません
ORA-27041: ファイルをオープンできません。

まだダメ。。
下記をちゃんと読んでディレクトリを確認し、既存の設定箇所にダンプを置いて実行。
https://blogs.oracle.com/oracle4engineer/entry/data_pumpexpdpimpdp
>impdp
接続先: Oracle Database 11g Express Edition Release 11.2.0.2.0 – Production
ORA-39001: 引数値が無効です
ORA-39000: ダンプ・ファイル指定が無効です
ORA-39142: 互換性のないバージョン番号4.1が、ダンプ・ファイル”C:\temp\data_20160928.dmp”内にあります

結局、ダンプを作成したOracleのバージョンが12なので無理っぽい。
こちらにもXE特有の問題があるとの事なので、開発端末へのインポートは諦め、テストサーバを作ってもらうまで待つことにします。。
http://freespeedo.blog.fc2.com/blog-entry-1.html#comment3

gitbucketとredmineを連携させてみる

 そろそろちゃんとソース管理をしないとと思い、既に使っているredmineにgitbucketを連携してみました。
redmineを入れているcentOS6サーバにgitbucketを導入してみます。って、下記を見る限り
http://qiita.com/pppurple/items/2e614a836e2184f70997
とりあえずgitbucketを動かすならwarをダウンロードしてJavaコマンド実行するだけのようですが、
java -jar gitbucket.war
では、どうもうまく動作しません。

で、payara-microにデプロイする形で実行してみます。
java -jar payara-micro-4.1.1.163.jar –port=18080 –deploy gitbucket.war

フロントがapacheなので、httpd.confのProxyPass当たりを追加し、とりあえずはgitbucketの画面が出るようになりました。

次はredmine側にリポジトリを登録します。が、リポジトリのパスはどこなんだろうと調べると、gitbucketデフォルトでは起動ユーザーのhomeに隠れて作られているとの事。このパスをそのままredmineのリポジトリに登録してみましたが、NotFound状態・・

リポジトリのパスを変えないとredmine側から参照できないようなので、ホームディレクトリを指定する実行時引数を追加。

結果的にこうなりました。
java -Dgitbucket.home=/var/lib/gitbucket -jar payara-micro-4.1.1.163.jar –port 18080 –deploy gitbucket.war

gitbucket側でリポジトリを新規作成し、サーバ内に出来た拡張子.gitのファイルパスをredmine側のリポジトリのパスに指定します。

やっとredmine側から見れるようになりました。
redminerep

作ったリポジトリへeclipseから強制PUSHするとこんな感じでgitbucketのホームディレクトリを変える前にコミットしたのも含めてredmineから参照出来ました。分散管理っていいですね。
redminerep2

後日、init.shを作って別途サービス登録しておきます。