Wicket stateless なページ

Wicket の基本は、ステートフルである。それでもステートレスのページが必要なケースも要件によっては発生するのが
Webアプリ開発の世界。

Form → StatelessForm
Link → StatelessLink か、 BookmarkablePageLink に置き換える

で済ませられれば良いが、Page の中で、

    boolean status = isPageStateless();

をチェックすると、AJAXコンポーネントやビヘビアでは false になってしまう。
wicketstuff-stateless が、これら AJAX におけるステートレス化を提供しているのでこれを使う。

現時点、バージョンが、Wicket本体より遅れてるみたいで、8.0.0-M2 なので、pom.xml は、

<dependency>
    <groupId>org.wicketstuff</groupId>
    <artifactId>wicketstuff-stateless</artifactId>
    <version>8.0.0-M2</version>
</dependency>

wicketstuff-stateless が提供するものは、
コンポーネントでは、、
StatelessAjaxEventBehavior
StatelessAjaxFormComponentUpdatingBehavior
StatelessAjaxFormSubmitBehavior
StatelessOnChangeAjaxBehavior

ビヘビアでは、
StatelessAjaxButton
StatelessAjaxFallbackLink
StatelessAjaxSubmitLink
StatelessIndicatingAjaxButton
StatelessIndicatingAjaxFallbackLink

例えば、、、

queue(new StatelessForm<Void>("form"));
queue(new Button("submit").add(new StatelessAjaxFormSubmitBehavior("click"){
   @Override
   protected void onSubmit(AjaxRequestTarget target){
      // TODO
   }
}));

ということだが、やはり、onSubmit は、Consumer ラムダ式にしたい。

ステートフルの時のように、、

queue(new Button("submit").add(AjaxFormSubmitBehavior.onSubmit("click", t->{
  // TODO
})));

と書きたい。。

しかたなく、StatelessAjaxFormSubmitBehavior を継承したクラスを用意して、、

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.util.lang.Args;
import org.danekja.java.util.function.serializable.SerializableConsumer;
import org.wicketstuff.stateless.behaviors.StatelessAjaxFormSubmitBehavior;

/**
 * CustomStatelessAjaxFormSubmitBehavior.
 */
public class CustomStatelessAjaxFormSubmitBehavior extends StatelessAjaxFormSubmitBehavior{

   public CustomStatelessAjaxFormSubmitBehavior(String event){
      super(event);
   }
   public static CustomStatelessAjaxFormSubmitBehavior onSubmit(String eventName, SerializableConsumer<AjaxRequestTarget> onSubmit){
      Args.notNull(onSubmit, "onSubmit");
      return new CustomStatelessAjaxFormSubmitBehavior(eventName){
         private static final long serialVersionUID = 1L;
         @Override
         protected void onSubmit(AjaxRequestTarget target)   {
            onSubmit.accept(target);
         }
      };
   }
}

ステートレスを保つように、、、以下にする。

queue(new Button("submit").add(CustomStatelessAjaxFormSubmitBehavior.onSubmit("click", t->{
  // TODO
})));

ZIP 圧縮と展開

先日、tar and gzip 圧縮・展開を書いたので、ZIP圧縮・展開です。これは Apache commons-compress を必要とすることなく
標準ライブラリと、Throwable な Consumerがあれば綺麗に書けます。→ ラムダ式の例外処理を綺麗にする - Oboe吹きプログラマの黙示録

圧縮サンプル ・・・Throwable な Consumer → ThrowableConsumer.of を使います。

List<String> files =  /* 圧縮対象のファイルのエントリPATH のリスト */
try(ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("out/test.zip")))){
   files.stream().forEach(ThrowableConsumer.of(e->{
      File f = new File(e);
      if (f.exists()){
         zos.putNextEntry(new ZipEntry(f.isDirectory() ? e + "/" : e));
         if (f.isFile()){
            try(FileInputStream fis = new FileInputStream(e); BufferedInputStream bis = new BufferedInputStream(fis)){
               int size = 0;
               byte[] buf = new byte[1024];
               while((size = bis.read(buf)) > 0){
                  zos.write(buf, 0, size);
               }
            }
         }
      }
   }));
}catch(Exception ex){
   ex.printStackTrace();
}

展開サンプル、展開時にエントリ名を標準出力してます。

try(ZipInputStream zin = new ZipInputStream(new FileInputStream("out/test.zip"))){
   ZipEntry entry;
   while((entry = zin.getNextEntry()) != null){
      System.out.println( entry.getName() );
      if (entry.isDirectory()){
         String s = entry.getName();
         new File(dirPath + "/" + s.substring(0, s.length()-1)).mkdir();
      }else{
         String[] d = entry.getName().split("/");
         String dir = dirPath;
         for(int i=0;i < d.length-1;i++){
            dir += "/" + d[i];
            new File(dir).mkdir();
         }
         try(FileOutputStream fos = new FileOutputStream(dirPath+"/"+entry.getName()); BufferedOutputStream bos = new BufferedOutputStream(fos)){
            int size = 0;
            byte[] buf = new byte[1024];
            while((size = zin.read(buf)) > 0){
               bos.write(buf, 0, size);
            }
         }
      }
   }
}catch(Exception ex){
   ex.printStackTrace();
}

tar and gzip のケースのように、
tar and gzip 圧縮・展開を補強 - Oboe吹きプログラマの黙示録

ディレクトリscan を合わせて、
oboe2uran.hatenablog.com

1つのインターフェースとしてまとめておきたくなりました。

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.yipuran.function.ThrowableConsumer;

/**
 * ZIP 圧縮/解凍プロセッサ.
 * (規則)
 *     ZIPファイル名拡張子 → .zip
 * (圧縮)
 * Supplier<Collection<FileCollection>> = ファイルコレクション(FileCollection)で指定する対象を
 * Collection<String> compress(String targzPath) で圧縮する。
 * メソッド戻り値は、tarエントリ名 Collection
 * (展開)
 * void decompress(String zipPath, String dirPath) で展開する。
 * @since 1.1
 */
public interface ZipProcessor extends Supplier<Collection<FileCollection>>{

   /**
    * ZIP 圧縮実行.
    * <PRE>
    * Supplier で渡す FileCollection の渡し方で単一か複数か決まる。
    * 例1)
    *    // targetPath配下を圧縮対象にする場合
    *    List<FileCollection> fileCollections =
    *    Arrays.stream(new File(targetPath).listFiles()).map(e->FileCollection.of(e.getAbsolutePath())).collect(Collectors.toList());
    *    ZipProcessor processor = ()->fileCollections;
    *    Collection<String> entries = processor.compress(zipPath);
    *
    * 例2)
    *    // 1つのディレクトリツリーで圧縮
    *    FileCollection fileCollection = FileCollection.of(targetPath);
    *    ZipProcessor processor = ()->Arrays.asList(fileCollection);
    *    Collection<String> entries = processor.compress(zipPath);
    *
    * </PRE>
    * @param zipPath 作成する ZIPファイルパス、 *.zip
    * @return ZIPエントリ名 Collection
    */
   public default Collection<String> compress(String zipPath){
      Collection<String> entries = new ArrayList<>();
      try(ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(zipPath)))){
         get().forEach(fc->{
            String prefix = fc.getFile().getParentFile().getAbsolutePath().replaceAll("\\\\", "/");
            fc.scan(ThrowableConsumer.of(f->{
               String entryName = f.getAbsolutePath().replaceAll("\\\\", "/").replaceFirst(prefix, "");
               ZipEntry entry = new ZipEntry(f.isDirectory() ? entryName + "/" : entryName);
               entries.add(entry.getName().charAt(0)=='/' ? entry.getName().substring(1) : entry.getName());
               zos.putNextEntry(entry);
               if (f.isFile()){
                  try(FileInputStream fis = new FileInputStream(f); BufferedInputStream bis = new BufferedInputStream(fis)){
                     int size = 0;
                     byte[] buf = new byte[1024];
                     while((size = bis.read(buf)) > 0){
                        zos.write(buf, 0, size);
                     }
                  }
               }
            }));
         });
      }catch(IOException ex){
         throw new RuntimeException(ex);
      }
      return entries;
   }
   /**
    * zip 圧縮実行(対象制限).
    * <PRE>
    * Predicate<File> で、tar作成対象を制限する。任意ディレクトリパスなど制限するために使用する。
    * </PRE>
    * @param zipPath 作成する zip ファイルパス、 *.zip
    * @param p Predicate<File>制限規則の付与
    * @return ZIPエントリ名 Collection
    */
   public default Collection<String> compress(String zipPath, Predicate<File> p){
      Collection<String> entries = new ArrayList<>();
      try(ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(zipPath)))){
         get().forEach(fc->{
            String prefix = fc.getFile().getParentFile().getAbsolutePath().replaceAll("\\\\", "/");
            fc.scan(p, ThrowableConsumer.of(f->{
               String entryName = f.getAbsolutePath().replaceAll("\\\\", "/").replaceFirst(prefix, "");
               ZipEntry entry = new ZipEntry(f.isDirectory() ? entryName + "/" : entryName);
               entries.add(entry.getName().charAt(0)=='/' ? entry.getName().substring(1) : entry.getName());
               zos.putNextEntry(entry);
               if (f.isFile()){
                  try(FileInputStream fis = new FileInputStream(f); BufferedInputStream bis = new BufferedInputStream(fis)){
                     int size = 0;
                     byte[] buf = new byte[1024];
                     while((size = bis.read(buf)) > 0){
                        zos.write(buf, 0, size);
                     }
                  }
               }
            }));
         });
      }catch(IOException ex){
         throw new RuntimeException(ex);
      }
      return entries;
   }
   /**
    * ZIPファイル展開.
    * @param zipPath ZIPファイルパス
    * @param dirPath 展開先ディレクトリパス
    * @return Set<String> ZIPエントリ名 Collection
    */
   public static Set<String> decompress(String zipPath, String dirPath){
      TreeSet<String> entries = new TreeSet<>();
      // 展開
      try(ZipInputStream zin = new ZipInputStream(new FileInputStream(zipPath))){
         ZipEntry entry;
         while((entry = zin.getNextEntry()) != null){
            entries.add(entry.getName());
            if (entry.isDirectory()){
               String s = entry.getName();
               new File(dirPath + "/" + s.substring(0, s.length()-1)).mkdir();
            }else{
               String[] d = entry.getName().split("/");
               String dir = dirPath;
               for(int i=0;i < d.length-1;i++){
                  dir += "/" + d[i];
                  new File(dir).mkdir();
               }
               try(FileOutputStream fos = new FileOutputStream(dirPath+"/"+entry.getName()); BufferedOutputStream bos = new BufferedOutputStream(fos)){
                  int size = 0;
                  byte[] buf = new byte[1024];
                  while((size = zin.read(buf)) > 0){
                     bos.write(buf, 0, size);
                  }
               }
            }
         }
      }catch(IOException ex){
         throw new RuntimeException(ex.getMessage(), ex);
      }
      return entries;
   }
   /**
    * エントリ名コレクション.
    * @param zipPath ZIPファイルパス、 *.zip
    * @return Set<String> ZIPエントリ名 Collection
    */
   public static Set<String> viewPath(String zipPath){
      TreeSet<String> entries = new TreeSet<>();
      try(ZipInputStream zin = new ZipInputStream(new FileInputStream(zipPath))){
         ZipEntry entry;
         while((entry = zin.getNextEntry()) != null){
            entries.add(entry.getName());
         }
      }catch(IOException ex){
         throw new RuntimeException(ex.getMessage(), ex);
      }
      return entries;
   }
}

Java8 です。

Java9 module-info 未対応 JAR の Maven 解決方法

先日、初めてJava9 Jigsaw に触れて四苦八苦したが、、、
Java9 Jigsaw モジュール参照側は結局すべてモジュールを引っ張れないとならない?! - Oboe吹きプログラマの黙示録
Java9 Jigsaw モジュール使用プログラムの実行 - Oboe吹きプログラマの黙示録
Java9 Jigsaw と Maven - Oboe吹きプログラマの黙示録
Eclipse WTP プロジェクトで失敗したのは、まだ module-info が作られていない JARファイルを
どう扱うかが、できてないからだ。

以下のようにすれば良い。
例えば、 mybatis Version 3.4.5 を使うとすると pom.xml で、たとえ他のJARで間接参照があって
その pom.xml で書いてあっても改めて pom.xml に書かないと、存在しないモジュール定義のJARでは
だめであった。

<dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.5</version>
</dependency>

コンパイルプラグインで、、--add-modules を書かなくてならず、
ここで指定する名が、存在しないモジュール宣言を強制的に宣言することになり、

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.7.0</version>
    <configuration>
       <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
       </descriptorRefs>
       <source>9</source>
       <target>9</target>
       <compilerArgs>
            <arg>--add-modules mybatis</arg>
            <arg>--add-modules wasample</arg>
       </compilerArgs>
    </configuration>
</plugin>

wasample は、このWTPプロジェクトでWARとしてつくる artifactId に相当。
作成している WTPプロジェクトの module-info.java で、

module org.wa.wsample{
	requires mybatis;
}

これは 自動でモジュール名を付ける約束として以下の約束で付けていくことになる。
http://download.java.net/java/jigsaw/docs/api/java/lang/module/ModuleFinder.html#of-java.nio.file.Path...-

mybatis.3.4.5.jar を参照するから、requires mybatis なのだ。

これを踏まえて苦労して、SL4J や、Wicket を含めると以下のとおり。。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.7.0</version>
    <configuration>
       <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
       </descriptorRefs>
       <source>9</source>
       <target>9</target>
       <compilerArgs>
              <arg>--add-modules sl4j-api</arg>
              <arg>--add-modules logback-core</arg>
              <arg>--add-modules logback-classic</arg>
              <arg>--add-modules wicket-core</arg>
              <arg>--add-modules wicket-request</arg>
              <arg>--add-modules wicket-devutils</arg>
              <arg>--add-modules wicket-extensions</arg>
              <arg>--add-modules wicket-guice</arg>
              <arg>--add-modules wicket-ioc</arg>
              <arg>--add-modules wicket-objectsizeof-agent</arg>
              <arg>--add-modules wicket-util</arg>
              <arg>--add-modules wicket-jquery-ui</arg>
              <arg>--add-modules jdk-serializable-functional</arg>
              <arg>--add-modules yipuran-wicketcustom</arg>
              <arg>--add-modules yipuran-wicketguice</arg>
              <arg>--add-modules yipuran-mybatis</arg>
              <arg>--add-modules guice</arg>
              <arg>--add-modules javax.inject</arg>
              <arg>--add-modules openjson</arg>
              <arg>--add-modules mybatis</arg>
              <arg>--add-modules wasample</arg>
       </compilerArgs>
    </configuration>
</plugin>

この pom.xml での dependency の記述にも注意が必要になり、の先頭にないと
コンパイル参照できなかったりする。

<dependencies>
  <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
   </dependency>
   <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
   </dependency>
   <dependency>
       <groupId>org.danekja</groupId>
       <artifactId>jdk-serializable-functional</artifactId>
       <version>1.8.3</version>
   </dependency>
   <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.5</version>
   </dependency>


依存するJARの artifactId の中の区切り文字が、"-" であるので module-info,java では、"." に置きかえる。

module org.wa.wsample{
	requires slf4j.api;
	requires jdk.serializable.functional;
	requires mybatis;

	requires wicket.core;
	requires wicket.request;
	requires wicket.devutils;
	requires wicket.extensions;
	requires wicket.guice;
	requires wicket.ioc;
	requires wicket.objectsizeof.agent;
	requires wicket.util;
	requires wicket.jquery.ui;
	requires guice;
	requires javax.inject;

	requires yipuran.wicketcustom;
	requires yipuran.wicketguice;
	requires yipuran.mybatis;
}

これでようやく Jigsaw のコンパイル時のモジュール参照が、WTP - maven でもなんとかなるが、
それにしても、コンパイルの所謂、、もっさり感の重さは酷いものだ。

QRコード生成&読込で良く使われてるもの。

Javaでなくても、C++C# でも Zxing を使えば QRコード生成&読込は、事足りる。

github.com


変なものを見つけた。JavaScriptQRコードをデコードするもの。。。
GitHub - colkito/qrcode-decoder-js: Javascript QR code decoder

どうして JavaScript でデコードする必要性があるのか?理解に苦しむ。

tar and gzip 圧縮・展開を補強

Tar GZIP 圧縮を先日書いた
oboe2uran.hatenablog.com

これに、圧縮する時に対象を制限したり展開せずにエントリを抽出するものを追加した。
長いけど以下のとおり。

ただし、→ FileCollection - Oboe吹きプログラマの黙示録 が必要

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;

/**
 * tar and gzip 圧縮/解凍プロセッサ.
 * (規則)
 *     tar          ファイル名拡張子 → .tar
 *     gzip         ファイル名拡張子 → .gz
 *     tar and gzip ファイル名拡張子 → .tar.gz
 * (圧縮)
 * Supplier<Collection<FileCollection>> = ファイルコレクション(FileCollection)で指定する対象を
 * Collection<String> compress(String targzPath) で圧縮する。
 * メソッド戻り値は、tarエントリ名 Collection
 * (展開)
 * void decompress(String targzPath, String dirPath) で展開する。
 */
public interface TarGzipProcessor extends Supplier<Collection<FileCollection>>{

  /**
   * tar and gzip 圧縮実行.
   * <PRE>
   * Supplier で渡す FileCollection の渡し方で単一か複数か決まる。
   * 例1)
   *    // targetPath配下を圧縮対象にする場合
   *    List<FileCollection> fileCollections =
   *    Arrays.stream(new File(targetPath).listFiles()).map(e->FileCollection.of(e.getAbsolutePath())).collect(Collectors.toList());
   *    TarGzipProcessor processor = ()->fileCollections;
   *    Collection<String> entries = processor.compress(targzipPath);
   *
   * 例2)
   *    // 1つのディレクトリツリーで圧縮
   *    FileCollection fileCollection = FileCollection.of(targetPath);
   *    TarGzipProcessor processor = ()->Arrays.asList(fileCollection);
   *    Collection<String> entries = processor.compress(targzipPath);
   *
   * </PRE>
   * @param targzPath 作成する tar and gzip ファイルパス、 *.tar.gz
   * @return tarエントリ名 Collection
   */
  public default Collection<String> compress(String targzPath){
    Collection<String> entries = new ArrayList<>();
    String tarpath = targzPath.replaceAll("\\.tar\\.gz$", ".tar");
    // tar 生成
    try(FileOutputStream out = new FileOutputStream(tarpath);TarArchiveOutputStream taos = new TarArchiveOutputStream(out)){
      get().forEach(fc->{
        String prefix = fc.getFile().getParentFile().getAbsolutePath().replaceAll("\\\\", "/");
        fc.scan(f->{
          try{
            if (f.isDirectory()){
              TarArchiveEntry entry = new TarArchiveEntry(fc.getFile(), f.getAbsolutePath().replaceAll("\\\\", "/").replaceFirst(prefix, ""));
              taos.putArchiveEntry(entry);
              taos.closeArchiveEntry();
              entries.add(entry.getName());
              return;
            }
            TarArchiveEntry entry = new TarArchiveEntry(f, f.getAbsolutePath().replaceAll("\\\\", "/").replaceFirst(prefix, ""));
            taos.putArchiveEntry(entry);
            //taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
            taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
            try(FileInputStream fis = new FileInputStream(f); BufferedInputStream bis = new BufferedInputStream(fis)){
              int size = 0;
              byte[] buf = new byte[1024];
              while((size = bis.read(buf)) > 0){
                taos.write(buf, 0, size);
              }
            }
            taos.closeArchiveEntry();
            entries.add(entry.getName());
          }catch(IOException ex){
            throw new RuntimeException(ex);
          }
        });
      });
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    // gzip 生成
    try(FileInputStream fis = new FileInputStream(tarpath); BufferedInputStream bis = new BufferedInputStream(fis);
      FileOutputStream fos = new FileOutputStream(targzPath); GzipCompressorOutputStream gout = new GzipCompressorOutputStream(fos)
    ){
      int size = 0;
      byte[] buf = new byte[1024];
      while((size = bis.read(buf)) > 0){
        gout.write(buf, 0, size);
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    new File(tarpath).delete();
    return entries;
  }

  /**
   * tar and gzip 圧縮実行(対象制限).
   * <PRE>
   * Predicate<File> で、tar作成対象を制限する。任意ディレクトリパスなど制限するために使用する。
   * </PRE>
   * @param targzPath 作成する tar and gzip ファイルパス、 *.tar.gz
   * @param p Predicate<File>制限規則の付与
   * @return tarエントリ名 Collection
   */
  public default Collection<String> compress(String targzPath, Predicate<File> p){
    Collection<String> entries = new ArrayList<>();
    String tarpath = targzPath.replaceAll("\\.tar\\.gz$", ".tar");
    // tar 生成
    try(FileOutputStream out = new FileOutputStream(tarpath);TarArchiveOutputStream taos = new TarArchiveOutputStream(out)){
      get().forEach(fc->{
        String prefix = fc.getFile().getParentFile().getAbsolutePath().replaceAll("\\\\", "/");
        fc.scan(p, f->{
          try{
            if (f.isDirectory()){
              TarArchiveEntry entry = new TarArchiveEntry(fc.getFile(), f.getAbsolutePath().replaceAll("\\\\", "/").replaceFirst(prefix, ""));
              taos.putArchiveEntry(entry);
              taos.closeArchiveEntry();
              entries.add(entry.getName());
              return;
            }
            TarArchiveEntry entry = new TarArchiveEntry(f, f.getAbsolutePath().replaceAll("\\\\", "/").replaceFirst(prefix, ""));
            taos.putArchiveEntry(entry);
            //taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
            taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
            try(FileInputStream fis = new FileInputStream(f); BufferedInputStream bis = new BufferedInputStream(fis)){
              int size = 0;
              byte[] buf = new byte[1024];
              while((size = bis.read(buf)) > 0){
                taos.write(buf, 0, size);
              }
            }
            taos.closeArchiveEntry();
            entries.add(entry.getName());
          }catch(IOException ex){
            throw new RuntimeException(ex);
          }
        });
      });
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    // gzip 生成
    try(FileInputStream fis = new FileInputStream(tarpath); BufferedInputStream bis = new BufferedInputStream(fis);
      FileOutputStream fos = new FileOutputStream(targzPath); GzipCompressorOutputStream gout = new GzipCompressorOutputStream(fos)
    ){
      int size = 0;
      byte[] buf = new byte[1024];
      while((size = bis.read(buf)) > 0){
        gout.write(buf, 0, size);
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    new File(tarpath).delete();
    return entries;
  }


  /**
   * tar and gzip 展開.
   * @param targzPath tar and gzip ファイルパス、 *.tar.gz
   * @param dirPath 展開先ディレクトリPATH
   * @return Collection<String> 展開された tar エントリ名
   */
  public static Collection<String> decompress(String targzPath, String dirPath){
    Collection<String> entries = new ArrayList<>();
    String tarname = targzPath.substring(targzPath.lastIndexOf("/") + 1).replaceAll("\\.gz$", "");
    // gzip 解凍
    try(FileInputStream fis = new FileInputStream(targzPath);GzipCompressorInputStream gin = new GzipCompressorInputStream(fis);
      FileOutputStream  fos = new FileOutputStream(dirPath + "/" + tarname)
    ){
      int size = 0;
      byte[] buf = new byte[1024];
      while((size = gin.read(buf)) > 0){
        fos.write(buf, 0, size);
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    // tar展開
    try(FileInputStream fis = new FileInputStream(dirPath + "/" + tarname);  TarArchiveInputStream tais = new TarArchiveInputStream(fis)){
      ArchiveEntry entry = null;
      while((entry = tais.getNextEntry()) != null){
        File file = new File(dirPath + "/" + entry.getName());
        if (entry.isDirectory()){
          file.mkdirs();
          entries.add(entry.getName());
          continue;
        }
        if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); }
        try(FileOutputStream fos = new FileOutputStream(file);
            BufferedOutputStream bos = new BufferedOutputStream(fos)){
          int size = 0;
          byte[] buf = new byte[1024];
          while((size = tais.read(buf)) > 0){
            bos.write(buf, 0, size);
          }
          entries.add(entry.getName());
        }
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    new File(dirPath + "/" + tarname).delete();
    return entries;
  }

  /**
   * tar and gzip エントリ名コレクション.
   * @param targzPath tar and gzip ファイルパス、 *.tar.gz
   * @return Collection<String>
   */
  public static Collection<String> viewPath(String targzPath){
    Collection<String> entries = new ArrayList<>();
    try(  FileInputStream fis = new FileInputStream(targzPath);
        GzipCompressorInputStream gin = new GzipCompressorInputStream(fis);
        PipedOutputStream pos = new PipedOutputStream();
        PipedInputStream pin = new PipedInputStream();
        TarArchiveInputStream tais = new TarArchiveInputStream(pin)
    ){
      pin.connect(pos);
      new Thread(()->{
        try{
          int size = 0;
          byte[] buf = new byte[1024];
          while((size = gin.read(buf)) >= 0){
            pos.write(buf, 0, size);
            pos.flush();
          }
        }catch(IOException e){
          throw new RuntimeException(e.getMessage(), e);
        }
      }).start();
      ArchiveEntry entry = null;
      while((entry = tais.getNextEntry()) != null){
        entries.add(entry.getName());
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    return entries;
  }
  /**
   * ArchiveEntryコレクション.
   * @param targzPath tar and gzip ファイルパス、 *.tar.gz
   * @return Collection<ArchiveEntry>
   */
  public static Collection<ArchiveEntry> entries(String targzPath){
    Collection<ArchiveEntry> entries = new ArrayList<>();
    try(  FileInputStream fis = new FileInputStream(targzPath);
        GzipCompressorInputStream gin = new GzipCompressorInputStream(fis);
        PipedOutputStream pos = new PipedOutputStream();
        PipedInputStream pin = new PipedInputStream();
        TarArchiveInputStream tais = new TarArchiveInputStream(pin)
    ){
      pin.connect(pos);
      new Thread(()->{
        try{
          int size = 0;
          byte[] buf = new byte[1024];
          while((size = gin.read(buf)) >= 0){
            pos.write(buf, 0, size);
            pos.flush();
          }
        }catch(IOException e){
          throw new RuntimeException(e.getMessage(), e);
        }
      }).start();
      ArchiveEntry entry = null;
      while((entry = tais.getNextEntry()) != null){
        entries.add(entry);
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    return entries;
  }
  /**
   * Predicate→ArchiveEntryコレクション.
   * @param targzPath tar and gzip ファイルパス、 *.tar.gz
   * @param p Predicate<ArchiveEntry> ファイルのArchiveEntry の Predicate
   * @return Collection<ArchiveEntry>
   */
  public static Collection<ArchiveEntry> entries(String targzPath, Predicate<ArchiveEntry> p){
    Collection<ArchiveEntry> entries = new ArrayList<>();
    try(  FileInputStream fis = new FileInputStream(targzPath);
        GzipCompressorInputStream gin = new GzipCompressorInputStream(fis);
        PipedOutputStream pos = new PipedOutputStream();
        PipedInputStream pin = new PipedInputStream();
        TarArchiveInputStream tais = new TarArchiveInputStream(pin)
    ){
      pin.connect(pos);
      try{
        Thread th = new Thread(()->{
          try{
            int size = 0;
            byte[] buf = new byte[1024];
            while((size = gin.read(buf)) >= 0){
              pos.write(buf, 0, size);
              pos.flush();
            }
          }catch(NullPointerException e){
            throw new RuntimeException(e.getMessage(), e);
          }catch(IOException e){
            throw new RuntimeException(e.getMessage(), e);
          }
        });
        th.start();
        th.join();
      }catch(InterruptedException e1){
      }
      ArchiveEntry entry = null;
      while((entry = tais.getNextEntry()) != null){
        if (p.equals(entry)) entries.add(entry);
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    return entries;
  }
  /**
   * Predicateファイル展開.
   * @param targzPath targzPath tar and gzip ファイルパス、 *.tar.gz
   * @param dirPath 展開先パス
   * @param p Predicate<ArchiveEntry> 展開するファイルのArchiveEntry の Predicate
   */
  public static void predicateOpen(String targzPath, String dirPath, Predicate<ArchiveEntry> p){
    try(  FileInputStream fis = new FileInputStream(targzPath);
        GzipCompressorInputStream gin = new GzipCompressorInputStream(fis);
        PipedOutputStream pos = new PipedOutputStream();
        PipedInputStream pin = new PipedInputStream();
        TarArchiveInputStream tais = new TarArchiveInputStream(pin)
    ){
      pin.connect(pos);
      try{
        Thread th = new Thread(()->{
          try{
            int size = 0;
            byte[] buf = new byte[1024];
            while((size = gin.read(buf)) >= 0){
              pos.write(buf, 0, size);
              pos.flush();
            }
          }catch(NullPointerException e){
            throw new RuntimeException(e.getMessage(), e);
          }catch(IOException e){
            throw new RuntimeException(e.getMessage(), e);
          }
        });
        th.start();
        th.join();
      }catch(InterruptedException e1){
      }
      ArchiveEntry entry = null;
      while((entry = tais.getNextEntry()) != null){
        if (p.test(entry) && !entry.isDirectory()){
          String[] names = entry.getName().split("/");
          try(FileOutputStream fos = new FileOutputStream(dirPath + "/" + names[names.length-1]);
            BufferedOutputStream bos = new BufferedOutputStream(fos)){
            int size = 0;
            byte[] buf = new byte[1024];
            while((size = tais.read(buf)) > 0){
              bos.write(buf, 0, size);
            }
          }catch(IOException e){
            throw new RuntimeException(e.getMessage(), e);
          }
        }
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
  }
  /**
   * GZIP解凍実行.
   * @param gzipPath gzip ファイルPATH   *.gz
   * @param dirPath 展開先ディレクトリPATH
   */
  public static void openGz(String gzipPath, String dirPath){
    String tarname = gzipPath.substring(gzipPath.lastIndexOf("/") + 1).replaceAll("\\.gz$", "");
    try(FileInputStream fis = new FileInputStream(gzipPath);GzipCompressorInputStream gin = new GzipCompressorInputStream(fis);
      FileOutputStream  fos = new FileOutputStream(dirPath + "/" + tarname)
    ){
      int size = 0;
      byte[] buf = new byte[1024];
      while((size = gin.read(buf)) > 0){
        fos.write(buf, 0, size);
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
  }
}

単純に tar だけだったら、、

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Predicate;
import java.util.function.Supplier;

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;

/**
 * tar 圧縮/解凍プロセッサ.
 * (規則)
 *     tarファイル名拡張子 → .tar
 * (圧縮)
 * Supplier<Collection<FileCollection>> = ファイルコレクション(FileCollection)で指定する対象を
 * Collection<String> compress(String targzPath) で圧縮する。
 * メソッド戻り値は、tarエントリ名 Collection
 * (展開)
 * void decompress(String targzPath, String dirPath) で展開する。
 */
public interface TarProcessor extends Supplier<Collection<FileCollection>>{

  /**
   * tar 圧縮実行.
   * <PRE>
   * Supplier で渡す FileCollection の渡し方で単一か複数か決まる。
   * 例1)
   *    // targetPath配下を圧縮対象にする場合
   *    List<FileCollection> fileCollections =
   *    Arrays.stream(new File(targetPath).listFiles()).map(e->FileCollection.of(e.getAbsolutePath())).collect(Collectors.toList());
   *    TarProcessor processor = ()->fileCollections;
   *    Collection<String> entries = processor.compress(tarPath);
   *
   * 例2)
   *    // 1つのディレクトリツリーで圧縮
   *    FileCollection fileCollection = FileCollection.of(targetPath);
   *    TarProcessor processor = ()->Arrays.asList(fileCollection);
   *    Collection<String> entries = processor.compress(tarPath);
   *
   * </PRE>
   * @param tarPath 作成する tar ファイルパス、 *.tar
   * @return tarエントリ名 Collection
   */
  public default Collection<String> compress(String tarPath){
    Collection<String> entries = new ArrayList<>();
    // tar 生成
    try(FileOutputStream out = new FileOutputStream(tarPath);TarArchiveOutputStream taos = new TarArchiveOutputStream(out)){
      get().forEach(fc->{
        String prefix = fc.getFile().getParentFile().getAbsolutePath().replaceAll("\\\\", "/");
        fc.scan(f->{
          try{
            if (f.isDirectory()){
              TarArchiveEntry entry = new TarArchiveEntry(fc.getFile(), f.getAbsolutePath().replaceAll("\\\\", "/").replaceFirst(prefix, ""));
              taos.putArchiveEntry(entry);
              taos.closeArchiveEntry();
              entries.add(entry.getName());
              return;
            }
            TarArchiveEntry entry = new TarArchiveEntry(f, f.getAbsolutePath().replaceAll("\\\\", "/").replaceFirst(prefix, ""));
            taos.putArchiveEntry(entry);
            //taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
            taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
            try(FileInputStream fis = new FileInputStream(f); BufferedInputStream bis = new BufferedInputStream(fis)){
              int size = 0;
              byte[] buf = new byte[1024];
              while((size = bis.read(buf)) > 0){
                taos.write(buf, 0, size);
              }
            }
            taos.closeArchiveEntry();
            entries.add(entry.getName());
          }catch(IOException ex){
            throw new RuntimeException(ex);
          }
        });
      });
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    return entries;
  }

  /**
   * tar ファイル展開.
   * @param tarPath tar ファイルパス、 *.tar
   * @param dirPath 展開先ディレクトリPATH
   * @return 展開された tar エントリ名
   */
  public static Collection<String> decompress(String tarPath, String dirPath){
    Collection<String> entries = new ArrayList<>();
    // tar 展開
    try(FileInputStream fis = new FileInputStream(tarPath); TarArchiveInputStream tais = new TarArchiveInputStream(fis)){
      ArchiveEntry entry = null;
      while((entry = tais.getNextEntry()) != null){
        File file = new File(dirPath + "/" + entry.getName());
        if (entry.isDirectory()){
          file.mkdirs();
          entries.add(entry.getName());
          continue;
        }
        if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); }
        try(FileOutputStream fos = new FileOutputStream(file); BufferedOutputStream bos = new BufferedOutputStream(fos)){
          int size = 0;
          byte[] buf = new byte[1024];
          while((size = tais.read(buf)) > 0){
            bos.write(buf, 0, size);
          }
        }
        entries.add(entry.getName());
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    return entries;
  }
  /**
   * エントリ名コレクション.
   * @param tarPath tar ファイルパス、 *.tar
   * @return Collection<String>
   */
  public static Collection<String> viewPath(String tarPath){
    Collection<String> entries = new ArrayList<>();
    try(FileInputStream fis = new FileInputStream(tarPath); TarArchiveInputStream tais = new TarArchiveInputStream(fis)){
      ArchiveEntry entry = null;
      while((entry = tais.getNextEntry()) != null){
        entries.add(entry.getName());
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    return entries;
  }
  /**
   * ArchiveEntryコレクション.
   * @param tarPath tar ファイルパス、 *.tar
   * @return Collection<ArchiveEntry>
   */
  public static Collection<ArchiveEntry> entries(String tarPath){
    Collection<ArchiveEntry> entries = new ArrayList<>();
    try(FileInputStream fis = new FileInputStream(tarPath); TarArchiveInputStream tais = new TarArchiveInputStream(fis)){
      ArchiveEntry entry = null;
      while((entry = tais.getNextEntry()) != null){
        entries.add(entry);
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    return entries;
  }
  /**
   * Predicate→ArchiveEntryコレクション.
   * @param tarPath tarPath tar ファイルパス、 *.tar
   * @param p ファイルのArchiveEntry の Predicate
   * @return Collection<ArchiveEntry>
   */
  public static Collection<ArchiveEntry> entries(String tarPath, Predicate<ArchiveEntry> p){
    Collection<ArchiveEntry> entries = new ArrayList<>();
    try(FileInputStream fis = new FileInputStream(tarPath); TarArchiveInputStream tais = new TarArchiveInputStream(fis)){
      ArchiveEntry entry = null;
      while((entry = tais.getNextEntry()) != null){
        if (p.equals(entry)) entries.add(entry);
      }
    }catch(IOException ex){
      throw new RuntimeException(ex.getMessage(), ex);
    }
    return entries;
  }
}

Webページ入力フィールド制限、ToolTip

今まで過去から何度も書いたものをまとめる。
過去。。。
入力フィールドでよく使いそうな jQuery 処理のメモ - Oboe吹きプログラマの黙示録
input タグ type="number" のスピンボタンを非表示 - Oboe吹きプログラマの黙示録
chromeで0以上の整数入力に限定する - Oboe吹きプログラマの黙示録
chrome で、何がなんでも全角入力させない - Oboe吹きプログラマの黙示録
chrome で、何がなんでも全角入力させない - Oboe吹きプログラマの黙示録
全角英数字→半角英数字 - Oboe吹きプログラマの黙示録

chrome限定ではなく、Edge 、IE11 でも有効で、尚且つ、全角モードやNumLockキーなど ToolTip で警告

CSSとして以下を用意 input-support.css

@CHARSET "UTF-8";
/*--- type="number" のスピンボタンを非表示 --------*/
input[type="number"]{
  -moz-appearance:textfield;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button{
  -webkit-appearance: none;
  margin: 0;
}
/*---- for ToolTip --------------------------------*/
.ui-tooltip {
  font-size: 14px;
  border-radius: 6px;
  color: #000000;
  background: #ffffe0;
  z-index: 20002;
  opacity: 0.85;
  filter: alpha(opacity=85);
  -ms-filter: "alpha(opacity=85)";
}

jQuery ソース

/**
 * input-support.js
 */
/* for 全角数字入力→ 半角数字 */
var numberConvert = function(value){
  var str = new String(value);
  var han = str.replace(/[0-9]/g, function(s){ return String.fromCharCode(s.charCodeAt(0)-0xFEE0); });
  return han.replace(/[\-|ー|―|-]/, function(s){ return '-'; }).replace(/[。.]/g, ".");
};
/* 全角英数字→半角英数字 */
var zenkakuTohan = function(value){
  var str = new String(value);
  var han = str.replace(/[A-Za-z0-9]/g, function(s){ return String.fromCharCode(s.charCodeAt(0)-0xFEE0); });
  return han.replace(/[\-|ー|―|-]/g, "-").replace(/[。.]/g, ".");
};
/* 数値→3桁区切り : 逆は、.replace(/,/g, ''); を使う */
var digitFormat = function(str){
   var num = new String(str).replace(/,/g, "");
   while(num != (num = num.replace(/^(-?\d+)(\d{3})/, "$1,$2")));
   return num;
};
/* 全角数字→半角数字 */
var zenkakuNumToHan = function(txt){
  return txt.replace(/[0-9]/g, function(s){ return String.fromCharCode(s.charCodeAt(0)-0xFEE0); });
};
/* ひらがな→カタカナ */
var hirakanaTokatakana = function(txt){
   return txt.replace(/[ぁ-ん]/g, function(s){ return String.fromCharCode(s.charCodeAt(0)+0x0060); });
}
/* カタカナ→ひらがな */
var hirakanaTokatakana = function(txt){
   return txt.replace(/[ァ-ン]/g, function(s){ return String.fromCharCode(s.charCodeAt(0)-0x0060); });
}
/* 日付入力チェック */
var isValidDate = function(s){
   if (!s.match(/^\d{4}\/(0{0,1}[1-9]|1[012])\/(0{0,1}[1-9]|[12][0-9]|3[01])$/)){
      return true;
   }
   var ary = s.split(/\//);
   var y = ary[0];
   var m = parseInt(ary[1], 10);
   var d = parseInt(ary[2], 10);
   if(m < 1 || m > 12 || d < 1 || d > 31) {
      return true;
   }
   var dt = new Date(y, m - 1, d, 0, 0, 0, 0);
   if(dt.getFullYear() != y || dt.getMonth() != m - 1 || dt.getDate() != d){
      return true;
   }
   return false;
};
/**
 * Tooltip 実行
 */
var callTooltip = function(input, message){
  $(input).prop("title", "");
  $(input).tooltip({
    content: message,
    show: { effect: "slideDown" },
    hide: { effect: "slideDown" },
  });
  $(input).tooltip("open");
  setTimeout("$('" + input + "').tooltip('close');$('" + input + "').tooltip('destroy');", 1000);
};

/**
 * 強制入力セット関数。
 *     class="ank-text" class="num-text" 、id が必須
 * 半角英数字(コピーペースト不可) : ank-text
 * 半角英数字(コピーペースト可能) : ank-pastable-text
 * 半角数字                         : num-text
 * 時刻 HH:mm:ss                    : time-text
 * 金額入力(半角)                 : money-text
 */
var inputHankakuForce = function(){
  // 半角英数
  $('input.ank-text').focus(function(eo){
    $(this).prop('type','tel');
  }).blur(function(eo){
    $(this).prop('type','text');
    $(this).val(zenkakuTohan($(this).val()));
  }).bind("paste",function(){
    return false;
  }).keyup(function(eo){
    $(this).val(zenkakuTohan($(this).val()));
    if (eo.keyCode==229){return false;  }
  }).keydown(function(eo){
    if ((eo.keyCode >= 33 && eo.keyCode <= 39)|| eo.keyCode==40 
       || eo.keyCode==45 || eo.keyCode==46 || eo.keyCode==12){
      if (event.getModifierState("NumLock")===false){
        callTooltip("#"+$(this).prop("id"), "NumLock OFF(消灯) になってます");
      }
    }
    if (eo.keyCode==229){
      callTooltip("#"+$(this).prop("id"), "全角入力になってます");
      return false;
    }
  });
  // 半角英数(コピーペースト可能)
  $('input.ank-pastable-text').focus(function(eo){
    $(this).prop('type','tel');
  }).blur(function(eo){
    $(this).prop('type','text');
    $(this).val(zenkakuTohan($(this).val()));
  }).keyup(function(eo){
    $(this).val(zenkakuTohan($(this).val()));
    if (eo.keyCode==229){return false;  }
  }).keydown(function(eo){
    if ((eo.keyCode >= 33 && eo.keyCode <= 39)|| eo.keyCode==40
         || eo.keyCode==45 || eo.keyCode==46 || eo.keyCode==12){
      if (event.getModifierState("NumLock")===false){
        callTooltip("#"+$(this).prop("id"), "NumLock OFF(消灯) になってます");
      }
    }
    if (eo.keyCode==229){
      callTooltip("#"+$(this).prop("id"), "全角入力になってます");
      return false;
    }
  });
  // 半角数字
  $('input.num-text').focus(function(eo){
    $(this).prop('type','tel');
  }).blur(function(eo){
    $(this).prop('type','text');
    $(this).val(numberConvert($(this).val()));
  }).bind("paste",function(){
    return false;
  }).keyup(function(eo){
    $(this).val(numberConvert($(this).val()).replace(/[^\-0-9]/g, ''));
    if (eo.keyCode==229){return false;  }
  }).keydown(function(eo){
    if ((eo.keyCode >= 33 && eo.keyCode <= 39)|| eo.keyCode==40
        || eo.keyCode==45 || eo.keyCode==46 || eo.keyCode==12){
      if (event.getModifierState("NumLock")===false){
        callTooltip("#"+$(this).prop("id"), "NumLock OFF(消灯) になってます");
      }
    }
    if (eo.keyCode==229){
      callTooltip("#"+$(this).prop("id"), "全角入力になってます");
      return false;
    }
  });
  // 時刻 HH:mm:ss
  $('input.time-text').focus(function(eo){
    $(this).prop('type','tel');
  }).blur(function(eo){
    $(this).prop('type','text');
    $(this).val(numberConvert($(this).val()));
    if (!$(this).val().match(/^(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[0-5][0-9]):(0[0-9]|[0-5][0-9])$/)){
      callTooltip("#"+$(this).prop("id"), "HH:mm:ss 形式でありません");
    }
  }).bind("paste",function(){
    return false;
  }).keyup(function(eo){
    $(this).val(numberConvert($(this).val()).replace(/:/g, ':').replace(/[^0-9:]/g, ''));
    if (eo.keyCode==229){return false;  }
  }).keydown(function(eo){
    if ((eo.keyCode >= 33 && eo.keyCode <= 39)|| eo.keyCode==40
         || eo.keyCode==45 || eo.keyCode==46 || eo.keyCode==12){
      if (event.getModifierState("NumLock")===false){
        callTooltip("#"+$(this).prop("id"), "NumLock OFF(消灯) になってます");
      }
    }
    if (eo.keyCode==229){
      callTooltip("#"+$(this).prop("id"), "全角入力になってます");
      return false;
    }
  });
  // 日付入力 yyyy/MM/dd
  $('input.date-text').focus(function(eo){
    $(this).prop('type','tel');
  }).blur(function(eo){
    $(this).prop('type','text');
    $(this).val(numberConvert($(this).val()));
    if (isValidDate($(this).val())){
      callTooltip("#"+$(this).prop("id"), "日付エラー yyyy/MM/dd");
    }
  }).bind("paste",function(){
    return false;
  }).keyup(function(eo){
    $(this).val(numberConvert($(this).val()).replace(///g, '/').replace(/[^0-9\/]/g, ''));
    if (eo.keyCode==229){return false;  }
  }).keydown(function(eo){
    if ((eo.keyCode >= 33 && eo.keyCode <= 39)|| eo.keyCode==40
          || eo.keyCode==45 || eo.keyCode==46 || eo.keyCode==12){
      if (event.getModifierState("NumLock")===false){
        callTooltip("#"+$(this).prop("id"), "NumLock OFF(消灯) になってます");
      }
    }
    if (eo.keyCode==229){
      callTooltip("#"+$(this).prop("id"), "全角入力になってます");
      return false;
    }
  });

  // 金額入力(半角)
  $('input.money-text').focus(function(eo){
    $(this).prop('type','tel');
  }).blur(function(eo){
    $(this).prop('type','text');
    $(this).val(numberConvert($(this).val()));
  }).bind("paste",function(){
    return false;
  }).keyup(function(eo){
    var value = numberConvert($(this).val()).replace(/[^\-0-9]/g, '');
    if (value.match(/^(|-)[0-9]+$/)){
      $(this).val(digitFormat(value));
    }
    if (eo.keyCode==229){return false;  }
  }).keydown(function(eo){
    if ((eo.keyCode >= 33 && eo.keyCode <= 39)|| eo.keyCode==40
          || eo.keyCode==45 || eo.keyCode==46 || eo.keyCode==12){
      if (event.getModifierState("NumLock")===false){
        callTooltip("#"+$(this).prop("id"), "NumLock OFF(消灯) になってます");
      }
    }
    if (eo.keyCode==229){
      callTooltip("#"+$(this).prop("id"), "全角入力になってます");
      return false;
    }
  });
  // IPアドレス
  $('input.ip-text').focus(function(eo){
    $(this).prop('type','tel');
  }).blur(function(eo){
    $(this).prop('type','text');
    $(this).val(numberConvert($(this).val()));
    var a = $(this).val().split(/\./);
    if (a.length != 4){
      callTooltip("#"+$(this).prop("id"), "IP address エラー");
    }
    var error = 0;
    $.each(a, function(i, t){
      if (t < 0 || 255 < t) error = 1;
    });
    if (error==1){
      callTooltip("#"+$(this).prop("id"), "IP address エラー");
    }
  }).bind("paste",function(){
    return false;
  }).keyup(function(eo){
    $(this).val(numberConvert($(this).val()).replace(/[^0-9\.]/g, ''));
    if (eo.keyCode==229){return false;  }
  }).keydown(function(eo){
    if ((eo.keyCode >= 33 && eo.keyCode <= 39)|| eo.keyCode==40
         || eo.keyCode==45 || eo.keyCode==46 || eo.keyCode==12){
      if (event.getModifierState("NumLock")===false){
        callTooltip("#"+$(this).prop("id"), "NumLock OFF(消灯) になってます");
      }
    }
    if (eo.keyCode==229){
      callTooltip("#"+$(this).prop("id"), "全角入力になってます");
      return false;
    }
  });
};

HTML

<link rel="stylesheet" type="text/css" href="../css/jquery-ui-1.12.1.min.css">
<script type="text/javascript" src="../js/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="../js/jquery-ui-1.12.1.min.js"></script>

<link rel="stylesheet" type="text/css" href="input-support.css">
<script type="text/javascript" src="input-support.js"></script>

<script type="text/javascript">
$(function(){
  inputHankakuForce();
});
</script>
<style type="text/css">
table{ white-space: nowrap; }
</style>
</head>
<body>
<form>
<table>
  <tr>
    <td>半角英数字 only(コピーペースト不可)</td>
    <td><input id="a1" type="text" class="ank-text"></td>
  </tr>
  <tr>
    <td>半角英数字 only(コピーペースト可能)</td>
    <td><input id="a2" type="text" class="ank-pastable-text"></td>
  </tr>
  <tr>
    <td>半角数字 only(全角数字→半角自動変換)</td>
    <td><input id="n1" type="text" class="num-text"></td>
  </tr>
  <tr>
    <td>時刻(hh:mm:ss)</td>
    <td><input id="t1" type="text" class="time-text" maxlength="8"></td>
  </tr>
  <tr>
    <td>日付(yyyy/MM/dd)</td>
    <td><input id="t1" type="text" class="date-text" maxlength="10"></td>
  </tr>
  <tr>
    <td>金額(3桁区切り)</td>
    <td><input id="y1" type="text" class="money-text"></td>
  </tr>
  <tr>
    <td>IP-address</td>
    <td><input id="y1" type="text" class="ip-text" maxlength="15"></td>
  </tr>
</table>

Java9 の HttpClient を試す

Java9 HttpClient インキュベーターなので、この先どうなるか判らないが、
Apatch HTTPClient を使わなくて済むようになるのか?
とりあえず試してみる。
モジュール使用宣言を用意する必要があり、module-info..java を次のように用意する。

module sample{
    requires jdk.incubator.httpclient;
}

パッケージは、jdk.incubator.http だが、requires で書くのは、jdk.incubator.httpclient
import するもの

import java.net.URI;
import java.nio.charset.StandardCharsets;
import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;

まずは、同期型

try{
   HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.google.com"))
   .GET()
   .build();
   HttpResponse<String> res = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
   .followRedirects(HttpClient.Redirect.SAME_PROTOCOL).build()
   .send(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8));
   // HttpResponse を参照
   res.headers().map().entrySet().forEach(e->{
      System.out.println(e.getKey() + "→" + e.getValue() );
   });
   System.out.println(res.body());
}catch(Exception e){
   e.printStackTrace();
} 

非同期型は、sendAsync を使う、HttpResponse.BodyHandlerを処理することになる。

HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.google.com"))
.GET()
.build();

CompletableFuture<HttpResponse<String>> future
= HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.SAME_PROTOCOL).build()
.sendAsync(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8));

// CompletableFuture<HttpResponse<String>> を join する。
future.handle((r, t)->{
   System.out.println(Thread.currentThread().getName());
   r.headers().map().entrySet().forEach(e->{
      System.out.println(e + "→" + e.getValue() );
   });
   System.out.println(r.body());
   return r;
}).join();

ExecutorService  handleAsync で、、、

final ExecutorService service = Executors.newFixedThreadPool(4);
HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.google.com"))
.GET()
.build();

HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.SAME_PROTOCOL)
.executor(service)
.build()
.sendAsync(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8))
.handleAsync((r, t)->{
   System.out.println(Thread.currentThread().getName());
   r.headers().map().entrySet().forEach(e->{
      System.out.println(e + "→" + e.getValue() );
   });
   System.out.println(r.body());
   return r;
}).join();
service.shutdown();

同じリクエスト、ExecutorService  whenCompleteAsync で、例外発生時も捕捉する場合

final ExecutorService service = Executors.newFixedThreadPool(4);
HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.google.com"))
.GET()
.build();

HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.SAME_PROTOCOL)
.executor(service)
.build()
.sendAsync(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8))
.whenCompleteAsync((r, x)->{
   r.headers().map().entrySet().forEach(e->{
      System.out.println(e + "→" + e.getValue() );
   });
   System.out.println(r.body());
   Optional.ofNullable(x).ifPresent(t->t.printStackTrace());
}).join();

これは、java.util.concurrent.CompletionException でラップされた例外を捕捉することになる。
URL が誤ってれば、java.nio.channels.UnresolvedAddressExceptionなどがラップされて捕捉される。

サンプルとしてもっと簡単に、返されるレスポンス BODYだけを標準出力するなら、、、

HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.SAME_PROTOCOL)
.executor(service)
.build()
.sendAsync(request, HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8))
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();

結果 body をファイル出力するなら。 HttpResponse.BodyHandler.asFile を使う。

HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.SAME_PROTOCOL)
.executor(service)
.build()
.sendAsync(request, HttpResponse.BodyHandler.asFile(FileSystems.getDefault().getPath("c:/work/test")))
.thenApply(HttpResponse::body)
//.thenAccept(System.out::println) /* ファイル出力した時の path を標準出力 */ 
.join();

↑ //.thenAccept(System.out::println)  は、ファイル出力もするけど、標準出力もするようにもできる。