tokuhirom's blog

Jetty でディレクトリリスティングを拒否したい

    webAppContext.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");

で、いけます。

ref. https://stackoverflow.com/questions/7217969/how-to-disable-directory-listing-for-jettys-webappcontext/7572313#7572313

Created: 2015-07-14 16:08:14
Updated: 2015-07-14 16:08:14

Java 8 の DateTimeFormatter の曜日等のフォーマットについて

DateTimeFormatter を利用して曜日などのフォーマットを行おうとすると、実際どういうふうにフォーマットされるのかよくわからんということになる。

https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html をちらっと見ると、以下のように書いてあって、各シンボルが何を表すのかよくわからない。 ``` Symbol Meaning Presentation Examples


G era text AD; Anno Domini; A u year year 2004; 04 y year-of-era year 2004; 04 D day-of-year number 189 M/L month-of-year number/text 7; 07; Jul; July; J d day-of-month number 10

Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter Y week-based-year year 1996; 96 w week-of-week-based-year number 27 W week-of-month number 4 E day-of-week text Tue; Tuesday; T e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T F week-of-month number 3

a am-pm-of-day text PM h clock-hour-of-am-pm (1-12) number 12 K hour-of-am-pm (0-11) number 0 k clock-hour-of-am-pm (1-24) number 0

H hour-of-day (0-23) number 0 m minute-of-hour number 30 s second-of-minute number 55 S fraction-of-second fraction 978 A milli-of-day number 1234 n nano-of-second number 987654321 N nano-of-day number 1234000000

V time-zone ID zone-id America/Los_Angeles; Z; -08:30 z time-zone name zone-name Pacific Standard Time; PST O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00; X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15; x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15; Z zone-offset offset-Z +0000; -0800; -08:00;

p pad next pad modifier 1

' escape for text delimiter '' single quote literal ' [ optional section start ] optional section end # reserved for future use { reserved for future use } reserved for future use ```

GGG などのように、同じ文字列を複数回続けることによって任意の形式で出力することができるのだが、この様式は以下のようになっている。

Date fields: Pattern letters to output a date.

  Pattern  Count  Equivalent builder methods
  -------  -----  --------------------------
    G       1      appendText(ChronoField.ERA, TextStyle.SHORT)
    GG      2      appendText(ChronoField.ERA, TextStyle.SHORT)
    GGG     3      appendText(ChronoField.ERA, TextStyle.SHORT)
    GGGG    4      appendText(ChronoField.ERA, TextStyle.FULL)
    GGGGG   5      appendText(ChronoField.ERA, TextStyle.NARROW)

    u       1      appendValue(ChronoField.YEAR, 1, 19, SignStyle.NORMAL);
    uu      2      appendValueReduced(ChronoField.YEAR, 2, 2000);
    uuu     3      appendValue(ChronoField.YEAR, 3, 19, SignStyle.NORMAL);
    u..u    4..n   appendValue(ChronoField.YEAR, n, 19, SignStyle.EXCEEDS_PAD);
    y       1      appendValue(ChronoField.YEAR_OF_ERA, 1, 19, SignStyle.NORMAL);
    yy      2      appendValueReduced(ChronoField.YEAR_OF_ERA, 2, 2000);
    yyy     3      appendValue(ChronoField.YEAR_OF_ERA, 3, 19, SignStyle.NORMAL);
    y..y    4..n   appendValue(ChronoField.YEAR_OF_ERA, n, 19, SignStyle.EXCEEDS_PAD);
    Y       1      append special localized WeekFields element for numeric week-based-year
    YY      2      append special localized WeekFields element for reduced numeric week-based-year 2 digits;
    YYY     3      append special localized WeekFields element for numeric week-based-year (3, 19, SignStyle.NORMAL);
    Y..Y    4..n   append special localized WeekFields element for numeric week-based-year (n, 19, SignStyle.EXCEEDS_PAD);

    Q       1      appendValue(IsoFields.QUARTER_OF_YEAR);
    QQ      2      appendValue(IsoFields.QUARTER_OF_YEAR, 2);
    QQQ     3      appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.SHORT)
    QQQQ    4      appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.FULL)
    QQQQQ   5      appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.NARROW)
    q       1      appendValue(IsoFields.QUARTER_OF_YEAR);
    qq      2      appendValue(IsoFields.QUARTER_OF_YEAR, 2);
    qqq     3      appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.SHORT_STANDALONE)
    qqqq    4      appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.FULL_STANDALONE)
    qqqqq   5      appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.NARROW_STANDALONE)

    M       1      appendValue(ChronoField.MONTH_OF_YEAR);
    MM      2      appendValue(ChronoField.MONTH_OF_YEAR, 2);
    MMM     3      appendText(ChronoField.MONTH_OF_YEAR, TextStyle.SHORT)
    MMMM    4      appendText(ChronoField.MONTH_OF_YEAR, TextStyle.FULL)
    MMMMM   5      appendText(ChronoField.MONTH_OF_YEAR, TextStyle.NARROW)
    L       1      appendValue(ChronoField.MONTH_OF_YEAR);
    LL      2      appendValue(ChronoField.MONTH_OF_YEAR, 2);
    LLL     3      appendText(ChronoField.MONTH_OF_YEAR, TextStyle.SHORT_STANDALONE)
    LLLL    4      appendText(ChronoField.MONTH_OF_YEAR, TextStyle.FULL_STANDALONE)
    LLLLL   5      appendText(ChronoField.MONTH_OF_YEAR, TextStyle.NARROW_STANDALONE)

    w       1      append special localized WeekFields element for numeric week-of-year
    ww      1      append special localized WeekFields element for numeric week-of-year, zero-padded
    W       1      append special localized WeekFields element for numeric week-of-month
    d       1      appendValue(ChronoField.DAY_OF_MONTH)
    dd      2      appendValue(ChronoField.DAY_OF_MONTH, 2)
    D       1      appendValue(ChronoField.DAY_OF_YEAR)
    DD      2      appendValue(ChronoField.DAY_OF_YEAR, 2)
    DDD     3      appendValue(ChronoField.DAY_OF_YEAR, 3)
    F       1      appendValue(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH)
    E       1      appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
    EE      2      appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
    EEE     3      appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
    EEEE    4      appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL)
    EEEEE   5      appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW)
    e       1      append special localized WeekFields element for numeric day-of-week
    ee      2      append special localized WeekFields element for numeric day-of-week, zero-padded
    eee     3      appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
    eeee    4      appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL)
    eeeee   5      appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW)
    c       1      append special localized WeekFields element for numeric day-of-week
    ccc     3      appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT_STANDALONE)
    cccc    4      appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL_STANDALONE)
    ccccc   5      appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW_STANDALONE)

Time fields: Pattern letters to output a time.

  Pattern  Count  Equivalent builder methods
  -------  -----  --------------------------
    a       1      appendText(ChronoField.AMPM_OF_DAY, TextStyle.SHORT)
    h       1      appendValue(ChronoField.CLOCK_HOUR_OF_AMPM)
    hh      2      appendValue(ChronoField.CLOCK_HOUR_OF_AMPM, 2)
    H       1      appendValue(ChronoField.HOUR_OF_DAY)
    HH      2      appendValue(ChronoField.HOUR_OF_DAY, 2)
    k       1      appendValue(ChronoField.CLOCK_HOUR_OF_DAY)
    kk      2      appendValue(ChronoField.CLOCK_HOUR_OF_DAY, 2)
    K       1      appendValue(ChronoField.HOUR_OF_AMPM)
    KK      2      appendValue(ChronoField.HOUR_OF_AMPM, 2)
    m       1      appendValue(ChronoField.MINUTE_OF_HOUR)
    mm      2      appendValue(ChronoField.MINUTE_OF_HOUR, 2)
    s       1      appendValue(ChronoField.SECOND_OF_MINUTE)
    ss      2      appendValue(ChronoField.SECOND_OF_MINUTE, 2)

    S..S    1..n   appendFraction(ChronoField.NANO_OF_SECOND, n, n, false)
    A       1      appendValue(ChronoField.MILLI_OF_DAY)
    A..A    2..n   appendValue(ChronoField.MILLI_OF_DAY, n)
    n       1      appendValue(ChronoField.NANO_OF_SECOND)
    n..n    2..n   appendValue(ChronoField.NANO_OF_SECOND, n)
    N       1      appendValue(ChronoField.NANO_OF_DAY)
    N..N    2..n   appendValue(ChronoField.NANO_OF_DAY, n)

Zone ID: Pattern letters to output ZoneId.

  Pattern  Count  Equivalent builder methods
  -------  -----  --------------------------
    VV      2      appendZoneId()
    z       1      appendZoneText(TextStyle.SHORT)
    zz      2      appendZoneText(TextStyle.SHORT)
    zzz     3      appendZoneText(TextStyle.SHORT)
    zzzz    4      appendZoneText(TextStyle.FULL)

Zone offset: Pattern letters to output ZoneOffset.

  Pattern  Count  Equivalent builder methods
  -------  -----  --------------------------
    O       1      appendLocalizedOffsetPrefixed(TextStyle.SHORT);
    OOOO    4      appendLocalizedOffsetPrefixed(TextStyle.FULL);
    X       1      appendOffset("+HHmm","Z")
    XX      2      appendOffset("+HHMM","Z")
    XXX     3      appendOffset("+HH:MM","Z")
    XXXX    4      appendOffset("+HHMMss","Z")
    XXXXX   5      appendOffset("+HH:MM:ss","Z")
    x       1      appendOffset("+HHmm","+00")
    xx      2      appendOffset("+HHMM","+0000")
    xxx     3      appendOffset("+HH:MM","+00:00")
    xxxx    4      appendOffset("+HHMMss","+0000")
    xxxxx   5      appendOffset("+HH:MM:ss","+00:00")
    Z       1      appendOffset("+HHMM","+0000")
    ZZ      2      appendOffset("+HHMM","+0000")
    ZZZ     3      appendOffset("+HHMM","+0000")
    ZZZZ    4      appendLocalizedOffset(TextStyle.FULL);
    ZZZZZ   5      appendOffset("+HH:MM:ss","Z")

Modifiers: Pattern letters that modify the rest of the pattern:

  Pattern  Count  Equivalent builder methods
  -------  -----  --------------------------
    [       1      optionalStart()
    ]       1      optionalEnd()
    p..p    1..n   padNext(n)

これは、 https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatterBuilder.html#appendPattern-java.lang.String- のコピペである。DateTimeFormatter のフォーマットの仕方が DateTimeFormatterBuilder の方にあるので、たどりつくのが難しい。

実際どのようにフォーマットされるのかを確認する

実際にどのようにフォーマットされるのかを確認しておけば、後々便利であろう。


package datetimeformattersample;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

/**
 *
 * @author tokuhirom
 */
public class DateTimeFormatterSample {

  /**
   * @param args the command line arguments
   */
  public static void main(String[] args) {
    String[][] patterns = new String[][]{
      new String[]{"G", "appendText(ChronoField.ERA,"},
      new String[]{"GG", "appendText(ChronoField.ERA,"},
      new String[]{"GGG", "appendText(ChronoField.ERA,"},
      new String[]{"GGGG", "appendText(ChronoField.ERA,"},
      new String[]{"GGGGG", "appendText(ChronoField.ERA,"},
      new String[]{"", ""},
      new String[]{"u", "appendValue(ChronoField.YEAR,"},
      new String[]{"uu", "appendValueReduced(ChronoField.YEAR,"},
      new String[]{"uuu", "appendValue(ChronoField.YEAR,"},
      new String[]{"uuuu", "appendValue(ChronoField.YEAR,"},
      new String[]{"y", "appendValue(ChronoField.YEAR_OF_ERA,"},
      new String[]{"yy", "appendValueReduced(ChronoField.YEAR_OF_ERA,"},
      new String[]{"yyy", "appendValue(ChronoField.YEAR_OF_ERA,"},
      new String[]{"yyyy", "appendValue(ChronoField.YEAR_OF_ERA,"},
      new String[]{"Y", "append"},
      new String[]{"YY", "append"},
      new String[]{"YYY", "append"},
      new String[]{"YYYY", "append"},
      new String[]{"", ""},
      new String[]{"Q", "appendValue(IsoFields.QUARTER_OF_YEAR);"},
      new String[]{"QQ", "appendValue(IsoFields.QUARTER_OF_YEAR,"},
      new String[]{"QQQ", "appendText(IsoFields.QUARTER_OF_YEAR,"},
      new String[]{"QQQQ", "appendText(IsoFields.QUARTER_OF_YEAR,"},
      new String[]{"QQQQQ", "appendText(IsoFields.QUARTER_OF_YEAR,"},
      new String[]{"q", "appendValue(IsoFields.QUARTER_OF_YEAR);"},
      new String[]{"qq", "appendValue(IsoFields.QUARTER_OF_YEAR,"},
      new String[]{"qqq", "appendText(IsoFields.QUARTER_OF_YEAR,"},
      new String[]{"qqqq", "appendText(IsoFields.QUARTER_OF_YEAR,"},
      new String[]{"qqqqq", "appendText(IsoFields.QUARTER_OF_YEAR,"},
      new String[]{"", ""},
      new String[]{"M", "appendValue(ChronoField.MONTH_OF_YEAR);"},
      new String[]{"MM", "appendValue(ChronoField.MONTH_OF_YEAR,"},
      new String[]{"MMM", "appendText(ChronoField.MONTH_OF_YEAR,"},
      new String[]{"MMMM", "appendText(ChronoField.MONTH_OF_YEAR,"},
      new String[]{"MMMMM", "appendText(ChronoField.MONTH_OF_YEAR,"},
      new String[]{"L", "appendValue(ChronoField.MONTH_OF_YEAR);"},
      new String[]{"LL", "appendValue(ChronoField.MONTH_OF_YEAR,"},
      new String[]{"LLL", "appendText(ChronoField.MONTH_OF_YEAR,"},
      new String[]{"LLLL", "appendText(ChronoField.MONTH_OF_YEAR,"},
      new String[]{"LLLLL", "appendText(ChronoField.MONTH_OF_YEAR,"},
      new String[]{"", ""},
      new String[]{"w", "append"},
      new String[]{"ww", "append"},
      new String[]{"W", "append"},
      new String[]{"d", "appendValue(ChronoField.DAY_OF_MONTH)"},
      new String[]{"dd", "appendValue(ChronoField.DAY_OF_MONTH,"},
      new String[]{"D", "appendValue(ChronoField.DAY_OF_YEAR)"},
      new String[]{"DD", "appendValue(ChronoField.DAY_OF_YEAR,"},
      new String[]{"DDD", "appendValue(ChronoField.DAY_OF_YEAR,"},
      new String[]{"F", "appendValue(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH)"},
      new String[]{"E", "appendText(ChronoField.DAY_OF_WEEK,"},
      new String[]{"EE", "appendText(ChronoField.DAY_OF_WEEK,"},
      new String[]{"EEE", "appendText(ChronoField.DAY_OF_WEEK,"},
      new String[]{"EEEE", "appendText(ChronoField.DAY_OF_WEEK,"},
      new String[]{"EEEEE", "appendText(ChronoField.DAY_OF_WEEK,"},
      new String[]{"e", "append"},
      new String[]{"ee", "append"},
      new String[]{"eee", "appendText(ChronoField.DAY_OF_WEEK,"},
      new String[]{"eeee", "appendText(ChronoField.DAY_OF_WEEK,"},
      new String[]{"eeeee", "appendText(ChronoField.DAY_OF_WEEK,"},
      new String[]{"c", "append"},
      new String[]{"ccc", "appendText(ChronoField.DAY_OF_WEEK,"},
      new String[]{"cccc", "appendText(ChronoField.DAY_OF_WEEK,"},
      new String[]{"ccccc", "appendText(ChronoField.DAY_OF_WEEK,"},
      new String[]{"a", "appendText(ChronoField.AMPM_OF_DAY,"},
      new String[]{"h", "appendValue(ChronoField.CLOCK_HOUR_OF_AMPM)"},
      new String[]{"hh", "appendValue(ChronoField.CLOCK_HOUR_OF_AMPM,"},
      new String[]{"H", "appendValue(ChronoField.HOUR_OF_DAY)"},
      new String[]{"HH", "appendValue(ChronoField.HOUR_OF_DAY,"},
      new String[]{"k", "appendValue(ChronoField.CLOCK_HOUR_OF_DAY)"},
      new String[]{"kk", "appendValue(ChronoField.CLOCK_HOUR_OF_DAY,"},
      new String[]{"K", "appendValue(ChronoField.HOUR_OF_AMPM)"},
      new String[]{"KK", "appendValue(ChronoField.HOUR_OF_AMPM,"},
      new String[]{"m", "appendValue(ChronoField.MINUTE_OF_HOUR)"},
      new String[]{"mm", "appendValue(ChronoField.MINUTE_OF_HOUR,"},
      new String[]{"s", "appendValue(ChronoField.SECOND_OF_MINUTE)"},
      new String[]{"ss", "appendValue(ChronoField.SECOND_OF_MINUTE,"},
      new String[]{"", ""},
      new String[]{"SSSS", "appendFraction(ChronoField.NANO_OF_SECOND,"},
      new String[]{"A", "appendValue(ChronoField.MILLI_OF_DAY)"},
      new String[]{"AAAA", "appendValue(ChronoField.MILLI_OF_DAY,"},
      new String[]{"n", "appendValue(ChronoField.NANO_OF_SECOND)"},
      new String[]{"nnnn", "appendValue(ChronoField.NANO_OF_SECOND,"},
      new String[]{"N", "appendValue(ChronoField.NANO_OF_DAY)"},
      new String[]{"NNNN", "appendValue(ChronoField.NANO_OF_DAY,"},
      new String[]{"VV", "appendZoneId()"},
      new String[]{"z", "appendZoneText(TextStyle.SHORT)"},
      new String[]{"zz", "appendZoneText(TextStyle.SHORT)"},
      new String[]{"zzz", "appendZoneText(TextStyle.SHORT)"},
      new String[]{"zzzz", "appendZoneText(TextStyle.FULL)"},
      new String[]{"O", "appendLocalizedOffsetPrefixed(TextStyle.SHORT);"},
      new String[]{"OOOO", "appendLocalizedOffsetPrefixed(TextStyle.FULL);"},
      new String[]{"X", "appendOffset(\"+HHmm\",\"Z\")"},
      new String[]{"XX", "appendOffset(\"+HHMM\",\"Z\")"},
      new String[]{"XXX", "appendOffset(\"+HH:MM\",\"Z\")"},
      new String[]{"XXXX", "appendOffset(\"+HHMMss\",\"Z\")"},
      new String[]{"XXXXX", "appendOffset(\"+HH:MM:ss\",\"Z\")"},
      new String[]{"x", "appendOffset(\"+HHmm\",\"+00\")"},
      new String[]{"xx", "appendOffset(\"+HHMM\",\"+0000\")"},
      new String[]{"xxx", "appendOffset(\"+HH:MM\",\"+00:00\")"},
      new String[]{"xxxx", "appendOffset(\"+HHMMss\",\"+0000\")"},
      new String[]{"xxxxx", "appendOffset(\"+HH:MM:ss\",\"+00:00\")"},
      new String[]{"Z", "appendOffset(\"+HHMM\",\"+0000\")"},
      new String[]{"ZZ", "appendOffset(\"+HHMM\",\"+0000\")"},
      new String[]{"ZZZ", "appendOffset(\"+HHMM\",\"+0000\")"},
      new String[]{"ZZZZ", "appendLocalizedOffset(TextStyle.FULL);"},
      new String[]{"ZZZZZ", "appendOffset(\"+HH:MM:ss\",\"Z\")"}
    };

    ZonedDateTime zonedDateTime = LocalDateTime
            .parse("2015-07-13T12:27:04")
            .atZone(ZoneId.of("Asia/Tokyo"));
    System.out.printf("%-7s : %-19s\t%-19s // %s\n", "Pattern", "English", "Japanese", "Comment");
    for (String[] pattern : patterns) {
      try {
      DateTimeFormatter enFormatter = DateTimeFormatter.ofPattern(pattern[0], Locale.ENGLISH);
      String en = zonedDateTime.format(enFormatter);

      DateTimeFormatter jpFormatter = DateTimeFormatter.ofPattern(pattern[0], Locale.JAPAN);
      String jp = zonedDateTime.format(jpFormatter);

        System.out.printf("%-7s : %-19s\t%-19s // %s\n", pattern[0], en, jp, pattern[1]);
      } catch (java.time.DateTimeException e) {
        System.err.printf("cannot format '%s': %s", pattern[0], e.getMessage());
      }
    }
  }

}

出力例は以下のようになる。

Pattern : English               Japanese            // Comment
G       : AD                    西暦                  // appendText(ChronoField.ERA,
GG      : AD                    西暦                  // appendText(ChronoField.ERA,
GGG     : AD                    西暦                  // appendText(ChronoField.ERA,
GGGG    : Anno Domini           西暦                  // appendText(ChronoField.ERA,
GGGGG   : A                     A                   // appendText(ChronoField.ERA,
        :                                           // 
u       : 2015                  2015                // appendValue(ChronoField.YEAR,
uu      : 15                    15                  // appendValueReduced(ChronoField.YEAR,
uuu     : 2015                  2015                // appendValue(ChronoField.YEAR,
uuuu    : 2015                  2015                // appendValue(ChronoField.YEAR,
y       : 2015                  2015                // appendValue(ChronoField.YEAR_OF_ERA,
yy      : 15                    15                  // appendValueReduced(ChronoField.YEAR_OF_ERA,
yyy     : 2015                  2015                // appendValue(ChronoField.YEAR_OF_ERA,
yyyy    : 2015                  2015                // appendValue(ChronoField.YEAR_OF_ERA,
Y       : 2015                  2015                // append
YY      : 15                    15                  // append
YYY     : 2015                  2015                // append
YYYY    : 2015                  2015                // append
        :                                           // 
Q       : 3                     3                   // appendValue(IsoFields.QUARTER_OF_YEAR);
QQ      : 03                    03                  // appendValue(IsoFields.QUARTER_OF_YEAR,
QQQ     : Q3                    Q3                  // appendText(IsoFields.QUARTER_OF_YEAR,
QQQQ    : 3rd quarter           第3四半期               // appendText(IsoFields.QUARTER_OF_YEAR,
QQQQQ   : 3                     3                   // appendText(IsoFields.QUARTER_OF_YEAR,
q       : 3                     3                   // appendValue(IsoFields.QUARTER_OF_YEAR);
qq      : 03                    03                  // appendValue(IsoFields.QUARTER_OF_YEAR,
qqq     : 3                     3                   // appendText(IsoFields.QUARTER_OF_YEAR,
qqqq    : 3                     3                   // appendText(IsoFields.QUARTER_OF_YEAR,
qqqqq   : 3                     3                   // appendText(IsoFields.QUARTER_OF_YEAR,
        :                                           // 
M       : 7                     7                   // appendValue(ChronoField.MONTH_OF_YEAR);
MM      : 07                    07                  // appendValue(ChronoField.MONTH_OF_YEAR,
MMM     : Jul                   7                   // appendText(ChronoField.MONTH_OF_YEAR,
MMMM    : July                  7月                  // appendText(ChronoField.MONTH_OF_YEAR,
MMMMM   : J                     J                   // appendText(ChronoField.MONTH_OF_YEAR,
L       : 7                     7                   // appendValue(ChronoField.MONTH_OF_YEAR);
LL      : 07                    07                  // appendValue(ChronoField.MONTH_OF_YEAR,
LLL     : 7                     7                   // appendText(ChronoField.MONTH_OF_YEAR,
LLLL    : 7                     7                   // appendText(ChronoField.MONTH_OF_YEAR,
LLLLL   : 7                     7                   // appendText(ChronoField.MONTH_OF_YEAR,
        :                                           // 
w       : 29                    29                  // append
cannot format 'DD': Field DayOfYear cannot be printed as the value 194 exceeds the maximum print width of 2
ww      : 29                    29                  // append
W       : 3                     3                   // append
d       : 13                    13                  // appendValue(ChronoField.DAY_OF_MONTH)
dd      : 13                    13                  // appendValue(ChronoField.DAY_OF_MONTH,
D       : 194                   194                 // appendValue(ChronoField.DAY_OF_YEAR)
DDD     : 194                   194                 // appendValue(ChronoField.DAY_OF_YEAR,
F       : 6                     6                   // appendValue(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH)
E       : Mon                   月                   // appendText(ChronoField.DAY_OF_WEEK,
EE      : Mon                   月                   // appendText(ChronoField.DAY_OF_WEEK,
EEE     : Mon                   月                   // appendText(ChronoField.DAY_OF_WEEK,
EEEE    : Monday                月曜日                 // appendText(ChronoField.DAY_OF_WEEK,
EEEEE   : M                     月                   // appendText(ChronoField.DAY_OF_WEEK,
e       : 2                     2                   // append
ee      : 02                    02                  // append
eee     : Mon                   月                   // appendText(ChronoField.DAY_OF_WEEK,
eeee    : Monday                月曜日                 // appendText(ChronoField.DAY_OF_WEEK,
eeeee   : M                     月                   // appendText(ChronoField.DAY_OF_WEEK,
c       : 2                     2                   // append
ccc     : Mon                   月                   // appendText(ChronoField.DAY_OF_WEEK,
cannot format 'AAAA': Field MilliOfDay cannot be printed as the value 44824000 exceeds the maximum print width of 4
cccc    : Monday                月曜日                 // appendText(ChronoField.DAY_OF_WEEK,
ccccc   : 1                     1                   // appendText(ChronoField.DAY_OF_WEEK,
a       : PM                    午後                  // appendText(ChronoField.AMPM_OF_DAY,
cannot format 'NNNN': Field NanoOfDay cannot be printed as the value 44824000000000 exceeds the maximum print width of 4
h       : 12                    12                  // appendValue(ChronoField.CLOCK_HOUR_OF_AMPM)
hh      : 12                    12                  // appendValue(ChronoField.CLOCK_HOUR_OF_AMPM,
H       : 12                    12                  // appendValue(ChronoField.HOUR_OF_DAY)
HH      : 12                    12                  // appendValue(ChronoField.HOUR_OF_DAY,
k       : 12                    12                  // appendValue(ChronoField.CLOCK_HOUR_OF_DAY)
kk      : 12                    12                  // appendValue(ChronoField.CLOCK_HOUR_OF_DAY,
K       : 0                     0                   // appendValue(ChronoField.HOUR_OF_AMPM)
KK      : 00                    00                  // appendValue(ChronoField.HOUR_OF_AMPM,
m       : 27                    27                  // appendValue(ChronoField.MINUTE_OF_HOUR)
mm      : 27                    27                  // appendValue(ChronoField.MINUTE_OF_HOUR,
s       : 4                     4                   // appendValue(ChronoField.SECOND_OF_MINUTE)
ss      : 04                    04                  // appendValue(ChronoField.SECOND_OF_MINUTE,
        :                                           // 
SSSS    : 0000                  0000                // appendFraction(ChronoField.NANO_OF_SECOND,
A       : 44824000              44824000            // appendValue(ChronoField.MILLI_OF_DAY)
n       : 0                     0                   // appendValue(ChronoField.NANO_OF_SECOND)
nnnn    : 0000                  0000                // appendValue(ChronoField.NANO_OF_SECOND,
N       : 44824000000000        44824000000000      // appendValue(ChronoField.NANO_OF_DAY)
VV      : Asia/Tokyo            Asia/Tokyo          // appendZoneId()
z       : JST                   JST                 // appendZoneText(TextStyle.SHORT)
zz      : JST                   JST                 // appendZoneText(TextStyle.SHORT)
zzz     : JST                   JST                 // appendZoneText(TextStyle.SHORT)
zzzz    : Japan Standard Time   日本標準時               // appendZoneText(TextStyle.FULL)
O       : GMT+9                 GMT+9               // appendLocalizedOffsetPrefixed(TextStyle.SHORT);
OOOO    : GMT+09:00             GMT+09:00           // appendLocalizedOffsetPrefixed(TextStyle.FULL);
X       : +09                   +09                 // appendOffset("+HHmm","Z")
XX      : +0900                 +0900               // appendOffset("+HHMM","Z")
XXX     : +09:00                +09:00              // appendOffset("+HH:MM","Z")
XXXX    : +0900                 +0900               // appendOffset("+HHMMss","Z")
XXXXX   : +09:00                +09:00              // appendOffset("+HH:MM:ss","Z")
x       : +09                   +09                 // appendOffset("+HHmm","+00")
xx      : +0900                 +0900               // appendOffset("+HHMM","+0000")
xxx     : +09:00                +09:00              // appendOffset("+HH:MM","+00:00")
xxxx    : +0900                 +0900               // appendOffset("+HHMMss","+0000")
xxxxx   : +09:00                +09:00              // appendOffset("+HH:MM:ss","+00:00")
Z       : +0900                 +0900               // appendOffset("+HHMM","+0000")
ZZ      : +0900                 +0900               // appendOffset("+HHMM","+0000")
ZZZ     : +0900                 +0900               // appendOffset("+HHMM","+0000")
ZZZZ    : GMT+09:00             GMT+09:00           // appendLocalizedOffset(TextStyle.FULL);
ZZZZZ   : +09:00                +09:00              // appendOffset("+HH:MM:ss","Z")

この一覧表を参考にしながらコーディングすれば、簡単にフォーマットできる。と思う。

先日公開した、Java8 Date and Time の cheat sheet にも追記した。 https://gist.github.com/tokuhirom/0b7441684653cb85cefa

Created: 2015-07-13 12:51:35
Updated: 2015-07-13 12:51:35

How do I include git revision in jar file with gradle?

If you want to include source git revision into jar file, you can include it in MANIFEST.MF. This helps you to solve issues.

jar {
    def revision = 'git rev-parse HEAD'.execute().text.trim()
    def date = new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
    manifest {
        attributes 'Implementation-Title': 'jetty with srever-starter sample',
          'Implementation-Version': version,
          'Main-Class': 'me.geso.example.Httpd',
          "Date": date,
          "Git-Revision": revision
    }
}
Created: 2015-07-11 08:58:51
Updated: 2015-07-11 08:58:51

gradle で findbugs の除外ルールを設定する

findbugs {
    excludeFilter = file("${project.rootDir}/config/findbugs/findbugs_filter.xml")
}

とかしておいて

以下のようなファイルを設置。 see http://findbugs.sourceforge.net/manual/filter.html ```

 <Match>
   <Class name="com.foobar.ClassWithSomeBugsMatched" />
   <Bug code="DE,UrF,SIC" />
 </Match>

 <!-- Match all XYZ violations. -->
 <Match>
   <Bug code="XYZ" />
 </Match>

 <!-- Match all doublecheck violations in these methods of "AnotherClass". -->
 <Match>
   <Class name="com.foobar.AnotherClass" />
   <Or>
     <Method name="nonOverloadedMethod" />
     <Method name="frob" params="int,java.lang.String" returns="void" />
     <Method name="blat" params="" returns="boolean" />
   </Or>
   <Bug code="DC" />
 </Match>

 <!-- A method with a dead local store false positive (medium priority). -->
 <Match>
   <Class name="com.foobar.MyClass" />
   <Method name="someMethod" />
   <Bug pattern="DLS_DEAD_LOCAL_STORE" />
   <Priority value="2" />
 </Match>

 <!-- All bugs in test classes, except for JUnit-specific bugs -->
 <Match>
  <Class name="~.*\.*Test" />
  <Not>
      <Bug code="IJU" />
  </Not>
 </Match>

```

Created: 2015-07-10 13:24:59
Updated: 2015-07-10 13:24:59

apache bench(ab) が OSX で刺さった夜は

ある日、OSX Yosemite で apache bench でベンチマークを使っていると、なぜか stuck して困っていた。

ab -c 10 -n 16500 http://127.0.0.1:5000/

どうも、対向サーバーを jetty にしても plack にしてもダメなので、なんなのかな~と。。


ab そのものを疑ってみる

いろいろ調べてみると、対向サーバーを変えても同じような感じで刺さるのでどうやら ab の側に問題があるのかな、と思って調べてみたところ、OSX Lion の時代にそのような問題が話題になっていたようだ。

最新版の ab は以下のようにして入れることができる。

wget http://ftp.tsukuba.wide.ad.jp/software/apache//httpd/httpd-2.4.12.tar.bz2
tar xzvf httpd-2.4.12.tar.bz2
cd httpd-2.4.12
sudo ln -s /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.10.xctoolchain
cd support/
make ab
cp ab /usr/local/bin/

osx のプロセスあたりのファイルデスクリプタ数制限を疑ってみる

kazuho さんが、「osx のプロセスあたりのファイルデスクリプタ数制限にぶちあたってるだけではないか」というアドバイスをくれたので調べてみたが、よくよく考えると -c 10 とかでやってるので枯渇することはなさそう。

-c 1000 とかだとありえる。

結論としては epehemeral port の枯渇でした、と

ephemeral port とは

An ephemeral port is a short-lived transport protocol port for Internet Protocol (IP) communications allocated automatically from a predefined range by the IP software.

のようなもののことですが、こいつが枯渇していた。

OSX では 49152 to 65535 = 16383 なので、ちょうどそのぐらいで枯渇してエラーになると。 kazuho さんによると「16325 みたいな数値をみたら、あー ephemeral」ってわかるものだそうです。僕にはわからなかった!

linux では同様の条件でやっても再現しないので困っていたのだが、単に linux の場合 ephemeral port の範囲がデフォルトで 32768 to 61000 = 28232 と広いため、-n 17000 とかでやっても再現していなかった。

まとめ

ab は腐ってない。

Created: 2015-07-10 09:25:34
Updated: 2015-07-10 09:25:34

Graceful shutdown on Embedded Jetty

You should set following 2 options.

    server.setStopAtShutdown(true);
    server.setStopTimeout(7_000);

setStopAtShutdown sets code Runtime.getRuntime().addShutdownHook to stop Jetty at JVM shutdown phase.

ref. https://github.com/eclipse/jetty.project/blob/master/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java

setStopTimeout is required. Without this option, Jetty kills all connections ASAP.

See following code.

    // Shall we gracefully wait for zero connections?
    long stopTimeout = getStopTimeout();
    if (stopTimeout>0)
    {
        long stop_by=System.currentTimeMillis()+stopTimeout;
        if (LOG.isDebugEnabled())
            LOG.debug("Graceful shutdown {} by ",this,new Date(stop_by));

        // Wait for shutdowns
        for (Future<Void> future: futures)
        {
            try
            {
                if (!future.isDone())
                    future.get(Math.max(1L,stop_by-System.currentTimeMillis()),TimeUnit.MILLISECONDS);
            }
            catch (Exception e)
            {
                mex.add(e);
            }
        }
    }

https://github.com/eclipse/jetty.project/blob/master/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java#L432-L453

Created: 2015-07-08 08:19:15
Updated: 2015-07-08 08:19:15

java と strace

java command は clone(2) するので、普通に strace してもダメ。 strace -f java ... のように -f オプションをつければ、clone した先も追ってくれるので、トレース可能となる。

ためしに以下のような何もしないプログラムを用意してみる。

public class Hello {
    public static void main(String[] args) {
    }
}

javac Hello.java とした後、strace -o java.log -f java Hello とすると、java.log というファイルが作成され、どのようなシステムコールを JVM が発行しているかがわかる。

ちなみに /usr/bin/perl -e0 した場合と比べると以下のようになり、Java で開発を行うと何もしないプログラムでもたくさんの system call を呼ぶことができて 便利 だということがわかる。

$ wc -l java.log perl.log
16735 java.log
232 perl.log
Created: 2015-06-30 07:40:02
Updated: 2015-06-30 07:40:02

golang で regexp のキャッシュ

golang で regexp をコンパイルするときは

    var stack_at_re = regexp.MustCompile(`\s*at ([^(]+)\(`)

のように MustCompile を利用して、global 変数に格納するのが基本なそうで。

(const で格納したいところだが、golang では MustCompile の結果が定数ではないので、それはできない。このへんが他の言語と違うところだ。)

Created: 2015-06-28 10:39:45
Updated: 2015-06-28 10:39:45

pthread のバージョンを確認したいでござる

pthread で NPTL が使われてるかどうか確認したかったのだけど、ただしく確認する方法がよくわからなかったので confstr で調べた。

#include <malloc.h>
#include <stdio.h>
#include <unistd.h>

int main() {
    char *buf;
    size_t n;

    n = confstr(_CS_GNU_LIBPTHREAD_VERSION, NULL, (size_t) 0);
    buf = malloc(n);
    if (buf == NULL) {
        printf("Cannot allocate memory\n");
        return;
    }
    confstr(_CS_GNU_LIBPTHREAD_VERSION, buf, n);
    printf("%s\n", buf);
}

昔の LinuxThreads では SIGUSR1, SIGUSR2 を使っていたりしていてユーザーが SIGUSER[12] 使えないなどの問題があったために、Linux 2.6 以後は NPTL に置き換えられており、もはや動作しているシステムでは殆どの場合、NPTL であると仮定してしまってよいように思う。とくに、新規で何かを作る場合、あえて LinuxThreads をサポートする必要はない。のだと思う。

Created: 2015-06-22 09:18:44
Updated: 2015-06-22 09:18:44

h2o の fastcgi サポートで Plack::Handler::FCGI を動かす

h2o で PHP を FCGI で動かすという話題が最近見かけますが、もちろん h2o+FCGI+Perl も快適に動作します。 具体的には以下のように書けばよろしい。

listen: 9090
hosts:
  default:
    paths:
      /:
        file.dir: /path/to/htdocs/
        fastcgi.spawn: "exec /opt/perl-5.18/bin/plackup -s FCGI --nproc 10 /path/to/app/"

簡単すぎて、とくに解説することがないです。

h2o+FCGI で運用した場合のメリットとして、Starlet 等の application server を立ち上げる場合と異なり、application server のプロセスを daemon tools や systemctl 等で管理する必要がないというメリットがある。

一方で、TCP+HTTP/1.1 でやっていれば通信を見るのが簡単なのに、STDIN で通信する FCGI では通信経路を観測するのが難しいので、そのへんをどう見るか、というところでしょうか。

Created: 2015-06-20 23:10:15
Updated: 2015-06-20 23:10:15