Eclipse で Tomcat 起動できない時の対処

EclipseTomcat 起動が次のエラーでできない時、

警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} 
Setting property 'source' to 'org.eclipse.jst.jee.server:Xxxxx' did not find a matching property.

サーバをクリックして、サーバーオプションの設定で
XMLファイルを分割してモジュールコンテキストを公開のところにチェックをつける。
Maven サブモジュールで webapp を作っている場合に起きた。
f:id:posturan:20180222183913j:plain

ERMaster Eclipse 4.6.2 でインストールできない

Eclipse Oxygen が重くて、まだNEON を使ったりするのですが、DBのER図を書くプラグイン
ERMaster をEclipse NEON に入れようとしてもどうしても入らなかった
help→新規ソフトウェアにインストール
http://ermaster.sourceforge.net/update-site/
入力してもサービス利用不可でインストールできない、マーケティングプレイスでもそんなものない。

Eclipse Oxygen なら、
https://sourceforge.net/projects/ermaster/
から、JAR をダウンロードして、eclipse/dropins の下に置けばインストールされる。

astah でER図を書くのが嫌になってきて ERMasterを使ってみようかと思ったけど。。
最近RDBを皆、敬遠してる。

Chart.js 凡例クリック無効化

Chart.js グラフの凡例をクリックするとデフォルトでは対象グラフ描画が消えて、
凡例テキストは打ち消し線が表示される。

これを無効にしたい時がある、options 設定で以下のとおり onClick の戻り値を false にすればよい。

legend:{
   onClick: function(){ return false; },
},

Chart.js ツールチップカスタマイズ

Chart.js ToolTip のカスタマイズはネット検索すれば、まとまった解説も沢山見つかるし、サンプルもある。
http://tr.you84815.space/chartjs/configuration/tooltip.html
あえて、時刻をX軸にしたグラフで ToolTipカスタマイズを書いてみた。

options に、tooltips: { ... } を書くことになる。ToolTip のタイトルやラベルは、
callback で、

title:  function(tooltipItem, data){
     return "タイトル";
},
label:  function(tooltipItem, data){
     return "ラベル";
},

というようにする。tooltipItemは対象データ、data はグラフデータ全体、
tooltipItem.index で並んだデータ配列のインデックスを参照できる。

サンプル、moment.js を使ってます。

var graph = {
    xLabels: [],
    yLabels: [],
    datasets: [{
        label: "First dataset",
        lineTension: 0,
    :
    省略
    :
};
var options = {
    responsive: true,
    title:{ display:true,
        text:'Time-Line Chart sample'
    },
    scales: {
        xAxes: [{ display: true,
            scaleLabel: { display: true, labelString: '時刻' },
            type: "time",
            time: {
                unit: "hour",
                displayFormats:{ hour: 'HH:mm' },
                min: new moment().hour(5).minute(0).second(0).millisecond(0),
                max: new moment().hour(17).minute(0).second(0).millisecond(0),
            },
        }],
        yAxes: [{ display: true,
            scaleLabel: { display: true, labelString: 'Value' },
            ticks: { min: 0, max: 100, stepSize: 20 }
        }]
    },
    tooltips: {
        titleFontSize: 18,
        bodyFontSize: 18,
        callbacks: {
            title: function (tooltipItem, data){
                return "First";
            },
            label: function (tooltipItem, data){
                return moment(tooltipItem.xLabel._i).format('YYYY年M月D日 HH:mm:ss')
                + "  value = "
                + tooltipItem.y;
            }
        }
    },
    legend:{    /* 凡例クリックが伝播しないようにする。*/
         onClick: function(){ return false; },
    },
};

var canvas = document.getElementById("myChart");
var ctx = canvas.getContext('2d');
var myChart = new Chart(ctx, {
    type: 'line',
    data: graph,
    options: options
});

var todaystr = new moment().format('YYYY-MM-DDT');

graph.datasets[0].data.push({ t: moment(todaystr + "05:17:20"), y: createRandom(0, 100) });
graph.datasets[0].data.push({ t: moment(todaystr + "06:43:52"), y: createRandom(0, 100) });
graph.datasets[0].data.push({ t: moment(todaystr + "07:21:05"), y: createRandom(0, 100) });
graph.datasets[0].data.push({ t: moment(todaystr + "08:06:12"), y: createRandom(0, 100) });
graph.datasets[0].data.push({ t: moment(todaystr + "09:47:41"), y: createRandom(0, 100) });
graph.datasets[0].data.push({ t: moment(todaystr + "10:02:33"), y: createRandom(0, 100) });
graph.datasets[0].data.push({ t: moment(todaystr + "11:12:02"), y: createRandom(0, 100) });
graph.datasets[0].data.push({ t: moment(todaystr + "12:11:02"), y: createRandom(0, 100) });
graph.datasets[0].data.push({ t: moment(todaystr + "13:12:28"), y: createRandom(0, 100) });
graph.datasets[0].data.push({ t: moment(todaystr + "14:16:41"), y: createRandom(0, 100) });

myChart.update();

label: function は、以下のように長いのが正解なのかもしれないが上の方が、簡潔

label: function (tooltipItem, data){
   return moment(graph.datasets[0].data[tooltipItem.index].t._i).format('YYYY年M月D日 HH:mm:ss')
   + "  value = "
   + graph.datasets[0].data[tooltipItem.index].y;
}

f:id:posturan:20180208231637j:plain

Base64 でエンコードされた画像データ文字列から、画像の復元

Qiita で以下、古い記事を見つけました。
Base64形式で受け取った画像データをBufferedImageに変換

とても参考になり良い記事です。特に、1ピクセルずつ画像を生成する処理は Cool ! です。
Java8 になってからは、Apache Commons Codec の Base64 でデコードしなくても、
java.util.Base64デコーダーでなんとかいけるようです。
Qiita の記事を参考にサンプル書いてみました。

1ピクセルずつ画像を生成する処理、ちょっと書き直します。

public static BufferedImage toBufferedImage(byte[] imageBinary) throws IOException{
   BufferedImage img = ImageIO.read(new ByteArrayInputStream(imageBinary));
   int width = img.getWidth();
   int height = img.getHeight();
   BufferedImage bufImage = new BufferedImage(img.getWidth(), height, BufferedImage.TYPE_INT_RGB);
   for(int y = 0; y < height; y++){
      for(int x = 0 ; x < width; x++){
         int c = img.getRGB(x, y);
         int r = c >> 16 & 0xff;
         int g = c >> 8 & 0xff;
         int b = c & 0xff;
         int rgb = 0xff000000 | r << 16 | g << 8 | b;
         bufImage.setRGB(x, y, rgb);
      }
   }
   return bufImage;
}

Base64 デコードしてこの toBufferedImage を呼び出し BufferedImage を作成します

try(FileInputStream in = new FileInputStream("base64string");
   OutputStream out=new FileOutputStream("sample.png");
   ByteArrayOutputStream bo = new ByteArrayOutputStream();){

   byte[] b = new byte[1024];
   int len;
   
   while((len = in.read(b, 0, b.length)) > 0){
      bo.write(b, 0, len);
   }
   BufferedImage bimage = toBufferedImage(Base64.getDecoder().decode(bo.toString()));

   ImageIO.write(bimage, "png", out);
}catch(Exception e){
   e.printStackTrace();
}

先日書いた、
Chart.js 図全体背景色、グラフエリア背景色、イメージダウンロードの問題を解決する - Oboe吹きプログラマの黙示録
でIEでできなかったダウンロードも、Base64 エンコードしたものを、FORMーPOST送信で
サーバに送って、サーバ側で上の処理の結果の画像をストリーム出力すれば
ダウンロードできますね。

Maven 依存関係深い時の Jigsaw モジュール参照できないエラーを回避するには

Java9 で、module-info を書いて、Maven で解決できない時、
Maven 依存関係が深くなってしまった場合、pom.xml でネストして書いていなかったものを
書けばコンパイルが通る。
実際の現象、、、
最初、pom.xml を以下のように記述していた。。

<properties>
    <wicket.version>8.0.0-M8</wicket.version>
    <guice.verison>4.1.0</guice.verison>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
    <dependency>
        <groupId>org.apache.wicket</groupId>
        <artifactId>wicket-guice</artifactId>
        <version>${wicket.version}</version>
    </dependency>
    <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
   </dependency>
   <dependency>
      <groupId>com.google.inject</groupId>
      <artifactId>guice</artifactId>
      <version>${guice.verison}</version>
   </dependency>
</dependencies>

Java のソースは、以下しかインポートしていないのに、

import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.application.IComponentInstantiationListener;
import org.apache.wicket.guice.GuiceInjectorHolder;
import org.apache.wicket.injection.IFieldValueFactory;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Stage;

wicket-util.jar の中にある org.apache.wicket.util.IHierarchical が見つからないとエラーになる。
IHierarchical を使用するコード書いたつもりはなかったけど、どこで使われてるんだろう?
pom.xml の依存関係階層を見ると、
f:id:posturan:20180201233504j:plain
wicket-util.jarが wicket-guice → wicket-core → wicket-util. と依存している。
module-info.java には、

requires wicket.core;
requires wicket.ioc;
requires wicket.guice;
requires javax.inject;
requires guice;

を書いていたのだが、ここに、wicket.util を追加する。

requires wicket.core;
requires wicket.ioc;
requires wicket.guice;
requires wicket.util;
requires javax.inject;
requires guice;

これで解決と思ったがこれだけではダメで、pom.xmldependency に wicket-utilも追加する。

<dependency>
	<groupId>org.apache.wicket</groupId>
	<artifactId>wicket-util</artifactId>
	<version>${wicket.version}</version>
</dependency>

これでコンパイルエラーにならないようになる。

MySQL 0埋めの AUTO_INCREMENT

MySQL で id など AUTO_INCREMENT を用意するとき、
0埋めで作成されるようにするには、桁数指定で、UNSIGNED ZEROFILL を付ける

例)4桁

CREATE TABLE branches (
  id           INT(4) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT
, branch_name  VARCHAR(60) NOT NULL
, PRIMARY KEY (id)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

サンプル

mysql> TRUNCATE TABLE branches;
mysql> INSERT INTO branches (bname)VALUES('aaa'),('bbb');
mysql> commit;
mysql> SELECT * FROM branches;

+------+-------+
| id   | bname |
+------+-------+
| 0001 | aaa   |
| 0002 | bbb   |
+------+-------+
2 rows in set (0.00 sec)

mysql> select * from branches where id = 2;
+------+-------+
| id   | bname |
+------+-------+
| 0002 | bbb   |
+------+-------+
1 row in set (0.00 sec)

mysql> select * from branches where id = '0002';
+------+-------+
| id   | bname |
+------+-------+
| 0002 | bbb   |
+------+-------+
1 row in set (0.00 sec)

mysql> select * from branches where id = 0002;
+------+-------+
| id   | bname |
+------+-------+
| 0002 | bbb   |
+------+-------+
1 row in set (0.00 sec)

mysql> select * from branches where id = 02;
+------+-------+
| id   | bname |
+------+-------+
| 0002 | bbb   |
+------+-------+
1 row in set (0.00 sec)

mysql>