2017年10月25日水曜日

【Java】try with resourcesのカバレッジ

◆EclEmmaでどうしても通せないルートがある。
 各サイトが説明しているようにカバレッジに拘る必要はないのだが、
テスト漏れを即検出したいので、できれば100%にしたい。
しかし、下記のようにtry-with-resources 文など一部のコードがEclEmmaで全ルートパスできない。

ツールチップを表示すると7ルートミス。

Jmockit(下記コード参照)で1まで行くのだが、あと1ルートがパスできない。

◆テスト対象
package sampl.junitsample;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class App {
 public static void main(String[] args) throws IOException {
  HttpURLConnection conn = (HttpURLConnection) new URL("http://localhost").openConnection();
  conn.setDoOutput(true);

  try (OutputStream st = conn.getOutputStream()) {
   st.write("abc".getBytes());
  }
 }
}

◆テストコード
package sampl.junitsample;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.junit.Test;

import mockit.Mock;
import mockit.MockUp;

public class AppTest {
 @Test
 public void testApp1() throws IOException {
  App.main(null);
 }

 @Test
 public void testApp2() throws IOException {
  new MockUp<sun.net.www.protocol.http.HttpURLConnection>() { // 何故かsun.~
   @Mock
   public OutputStream getOutputStream() throws IOException {
    throw new IOException("mock exception.");
   }
  };
  try {
   App.main(null);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 @Test
 public void testApp3() throws IOException {
  new MockUp<java.io.ByteArrayOutputStream>() { // デバッガで実際の型を調べるとPosterOutputStream
   @Mock
   public void write(byte[] b) throws IOException {
    throw new IOException("mock exception.");
   }
  };
  try {
   App.main(null);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 @Test
 public void testApp5() throws IOException {
  new MockUp<ByteArrayOutputStream>() { // デバッガで実際の型を調べるとPosterOutputStream
   @Mock
   public void write(byte[] b) throws IOException {
    throw new IOException("mock exception.");
   }

   @Mock
   public void close() throws IOException {
    throw new IOException("mock exception.");
   }
  };

  try {
   App.main(null);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 @Test
 public void testApp6() throws IOException {
  new MockUp<sun.net.www.protocol.http.HttpURLConnection>() { // 何故かsun.~
   @Mock
   public OutputStream getOutputStream() throws IOException {
    return null;
   }
  };
  new MockUp<ByteArrayOutputStream>() {
   @Mock
   public void write(byte[] b) throws IOException {
    return;
   }

   @Mock
   public void close() throws IOException {
    throw new IOException("mock exception.");
   }
  };

  try {
   App.main(null);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}

◆Maven(pom.xml)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>sampl</groupId>
 <artifactId>junitsample</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>junitsample</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <dependencies>
  <!-- https://mvnrepository.com/artifact/junit/junit -->
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.4</version>
   <scope>test</scope>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.jmockit/jmockit -->
  <dependency>
   <groupId>org.jmockit</groupId>
   <artifactId>jmockit</artifactId>
   <version>1.35</version>
   <scope>test</scope>
  </dependency>
 </dependencies>

</project>

◆環境

  • Eclipse:4.7(pleiades)
  • EclEmma:3.0.0.201706140232


2017年8月21日月曜日

【Java】File Downloadその3(commons-io)

String str = IOUtils.toString(new URL(targetUrl), Charset.defaultCharset());
FileUtils.writeStringToFile(new File(filePath), str, Charset.defaultCharset());

ファイル保存される。ちょっとしたものは上記で良いが、apache httpclientを推奨。apache httpclientはアクセス先がページ移動(301)したとき等に自動で移動してくれる。

◆ソースコード
package sample.httpget;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

public class App {
  public static void main(String[] args) {
    try {
      download("http://google.com/", "/tmp/google.htm", Charset.forName("Shift_JIS"));
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public static void download(String targetUrl, String filePath, Charset ch) throws IOException {
    String str = IOUtils.toString(new URL(targetUrl), ch);
    FileUtils.writeStringToFile(new File(filePath), str, Charset.defaultCharset());
  }
}

◆pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>sample</groupId>
 <artifactId>HttpGet</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>HttpGet</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.1</version>
   <scope>test</scope>
  </dependency>
  <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
  <dependency>
   <groupId>commons-io</groupId>
   <artifactId>commons-io</artifactId>
   <version>2.5</version>
  </dependency>

 </dependencies>
</project>

2017年8月20日日曜日

【Java】簡易HTTP Post

■3つ程。3番目はcommons-ioが必要。

◆ソースコード
package sample.httppost;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;

import org.apache.commons.io.IOUtils;

import javafx.util.Pair;

public class App {
  public static void main(String[] args) {
    try {
      sendPost1("http://localhost:8090/", "abc");
      System.out.println(sendPost2("http://localhost:8090/", "abc"));
      System.out.println(sendPost3("http://localhost:8090/", "abc", Charset.defaultCharset()));
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public static void sendPost1(String targetUrl, String sendStr) throws IOException {
    URLConnection conn = new URL(targetUrl).openConnection();
    conn.setDoOutput(true);

    try (OutputStream st = conn.getOutputStream()) {
      st.write(sendStr.getBytes());
    }
  }

  public static int sendPost2(String targetUrl, String sendStr) throws IOException {
    HttpURLConnection conn = (HttpURLConnection) new URL(targetUrl).openConnection();
    conn.setDoOutput(true);

    try (OutputStream st = conn.getOutputStream()) {
      st.write(sendStr.getBytes());
    }
    return conn.getResponseCode();
  }


  public static Pair<Integer,String> sendPost3(String targetUrl, String sendStr, Charset chSet) throws IOException {
    HttpURLConnection conn = (HttpURLConnection) new URL(targetUrl).openConnection();
    conn.setDoOutput(true);
    conn.setRequestProperty("Accept-Charset", chSet.name());
    conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + chSet.name());
    conn.setRequestProperty("User-Agent", "Mozilla/5.0");

    try (OutputStream st = conn.getOutputStream()) {
      st.write(sendStr.getBytes());
    }

    try (InputStream st = conn.getInputStream()) {
      int code = conn.getResponseCode();
      String str = IOUtils.toString(st, chSet);
      return new Pair<Integer, String>(code, str);
    }
  }
}

◆pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>sample</groupId>
 <artifactId>HttpPost</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>HttpPost</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.1</version>
   <scope>test</scope>
  </dependency>
  <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
  <dependency>
   <groupId>commons-io</groupId>
   <artifactId>commons-io</artifactId>
   <version>2.5</version>
  </dependency>
 </dependencies>
</project>

【Java】File Downloadその2(commons-io)

FileUtils.copyURLToFile(new URL("http://google.com/"), new File("/tmp/google.htm"));

1行ですむが文字コードが指定できない。保存したファイルをブラウザで開くとサイトによっては文字化けする。何故だ…
ちょっとしたものは上記で良いが、apache httpclientを推奨。apache httpclientはアクセス先がページ移動(301)したとき等に自動で移動してくれる。

◆ソースコード
package sample.httpget;

import java.io.File;
import java.io.IOException;
import java.net.URL;

import org.apache.commons.io.FileUtils;

public class App {
 public static void main(String[] args) {
  try {
   download("http://google.com/", "/tmp/google.htm");
  } catch (IOException e) {
   e.printStackTrace();
  }
 }

 public static void download(String targetUrl, String filePath) throws IOException {
  FileUtils.copyURLToFile(new URL(targetUrl), new File(filePath)); // 文字コードが設定できないのでサイトによっては文字化けする?
 }
}



◆pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>sample</groupId>
 <artifactId>HttpGet</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>HttpGet</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.1</version>
   <scope>test</scope>
  </dependency>
  <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
  <dependency>
   <groupId>commons-io</groupId>
   <artifactId>commons-io</artifactId>
   <version>2.5</version>
  </dependency>

 </dependencies>
</project>



【Java】File Downloadその1(commons-io)

IOUtils.toString(new URL("http://google.com/"), "Shift_JIS");

ちょっとしたものは上記で良いが、apache httpclientを推奨。apache httpclientはアクセス先がページ移動(301)したとき等に自動で移動してくれる。

◆ソースコード
package sample.httpget;

import java.io.IOException;
import java.net.URL;

import org.apache.commons.io.IOUtils;

public class App {
  public static void main(String[] args) {
    try {
      System.out.println(download("http://google.com/", "Shift_JIS"));
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public static String download(String targetUrl, String ch) throws IOException {
    return IOUtils.toString(new URL(targetUrl), ch);
  }
}

◆pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>sample</groupId>
 <artifactId>HttpGet</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>HttpGet</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.1</version>
   <scope>test</scope>
  </dependency>
  <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
  <dependency>
   <groupId>commons-io</groupId>
   <artifactId>commons-io</artifactId>
   <version>2.5</version>
  </dependency>

 </dependencies>
</project>



2017年8月19日土曜日

【Java】ServletでFile download(nio)

Files.copyでiostreamが指定可能とのことなので、試してみる。ちなみにサーブレットコンテナはJetty。
◆ソース(必要に応じてtry-catchを入れること。でないとexception発生時にexceptionがブラウザに出てしまう。)
package sample.niocopy;

import java.io.BufferedOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;

public class MyServlet extends HttpServlet {
  public static void main(String[] args) throws Exception {
    ServletHandler handler = new ServletHandler();
    handler.addServletWithMapping(MyServlet.class, "/*");

    Server server = new Server(8090);
    server.setHandler(handler);
    server.start();
    server.join();
  }

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse res) {
    try {
      Path uri = Paths.get("c:/tmp" + req.getRequestURI());

      if (Files.notExists(uri) || Files.isDirectory(uri)) {
        res.setStatus(404);
      } else {
        res.setContentType("application/octet-stream");
        res.setContentType("text/html; charset=UTF-8");
        res.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", uri.getFileName()));
        try (BufferedOutputStream bos = new BufferedOutputStream(res.getOutputStream())) {
          Files.copy(uri, bos);
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

◆速度

◆結論
普通に使える。パット見メモリリークはないが、要確認。BufferedOutputStream,BufferedInputStreamの2つを利用した場合と比較したが、速度は同じ。

◆pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>sample</groupId>
 <artifactId>NioCopy</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>NioCopy</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <jetty.version>9.3.6.v20151106</jetty.version>
 </properties>

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.1</version>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>org.eclipse.jetty</groupId>
   <artifactId>jetty-server</artifactId>
   <version>${jetty.version}</version>
  </dependency>
  <dependency>
   <groupId>org.eclipse.jetty</groupId>
   <artifactId>jetty-webapp</artifactId>
   <version>${jetty.version}</version>
  </dependency>
 </dependencies>
</project>

◆環境
  • 速度確認した仮想マシン
    • CPU core数:4
    • メモリ:2.5GB
    • OS:Windows10 Pro x64
    • 仮想マシン:VMWare vShpere ESXi6.5
    • Java:1.8

  • ホストPCの性能(参考)
    • CPU:Intel Core i5-4250U(2Core 2HT)
    • メモリ:16GB
    • ドライブ:SSD(ウルトラ II mSATA SSD SDMSATA-512G-G25C)
    • 仮想マシン:VMWare ESXi6.5上の仮想マシン

2017年7月26日水曜日

【Java】【Spring Integration】Service Activatorで例外送出

Service Activatorで例外送出するとどうなるのか?
下記のsaServiceで例外を発生させる。

◆integration-graph

◆ファイル構成

ソースコードのvalはlombokを使用。
◆Main.java
public class Main {
  public static void main(String[] args) {
    try (val ctx = new ClassPathXmlApplicationContext("SpringIntegration.xml")) {
      val inCh = ctx.getBean("inCh", MessageChannel.class);
      val outCh = ctx.getBean("outCh", PollableChannel.class);

      inCh.send(MessageBuilder.withPayload("test").build());
      System.out.println(outCh.receive());

      System.in.read();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

◆SampleService
@Component
public class SampleService {
  @ServiceActivator
  public String Exec(String msg) throws IllegalArgumentException {
    throw new IllegalArgumentException("err");
  }
}

◆SpringIntegration.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
  xmlns:int="http://www.springframework.org/schema/integration"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
    http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-4.1.xsd">

  <context:component-scan base-package="sample.springint.exception" />
  <int:channel id="inCh" />
  <int:channel id="outCh">
    <int:queue capacity="10" />
  </int:channel>

  <int:service-activator id="saService"
    input-channel="inCh" output-channel="outCh" ref="sampleService" />
</beans>

◆log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration
  xmlns:log4j='http://jakarta.apache.org/log4j/'>
  <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%p] %m%n" />
    </layout>
  </appender>
  <root>
    <priority value="info" />
    <appender-ref ref="STDOUT" />
  </root>
</log4j:configuration>


◆実行結果
2017-07-26 01:43:34 [INFO] Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2e18f2: startup date [Wed Jul 26 01:43:34 JST 2017]; root of context hierarchy
2017-07-26 01:43:34 [INFO] Loading XML bean definitions from class path resource [SpringIntegration.xml]
2017-07-26 01:43:35 [INFO] Loading properties file from URL [jar:file:/C:/Users/root/.m2/repository/org/springframework/integration/spring-integration-core/4.1.4.RELEASE/spring-integration-core-4.1.4.RELEASE.jar!/META-INF/spring.integration.default.properties]
2017-07-26 01:43:35 [INFO] No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.
2017-07-26 01:43:35 [INFO] No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created.
2017-07-26 01:43:35 [INFO] No bean named 'taskScheduler' has been explicitly defined. Therefore, a default ThreadPoolTaskScheduler will be created.
2017-07-26 01:43:35 [INFO] JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2017-07-26 01:43:35 [INFO] Loading properties file from URL [jar:file:/C:/Users/root/.m2/repository/org/springframework/integration/spring-integration-core/4.1.4.RELEASE/spring-integration-core-4.1.4.RELEASE.jar!/META-INF/spring.integration.default.properties]
2017-07-26 01:43:35 [INFO] Initializing ExecutorService  'defaultSockJsTaskScheduler'
2017-07-26 01:43:35 [INFO] Initializing ExecutorService  'taskScheduler'
2017-07-26 01:43:36 [WARN] Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping
2017-07-26 01:43:36 [INFO] Starting beans in phase -2147483648
2017-07-26 01:43:36 [INFO] Starting beans in phase 0
2017-07-26 01:43:36 [INFO] Adding {service-activator:saService} as a subscriber to the 'inCh' channel
2017-07-26 01:43:36 [INFO] Channel 'org.springframework.context.support.ClassPathXmlApplicationContext@2e18f2.inCh' has 1 subscriber(s).
2017-07-26 01:43:36 [INFO] started saService
2017-07-26 01:43:36 [INFO] Adding {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
2017-07-26 01:43:36 [INFO] Channel 'org.springframework.context.support.ClassPathXmlApplicationContext@2e18f2.errorChannel' has 1 subscriber(s).
2017-07-26 01:43:36 [INFO] started _org.springframework.integration.errorLogger