カテゴリー「java」

ローカル環境にS3環境を構築できる「S3 ninja」なるものを入れてみたので、

備忘録を残しておく。

 

  • ダウンロード

http://s3ninja.net/

上記からzipファイルを落として解凍すればよい。

 

  • 初期設定

解凍した直下に、「instance.conf.sdsignore」というファイルがあるので、

これを「instance.conf」とする。

また、dataフォルダがあるので、

その下に「s3」フォルダを作成する。

 

  • 起動

以下を実行すれば、「http://localhost:9444」でアクセス可能となる。

$ ./sirius.sh start

 

phpとjavaからアップしたが、本物とは違ったので、

コードを記載しておく。

なお、前提として、「http://localhost:9444」にアクセスして、

事前に「test」というバケットを作り、「public」にしておくものとする。

 

phpコード

<?php
require_once("aws.phar");
$bucketName = "s3";
$accessKey = "AKIAIOSFODNN7EXAMPLE";
$secretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
$keyName = "test/php/000.text";

$s3 = new Aws\S3\S3Client(array(
    'credentials' => array(
        'key'    => $accessKey,
        'secret' => $secretKey,
    ),
    'version' => "2006-03-01",
    'region' => "us-east-1",
    'endpoint' => "http://localhost:9444/",
));

$r = $s3->putObject(array(
   'Bucket' => $bucketName,
   'Key' => $keyName,
   'Body' => fopen("test.txt", "r"),
   'ACL' => 'public-read',
   'CacheControl' => 'no-store, no-cache',
));
var_dump($r);

 

javaコード

package sample.ninja_test;

import java.io.File;

import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.S3ClientOptions;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.PutObjectRequest;

public class App
{
  private static final String keyName = "java/000.text";
  private static final String bucketName = "test";
  private static final String accessKey = "AKIAIOSFODNN7EXAMPLE";
  private static final String secretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";

  public static void main(String[] args) {
    AmazonS3Client client = new AmazonS3Client(new BasicAWSCredentials(accessKey, secretKey));
    client.setS3ClientOptions(new S3ClientOptions().withPathStyleAccess(true));
    client.setEndpoint("http://localhost:9444/s3");

    PutObjectRequest putObj = new PutObjectRequest(bucketName, keyName, new File("test.txt"));
    putObj.setCannedAcl(CannedAccessControlList.PublicRead);
    client.putObject(putObj);

    String url = client.getResourceUrl(bucketName, keyName);
    System.out.println(url);
  }
}

期待した設定としては、javaのコードが正しい。
phpのコードはちょっと設定がおかしい。

とはいえ、この辺は、プログラムの設定等で吸収できるので、
簡易的なテストツールとしてはいいかもしれません。

投稿日時:2016年02月22日 16:58   カテゴリー:aws, java, php   [コメントがあればどうぞ]

jettyでtomcatJDBC poolを使っていたら、

以下のようなエラーに遭遇した。

java.util.ServiceConfigurationError: org.apache.juli.logging.Log: Provider org.eclipse.jetty.apache.jsp.JuliLog not a subtype

というわけで、
pomを修正して、tomcat jdbc poolが使っているロガーの依存関係を排除した。

<dependency>
  <groupId>org.apache.tomcat</groupId>
  <artifactId>tomcat-jdbc</artifactId>
  <version>8.0.28</version>
  <exclusions>
    <exclusion>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-juli</artifactId>
    </exclusion>
  </exclusions>
</dependency>

意外とわからなかった。。

投稿日時:2016年01月21日 13:56   カテゴリー:java   [コメントがあればどうぞ]

javaでsynchronizedブロックにて、

Stringを使うと、

異なるオブジェクトであっても、

文字列が同じであれば排他制御がかかる。

 

ただし、異なるオブジェクト・同一文字列で排他制御をかけるべきでない。

なぜなら、依存ライブラリ等でもしsyncrozinedブロックを使っていたら、

最悪デッドロック等もありえる。

(そんなライブラリはないと思うが。。)

 

これはStringの特性によるものなのだが、

いつか詳細を記載したいとは思う。

 

投稿日時:2016年01月07日 19:20   カテゴリー:java   [コメントがあればどうぞ]

持続的接続をredisで行う場合、

接続は1つでよいと書いた記憶があったが、

javaでノンブロッキングでも大丈夫か検証してみた。

 

javaのredisクライアントはlettuceを使用し、

1つのコネクションを、

マルチスレッドで共有し、SET、GETを試みたところ、

結果にずれは生じなかった。

 

結論として、

redisはシングルスレッドであるため、

持続的接続の場合は1コネクションでOK。

 

ただし、javaではマルチスレッドの性能を生かすため、

CPU数 x 2〜3 ぐらいがいいのかなとは思う。

ちなみに、上記でやっても問題なしでした。

 

以上

投稿日時:2015年12月29日 15:38   カテゴリー:java, redis   [コメントがあればどうぞ]

JavaMailでSMTP接続をして、

メールを送信しようとした時、

javax.mail.MessagingException: 501 Syntax: HELO hostname

のエラーがでることがある。

 

これは送信元のサーバのホスト名が

/etc/hostsに掲載されていない場合に起こる。

 

その他にも色々方法はあるようだが、

/etc/hostsにhostnameをしっかり書いておくことは大事です。

とはいえ、最近クラウドばかり使っていると、

この辺疎かになりがち。。

 

投稿日時:2015年12月25日 13:45   カテゴリー:java   [コメントがあればどうぞ]

javaのweb socketで、decoderとpathparamはある条件において不可能なようなだ。

 

以下の場合はダメ。

@ServerEndpoint(
  value = "/ws/{p1}/{p2}/"
  decoders = {HogeDecoder.class},
  encoders = {HogeEncoder.class})
public class HogeWebsocket {
  /**
   * open hander.
   */
  @OnOpen
  public void onOpen(@PathParam("p1") String p1, Session session, EndpointConfig config) {
    :
    :  
  }
}

 

これだと、p1がdecoderの対象になるみたい。

そのため、次のような方法で対処する。

 

@ServerEndpoint(
  value = "/ws/{p1}/{p2}/"
  decoders = {HogeDecoder.class},
  encoders = {HogeEncoder.class})
public class HogeWebsocket {
  /**
   * open hander.
   */
  @OnOpen
  public void onOpen(Session session, EndpointConfig config) {
    Map<String, String> pathParameters = session.getPathParameters();
    String p1 = pathParameters.get("p1");
    :  
  }
}

 

本件は、decoderでバイナリからテキストに変換しようとした際に起きた現象。

なので、decoderが何をするかによるとは思うが。。

javaのページを見たが、それらしき記述はなかった。。

 

投稿日時:2015年12月21日 18:38   カテゴリー:java, websocket   [コメントがあればどうぞ]

redisの冗長化を行うためには、

  • master <-> slave構成
  • master <-> slave構成 + sentinel
  • cluster

がある。

clusterはredis3より正式サポートされた機能である。

 

特徴としては、以下の通り。

 

master <-> slave構成だと、

masterが倒れたときのフェイルオーバが皆無である。

 

master <-> slave構成 + sentinelだと、

masterが倒れたときに、slaveが自動昇格できる。

ただ、slave x 2以上、sentinel x 3以上が望ましい感じがする。

 

cluster構成だと、

分散でデータを保持しているため、

slaveも同時に使い、自動昇格させる必要がある。(slaveがあれば自動昇格する)

master x 3以上、slave x 3以上にする必要がある。

 

と考えると、clusterが一番な気がするが、

clusterの欠点は以下の通り。

  1. selectはない
  2. multi – execができない?
  3. 対応しているクライアントライブラリが少ない
  4. リダイレクトが発生するため、速度が遅くなる?
  5. クラスター再構築が難しめ

などなど。

とくに、クライアントライブラリが少ないのは気がかりである。

python, rubyではあるらしい。

javaでもlettuceが対応している。

javaのコードは以下の通り。

List<RedisURI> list = new ArrayList<>();
list.add(new RedisURI("192.168.1.45", 16381, 1, TimeUnit.SECONDS));
list.add(new RedisURI("192.168.1.45", 16382, 1, TimeUnit.SECONDS));
list.add(new RedisURI("192.168.1.45", 16383, 1, TimeUnit.SECONDS));
list.add(new RedisURI("192.168.1.45", 16384, 1, TimeUnit.SECONDS));
list.add(new RedisURI("192.168.1.45", 16385, 1, TimeUnit.SECONDS));
list.add(new RedisURI("192.168.1.45", 16386, 1, TimeUnit.SECONDS));

RedisClusterClient client = RedisClusterClient.create(new Iterable<RedisURI>() {
  @Override
  public Iterator<RedisURI> iterator() {
    return list.iterator();
  }
});

AsyncExecutions<String> excutions = null;

RedisAdvancedClusterAsyncCommands<String, String> con = client.connect().async();

AsyncNodeSelection<String, String> masters = con.masters();

excutions = masters.commands().set("hoge", "fuga");

excutions.forEach(result -> result.thenAccept(ret -> System.out.println(ret)));
excutions = masters.commands().get("hoge");

excutions.forEach(result -> result.thenAccept(ret -> System.out.println(ret)));
con.close();
client.shutdown();

 

しかし、multi – execができないのは結構痛い。。。

うまいことやればできるのかな。。

ただ、分散してしまうから、無理なきがする。

 

このlettuceっていうライブラリは良さげ。

nettyをベースに使っていて、

ノンブロッキングをサポートしているしね。

 

投稿日時:2015年12月10日 13:06   カテゴリー:java, redis   [コメントがあればどうぞ]

昨日書いた記事を検証してみた。

 

以下3つの例を検証。

 

1.単純ケース

@WebServlet(name = "test01", urlPatterns = {"/test01"})
public class Test01 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String name = req.getParameter("name");

    try (PrintWriter writer = resp.getWriter();) {
      resp.setStatus(200);
      resp.setContentType("text/plain");
      writer.write("name is " + name);
      writer.flush();
      writer.close();
    }
  }
}

 

2.絶対ダメなケース

@WebServlet(name = "test02", urlPatterns = {"/test02"})
public class Test02 extends HttpServlet {

  private String name;

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    name = req.getParameter("name");

    try (PrintWriter writer = resp.getWriter();) {
      resp.setStatus(200);
      resp.setContentType("text/plain");
      writer.write("name is " + name);
      writer.flush();
      writer.close();
    }
  }
}

 

3.今回検証したかったケース

@WebServlet(name = "test03", urlPatterns = {"/test03"})
public class Test03 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    new MyClass(req, resp).execute();
  }

  class MyClass {

    private HttpServletRequest req;
    private HttpServletResponse resp;
    private String name;

    public MyClass(HttpServletRequest req, HttpServletResponse resp) {
      this.req = req;
      this.resp = resp;

      name = req.getParameter("name");
    }

    public void execute() throws IOException {
      try (PrintWriter writer = resp.getWriter();) {
        resp.setStatus(200);
        resp.setContentType("text/plain");
        writer.write("name is " + name);
        writer.flush();
        writer.close();
      }
    }
  }
}

 

結論としては、3は大丈夫であった。

jmeterで同時接続100を10回やって、一度も不整合は起きず。

 

投稿日時:2015年11月27日 14:52   カテゴリー:java   [コメントがあればどうぞ]

servletはエントリーポイントのインスタンスは1つしか作成しない。

そして、それが複数スレッドで共有される。

そのため、インスタンスフィールドはスレッドセーフにならない。

 

・スレッドセーフではない例

public class TestServlet extends HttpServlet {
  private String name;

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    name = req.getParameter("name");

    PrintWriter writer = res.getWriter();
    writer.print(name);
  }
}

 

で、ここからがわからんところなのだが、

ローカル変数はスレッドセーフになるのだから、

doGetの処理の中で、以下のようなことをしたら、問題ないのか?

public class TestServlet extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    new MyClass(req, res).execute();
  }
}

public class MyClass {
  private HttpServletRequest req;
  private HttpServletResponse res;

  public MyClass(HttpServletRequest req, HttpServletResponse res) {
    this.req = req;
    this.res = res;
  }

  public void execute() {
    String name = req.getParameter("name");

    PrintWriter writer = res.getWriter();
    writer.print(name);
  } 
} 

 

もし、上記でスレッドセーフになるっていうなら、

それでよくね?って思っちゃう。

 

それとも、MyClassのインスタンスフィールドは実はスレッドセーフではないのか?

 

メモリ使用量としては、生成するインスタンス分大きいのはわかるが、

もし上記で解決するなら、煩わしいスレッド問題とはオサラバできると思うのだが、

問題あるのだろうか?

 

servletはフレームワークの世界だから、厳密にわかる人は少ないのだろうけど、

ここは教えて欲しい。。。

 

・・・後日

上の例は大丈夫だった。

新しく記事を書きました。

投稿日時:2015年11月26日 19:58   カテゴリー:java   [コメントがあればどうぞ]

ラムダ式が導入されて(java8)結構たちましたが、

あまりやる気がしなかったが、

重い腰を上げて、おれおれフレームワークに少しづつ適用中。

 

いろいろ例はあるが、やはりコレクションには適用しやすい。

二元ループをするケースで結構はまったので、

メモを残しておく。

 

(例)URLのBodyパラメータを手作業で分解する場合

Bodyパラメータはこんな感じを想定

hoge=1&fuga=2&piyo[]=1&piyo[]=2

 

ここではInputStreamから抜かないで、一度分解したのち、文字列に戻すということをやる。

これを従来の処理でやると以下になる。

Map<String, List<String>> paramsBody = new LinkedHashMap<String, List<String>>(); 
StringBuffer buffer = new StringBuffer(); 

// サーブレットリクエストからパラメータの情報をとる 
Map<String, String[]> params = servletRequest.getParameterMap(); 

// 回しながら、List型に変換して、さらに文字列を構築する 
String sep = ""; 
for (Iterator<Entry<String, String[]>> iterator = params.entrySet().iterator(); iterator.hasNext();) { 
  Map.Entry<String, String[]> entry = (Map.Entry<String, String[]>) iterator.next(); 
  String name = entry.getKey(); 
  String[] values = entry.getValue(); 
  if (!paramsBody.containsKey(name)) { 
    paramsBody.put(name, new ArrayList<String>(Arrays.asList(values))); 
  } 
  for (String value : values) { 
    buffer.append(sep).append(name).append("=").append(WsUrl.decode(value, encoding)); 
    sep = "&"; 
  } 
  body = buffer.toString(); 
}

 

おなじことをラムダ式をつかうと、

Map<String, List<String>> paramsBody = new LinkedHashMap<String, List<String>>();
StringBuffer buffer = new StringBuffer(); 

// サーブレットリクエストからパラメータの情報をとる 
Map<String, String[]> params = servletRequest.getParameterMap(); 

// 回しながら、List型に変換して、さらに文字列を構築する 
String paramString = params.entrySet() 
    .stream() 
    .map(entry -> { 
      String name = entry.getKey(); 
      return Arrays.stream(entry.getValue()) 
         .map(value -> String.format("%s=%s", name, WsUrl.decode(value, encoding))) 
         .collect(Collectors.joining("&")); 
    })
    .collect(Collectors.joining("&")); 
buffer.append(paramString); 
body = buffer.toString();

 

ここまでくるのに結構大変だった。。

投稿日時:2015年10月29日 16:28   カテゴリー:java   [コメントがあればどうぞ]