Wicket 6.x→7.x でファイルダウンロード時のファイル名の注意

Wicket でファイルダウンロードのファイル名を日本語を使用する場合、Wicket 6.x では
URLエンコードしていた。
Wicket 7.x 移行では必要ないことに気がついた。


scheduleRequestHandlerAfterCurrent を使う場合。。

try(IResourceStreamWriter writer = new AbstractResourceStreamWriter(){
   @Override
   public void write(OutputStream out) throws IOException{

      // OutputStream に出力する。

      out.flush();
      out.close();
   }
   @Override
   public String getContentType() {
      // 返す ContentType を指定
      return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
   }
}){
   getRequestCycle().scheduleRequestHandlerAfterCurrent(
     new ResourceStreamRequestHandler(writer, "サンプル.xlsx")
   );
}catch(Exception e){
   // エラー捕捉
}


Wicket 6.x では、

  new ResourceStreamRequestHandler(writer, URLEncoder.encode("サンプル.xlsx", "UTF-8")

と書いていた。。

AjaxBehavior による ダウンロードも以下の抽象クラスを用意することになる

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.behavior.AbstractAjaxBehavior;
import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
import org.apache.wicket.request.resource.ContentDisposition;
import org.apache.wicket.util.resource.IResourceStream;
/**
 * AJAXDownload.
 */
public abstract class AJAXDownload extends AbstractAjaxBehavior{
   protected abstract IResourceStream getResourceStream();

   public void callBackDownload(AjaxRequestTarget target){
      target.appendJavaScript("setTimeout(\"window.location.href='" + getCallbackUrl().toString() + "'\", 100);");
   }
   @Override
   public void onRequest(){
      try{
         ResourceStreamRequestHandler handler = new ResourceStreamRequestHandler(getResourceStream(), getFileName());
         handler.setContentDisposition(ContentDisposition.ATTACHMENT);
         getComponent().getRequestCycle().scheduleRequestHandlerAfterCurrent(handler);
      }catch(Exception e){
         throw new RuntimeException(e.getMessage(), e);
      }
   }
   protected String getFileName(){
      return null;
   }
}

使う時、、、

final AJAXDownload download = new AJAXDownload(){
   @Override
   protected IResourceStream getResourceStream(){
      return new AbstractResourceStreamWriter(){
         @Override
         public void write(OutputStream out){
            try{

   // TODO OutputStream に出力

               out.flush();
               out.close();
            }catch(Exception e){
               throw new RuntimeException(e);
            }
         }
         @Override
         public String getContentType() {
            return "application/pdf";
         }
      };
   }
   @Override
   protected String getFileName() {
      return downloadname +".pdf";
   }
};

queue(new AjaxButton("download"){
   @Override
   protected void onSubmit(AjaxRequestTarget target, Form<?> f){
      download.callBackDownload(target);
   }
}.add(download));

Jasperreports のコンパイル実行を、Stream 処理でまとめる

Jasperreports のコンパイル実行を、Stream 処理でまとめてみました。

/**
 * @param directoryPath jrxmlを置いたディレクトリPATH
 * @return Map<String, Throwable> key=コンパイル処理実行のjrxmlファイルPATH、value=コンパイルエラー発生のThroawble
 */
public static Map<String, Throwable> jasperCompileWithDirectory(String directoryPath){
   return  Arrays.stream(new File(directoryPath).listFiles()).filter(e->e.getName().endsWith(".jrxml"))
   .collect(HashMap::new,(r, t)->{
      Throwable error = null;
      try(InputStream in=new FileInputStream(t.getAbsolutePath());
          OutputStream out = new FileOutputStream(t.getAbsolutePath().replaceAll("\\.jrxml", ".jasper"))
      ){
         JasperCompileManager.compileReportToStream(in, out);
      }catch(Exception e){
         error = e;
      }finally{
         r.put(t.getAbsolutePath(), error);
      }
   }, (r, u)->r.putAll(u));
}

使い方は、、、

    Map<String, Throwable> map = jasperCompileWithDirectory(Thread.currentThread().getContextClassLoader()
                                                            .getResource("").getPath() + "../../template");

のようにして、map の value が null ならコンパイル成功

stopPropagationの例、checkbox を td セルのクリックでも制御

JavaScript stopPropagationの例、メモ。

テーブルタグに配置したcheckbox を td セルのクリックでも制御


class="sample" の 配下の tableで、、1列目に checkbox ある場合、、

$(".sample td").click(function(){
   if ($(this).parent().children("td:nth-child(1)").children("input[type='checkbox']").prop("checked")){
      $(this).parent().children("td:nth-child(1)").children("input[type='checkbox']").prop("checked", false);
   }else{
      $(this).parent().children("td:nth-child(1)").children("input[type='checkbox']").prop("checked", true);
   }
});
$(".sample td input[type='checkbox']").click(function(ev){
   ev.stopPropagation();
});

Jasperreports 5.6.0 で Java8 LocalDate を出力するには

Jasperreports 5.6.0 で、Java8 LocalDate を出力するには、Jassperreports がテンプレートを読んで実行するコンパイラ
を別に用意しないとならなに。もう、Jassperreports が Java 8 に対応しないからだ。
コミュニティを探した結果。Eclipse の jdt コンパイラを指定する方法を見つけた。

→ Jasperreports 6.5.1 以降では、もはやその必要はない!
Jasperreports でLocalDate をフォーマット出力 - Oboe吹きプログラマの黙示録


環境。。。

Maven pom.xml

<dependency>
   <groupId>net.sf.jasperreports</groupId>
   <artifactId>jasperreports</artifactId>
   <version>5.6.0</version>
   <exclusions>
       <exclusion>
          <groupId>eclipse</groupId>
          <artifactId>jdtcore</artifactId>
       </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.eclipse.jdt.core.compiler</groupId>
    <artifactId>ecj</artifactId>
    <version>4.4</version>
</dependency>

jrxml での設定、1つ1つの jrxml ファイルに、設定が必要になるのが難点だけど。

LocalDate の出力フォーマットを指定する為に、java.time.format.DateTimeFormatter を使うなら、
インポート設定をする。
f:id:posturan:20161023131216j:plain

つまりこれは

   <import value="java.time.format.DateTimeFormatter"/>

に相当して、XML ファイル上。。

<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
 name="sample2_subreport1" pageWidth="555" pageHeight="802" columnWidth="555"
 leftMargin="0" rightMargin="0" topMargin="0" bottomMargin="0" uuid="e7afe545-1b11-4653-a894-10c52167832f">
   <property name="ireport.zoom" value="2.5937424601000028"/>
   <property name="ireport.x" value="414"/>
   <property name="ireport.y" value="0"/>
   <import value="java.time.format.DateTimeFormatter"/>

のように、追加される。

Field および Parameter の指定では、クラス定義として。java.time.LocalDate を指定する必要がある。
フィールドの例
f:id:posturan:20161023131633j:plain

   <field name="at_date" class="java.time.LocalDate"/>

パラメータの例
f:id:posturan:20161023131804j:plain

   <parameter name="nowdate" class="java.time.LocalDate" isForPrompting="false"/>

これを出力する記述として、、

   $F{at_date}.format(DateTimeFormatter.ofPattern("yyyy年 M月 d日"))


   $P{nowdate}.format(DateTimeFormatter.ofPattern("yyyy年 M月 d日"))

を jrxml に記述する。

iReport5.6.0 起動しない時、JDKパスを指定する

PDF 作成の ツール iReport もう サポートも終了しまったが、保守などでそれでも使うことがある。

Java 8 のPCで起動しなくなった。使用できるようにするには iReport インストールしたディレクトリの下に、 etc フォルダがあり、

     C:\Program Files (x86)\Jaspersoft\iReport-5.6.0\etc

そのフォルダ配下、 

    ireport.conf

がある。

この中に

# default location of JDK/JRE, can be overridden by using --jdkhome <dir> switch

に続けて、jdkhome JavaJDK パスを指定する。

Stream で LocalDateリストを生成

最終目的は、jQuery ui の datepicker 等を使用しないで、Javaからカレンダーを作成してスケジュールを
描画編集すること。DBに用意するデータと連携するために jQuery だけじゃ苦しいので Java でベースになる
日付のリストを先ず生成する。
簡単なものから、、、

現在日→当月のLocalDateリスト (現在日の個所は任意の日付にすればこのパターンが使える)

final LocalDate firstDate = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
List<LocalDate> list = Stream.iterate(firstDate, e->e.plusDays(1))
  .limit(firstDate.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth())
  .collect(Collectors.toList());

1日~末日だけのリストはこれで充分だが、カレンダー表示;曜日に沿って並べるのはこれでは苦しい。
CSSの nth-child と float属性で、並べていくとして、

li{
   float: left;
}
li:nth-child(7n+1){
   clear: both;
}

これで並べるにしても、月の初日が曜日の適切な位置で始まるようにリストを作成する必要がある。
→ 今回は、null を先頭に適切な数だけ入れることにする。

上の Stream の collect 終端処理で、Collectors.toList() でなくて
 collect(Supplier supplier,
BiConsumer accumulator,
BiConsumer combiner)
リスト生成するようにする。
段階として、上の処理を、一旦、Collectors.toList() を書き換えて確認しておく。

final LocalDate firstDate = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
List<LocalDate> list = Stream.iterate(firstDate, e->e.plusDays(1))
   .limit(firstDate.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth())
   .collect(ArrayList::new, (r, t)->r.add(t), (r, u)->r.addAll(u));

ここから、collect の Supplier 部分を必要な部分を適切に、null が格納されたリストを提供するようにする。
月の初日の曜日の列挙型の int値 を 7で割った余りの数だけ null を格納するリストを生成して
Supplierが返すリストにする。

final LocalDate firstDate = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
List<LocalDate> list = Stream.iterate(firstDate, e->e.plusDays(1))
  .limit(firstDate.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth())
  .collect(()->IntStream.range(0, firstDate.getDayOfWeek().getValue() % 7)
           .collect(ArrayList::new, (r, t)->r.add(null), (r, u)->r.addAll(u))
     , (r, t)->r.add(t), (r, u)->r.addAll(u)
);

これで完成。他人がパッと見て読み解くのたいへんそう。でも使い回しができそう。
li タグを書き並べる処理が書けるし、タグ内も LocalDateをキーにしてDBに用意した情報を
割りあてができる。もちろん1日ずつの走査の中でDBに毎回アクセスするような
馬鹿な事はしないで、予め Map に1ヶ月分を読み込んで格納しておくことが前提である

Linux MySQL dump → Windows 復元

MySQL ダンプを復元するとき、通常は、

mysql -u ユーザ名 -p データベース名 < ダンプファイル

でも、LinuxMySQLダンプしたものを Windows で復元を同じコマンドで実行してしまうと、

  Unknown command '\''

と、エラーになってしまう。当然と言えばそのとおりで、うっかり、やってしまう。

Linux 側、MySQL たいていは、昔と違って、UTF8 で構築するので、、

   --default-character-set=utf8 

オプションをつけて復元するようにする。

 

mysql -u ユーザ名 -p --default-character-set=utf8 データベース名 < ダンプファイル

ということだ。オプション名が長いのをなんとかして欲しい。