[gerrit] Permission denied (publickey) 에러

gerrit에서 clone 받을 때 Permission denied (publickey) 에러가 났을 때 확인해야 할 점 :

1. ssh 접근할 때 username이 gerrit의 'Settings' -> 'Profile' 메뉴 아래에 있는 username과 동일한지 확인

2. 'Settings' -> 'SSH Public Keys'에 public key 등록했는지 확인

3. ssh -vv -p 29418 john.doe@git.example.com 명령어로 접근이 되는지 확인


위와 같이 했는데, 3번에서 아래와 같은 에러가 나는 경우,

debug1: Skipping ssh-dss key /home/<user>/.ssh/id_dsa for not in PubkeyAcceptedKeyTypes


~/.ssh/config 에 파일 생성 후, 아래 설정 추가

PubkeyAcceptedKeyTypes ssh-dss


참고 사이트 :

https://git.eclipse.org/r/Documentation/error-permission-denied.html

https://coderwall.com/p/ykgawg/when-openssh-7-blocks-your-public-key

[Java 1.8] 날짜 정리

Java 1.8 날짜 정리

자바 1.8 이전에는 날짜 연산이 쉽지 않았다. Joda 같은 라이브러리들을 쓰면 된다고 하는데, 필자는 Util 클래스에서 내부적으로 Calendar를 사용하여 연산하고 Date/long/String 간의 변환을 통하여 사용해왔다. 그러던 와중에 자바 1.8의 새로운 날짜들을 보니 신세계가 열렸다. 그래서 간단하게 소개하고 자주 사용될 만한 예제들도 나열하려 한다.

먼저 중요하다고 생각되는 클래스들을 소개하겠다.

클래스

날짜 (Temporal)

Instant : machine time에 유용한 1970년 1월 1일부터 시간을 세는 클래스 (millisecond 뿐만 아니라 nanosecond까지 센다)

LocalDate : [년,월,일]과 같은 날짜만 표현하는 클래스 (시간은 포함하지 않는다)

LocalDateTime : [년,월,일,시,분,초]를 표현하는 클래스 (LocalDate와 함께 가장 많이 쓰이는 클래스가 될 것 같다)

LocalTime : [시,분,초]와 같이 시간만 표현하는 클래스

기간 (TemporalAmount)

Period : 두 날짜 사이의 [년,월,일]로 표현되는 기간 (시간을 다루지 않다 보니 LocalDate를 사용한다)

Duration : 두 시간 사이의 [일,시,분,초]로 표현되는 기간 (Instant 클래스를 사용하고, seconds와 nanoseconds로 측정 되지만 [일,시,분,초]로 변환해 주는 메쏘드를 제공)

기타

ChronoUnit : 한가지의 단위를 표현하기 위한 클래스 (년,월,일,시,분,초 등)

DayOfWeek : 요일

자주 쓰는 메쏘드들

  • 날짜 가져오기

      LocalDate.now(); // 오늘
      LocalDateTime.now(); // 지금
      LocalDate.of(2015, 4, 17); // 2015년4월17일
      LocalDateTime.of(2015, 4, 17, 23, 23, 50); // 2015년4월17일23시23분50초
      Year.of(2015).atMonth(3).atDay(4).atTime(10, 30); // 2015년3월4일 10시30분00초
    
  • 기간 가져오기

      Period.ofYears(2); // 2년간(P2Y)
      Period.ofMonths(5); // 5개월간(P5M)
      Period.ofWeeks(3); // 3주간(P21D)
      Period.ofDays(20); // 20일간(P20D)
    
      Duration.ofDays(2); // 48시간(PT48H)
      Duration.ofHours(8); // 8시간(PT8H)
      Duration.ofMinutes(10); // 10분간(PT10M)
      Duration.ofSeconds(30); // 30초간(PT30S)
    
  • 날짜 + 기간 = 날짜

      LocalTime.of(9, 0, 0).plus(Duration.ofMinutes(10)); // (9시 + 10분간) = 9시10분
      LocalDate.of(2015, 5, 15).plus(Period.ofDays(1)); // (2015년5월15일 + 1일간) = 2015년5월16일
      LocalDateTime.of(2015, 4, 17, 23, 47, 5).minus(Period.ofWeeks(3)); // (2015년4월17일 23시47분05초 - 3주간) = 2015년3월27일 23시47분05초
      LocalDate.now().plusDays(1); // (오늘 + 1일) = 내일
      LocalTime.now().minusHours(3); // (지금 - 3시간) = 3시간 전
    
  • 날짜 - 날짜 = 기간

      Period.between(LocalDate.of(1950, 6, 25), LocalDate.of(1953, 7, 27)); // (1953년7월27일 - 1950년6월25일) = 3년1개월2일간(P3Y1M2D)
      Period.between(LocalDate.of(1950, 6, 25), LocalDate.of(1953, 7, 27)).getDays(); // 3년1개월2일간 => 2일간
      LocalDate.of(1950, 6, 25).until(LocalDate.of(1953, 7, 27), ChronoUnit.DAYS); // 3년1개월2일간 => 1128일간
      ChronoUnit.DAYS.between(LocalDate.of(1950, 6, 25), LocalDate.of(1953, 7, 27)); // 3년1개월2일간 => 1128일간
    
      Duration.between(LocalTime.of(10, 50), LocalTime.of(19, 0)); // (19시00분00초 - 10시50분00초) = 8시간10분간(PT8H10M)
      Duration.between(LocalDateTime.of(2015, 1, 1, 0, 0), LocalDateTime.of(2016, 1, 1, 0, 0)).toDays(); // 365일간
      ChronoUnit.YEARS.between(LocalDate.of(2015, 5, 5), LocalDate.of(2017, 2, 1)); // 1년간
    
  • 날짜 변환하기

    • LocalDate -> String

      LocalDate.of(2020, 12, 12).format(DateTimeFormatter.BASIC_ISO_DATE); // 20201212
      
    • LocalDateTime -> String

      LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 2015-04-18 00:42:24
      
    • LocalDateTime -> java.util.Date

      Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()); // Sat Apr 18 01:00:30 KST 2015
      
    • LocalDate -> java.sql.Date

      Date.valueOf(LocalDate.of(2015, 5, 5)); // 2015-05-05
      
    • LocalDateTime -> java.sql.Timestamp

      Timestamp.valueOf(LocalDateTime.now()); // 2015-04-18 01:06:55.323
      
    • String -> LocalDate

      LocalDate.parse("2002-05-09"); // 2002-05-09
      LocalDate.parse("20081004", DateTimeFormatter.BASIC_ISO_DATE); // 2008-10-04
      
    • String -> LocalDateTime

      LocalDateTime.parse("2007-12-03T10:15:30"); // 2007-12-03T10:15:30
      LocalDateTime.parse("2010-11-25 12:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 2010-11-25T12:30
      
    • java.util.Date -> LocalDateTime

      LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()); // 2015-04-18T01:16:46.755
      
    • java.sql.Date -> LocalDate

      new Date(System.currentTimeMillis()).toLocalDate(); // 2015-04-18
      
    • java.sql.Timestamp -> LocalDateTime

      new Timestamp(System.currentTimeMillis()).toLocalDateTime(); // 2015-04-18T01:20:07.364
      
    • LocalDateTime -> LocalDate

      LocalDate.from(LocalDateTime.now()); // 2015-04-18
      
    • LocalDate -> LocalDateTime

      LocalDate.now().atTime(2, 30); // 2015-04-18T02:30
      
  • 요일로 날짜 가져오기

      LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SATURDAY)); // 다음 토요일
      LocalDate.of(2016, 5, 1).with(TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.SUNDAY)); // 2016년5월 세번째 일요일
      LocalDate.of(2015, 7, 1).with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); // 2015년7월 첫번째 월요일
    
  • 언어별 출력

      DayOfWeek.MONDAY.getDisplayName(TextStyle.FULL, Locale.ENGLISH); // Monday
      DayOfWeek.MONDAY.getDisplayName(TextStyle.NARROW, Locale.ENGLISH); // M
      DayOfWeek.MONDAY.getDisplayName(TextStyle.SHORT, Locale.ENGLISH); // Mon
    
      DayOfWeek.MONDAY.getDisplayName(TextStyle.FULL, Locale.KOREAN); // 월요일
      DayOfWeek.MONDAY.getDisplayName(TextStyle.NARROW, Locale.KOREAN); // 월
      DayOfWeek.MONDAY.getDisplayName(TextStyle.SHORT, Locale.KOREAN); // 월
    
      Month.FEBRUARY.getDisplayName(TextStyle.FULL, Locale.US); // February
      Month.FEBRUARY.getDisplayName(TextStyle.FULL, Locale.KOREA); // 2월
    

참고

[spring security] enum으로 @Secured 권한 관리하기

Spring Security Annotations


spring security는 @Secured나 @PreAuthorize 같은 어노테이션을 사용하여 메소드에 권한을 줄 수 있다. Secured 어노테이션은 @Secured("ROLE_USER") 와 같이 권한 이름을 파라미터로 사용하는데, 이 권한 이름들을 Enum에 정의하고, 사용해보자.

아래와 같은 권한용 Enum이 있다.

import org.springframework.security.core.GrantedAuthority;

public enum MyAuthority implements GrantedAuthority{

    USER("ROLE_USER", "유저권한"),
    ADMIN("ROLE_ADMIN", "어드민권한");

    private String authority;
    private String description;

    private MyAuthority(String authority, String description){
        this.authority = authority;
        this.description = description;
    }

    @Override
    public String getAuthority() {
        return authority;
    }

    public String getDescription() {
        return description;
    }

}

그리고 나서 @Secured에서 아래와 같이 사용해보자.

@Secured(MyAuthority.USER.getAuthority())

@Secured안에는 String이 들어가고, getAuthority()는 String을 리턴하는데, "The value for annotation attribute Secured.value must be a constant expression" 라는 컴파일 에러가 났다. 어노테이션안에 Enum을 사용한 적은 많은 것 같은데, 왜 에러일까?

Enum 타입 사용이 가능했던 어노테이션들은 실제로 Enum을 받도록 만들어진 어노테이션이였다. @RequestMapping의 method를 예로들면, 타입이 String이 아닌 RequestMethod 라는 Enum을 받도록 되어있다. 만약 @Secured에서 Enum을 사용하려 했다면, String이 아닌 Enum을 받도록 설계되어 있어야 한다.

그렇다면 방법은 전혀 없는 것일까? 차선책으로는 static 레벨에 final로 권한을 선언하는 것이다. Enum 안에 ROLE_USER와 같은 권한들은 객체안에 생성되서 컴파일러가 가변적이라고 판단하는 것 같다. 그래서 코드를 아래와 같이 바꾸었다.

import org.springframework.security.core.GrantedAuthority;

public enum MyAuthority implements GrantedAuthority{

    USER(ROLES.USER, "유저권한"),
    ADMIN(ROLES.ADMIN, "어드민권한");

    public static class ROLES{
        public static final String USER = "ROLE_USER";
        public static final String ADMIN = "ROLE_ADMIN";
    }

    private String authority;
    private String description;

    private MyAuthority(String authority, String description){
        this.authority = authority;
        this.description = description;
    }

    @Override
    public String getAuthority() {
        return authority;
    }

    public String getDescription() {
        return description;
    }
}

@Secured를 사용하는 부분은 아래와 같이 바꾸면 잘 된다.

@Secured(MyAuthority.ROLES.USER)

기존의 목표였던 권한을 Enum에서 관리하면서, Secured 어노테이션에서도 그 권한을 참조 할 수 있게 되었다. String을 파라미터로 받는 어노테이션에 변수를 전달하고 싶을 때는 final로 선언되어 있어야 한다.

[eclipse] 외부 에디터 사용할때 새로고침 자동으로 설정

화면쪽 작업을 할 때, 이클립스 에디터 말고 다른 에디터를 사용하고 싶다. 하지만 다른 에디터에서 저장을 해도 화면에 바로 적용이 안되고 이클립스에서 Refresh를 해줘야 하는 불편함이 있다. 그래서 찾아보니 Refresh 안해도 되게 하는 설정이 있다.

Eclipse 메뉴에서 Window > Preferences 선택하면 창이 뜬다.
그리고 General > Workspace 페이지로 가서 Refresh using native hooks or polling 설정을 체크해 주면 된다.

이것을 설정하면, 파일 업로드를 해도 Refresh를 해줘야 했었는데 Refresh 안해도 될 것 같다.

참고 : http://help.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.platform.doc.user%2Ftasks%2Ftasks-52.htm

[spring] static에서 spring bean 가져오기

먼저 목적은 아래와 같은 코드가 작동하게 하는 것이다.

@Component
public class Foo {
    public void doA(){
        System.out.println("doA");
    }
}

public class Boo {
    @Autowired
    public static Foo foo;
}

public class ApplicationTests {
    @Test
    public void contextLoads() {
        Boo.foo.doA();
    }
}

물론 디자인 상으로는 클래스에 저장되는 static 자원에서 객체를 가리키게 하는 것은 좋지 않지만, 이런 상황에서 그나마 깔끔하게 처리하는 방법은 없을까?

문제점 :

  1. 먼저 @Autowired는 Spring에 의해서 관리되야 주입을 해주는데, Boo는 현재 그렇지 않다.
  2. 아무리 Boo에서는 클래스 레벨에서 Foo를 사용하고 싶다고 하더라도 객체는 클래스 로딩된 후에 생성되기 때문에 Boo 클래스가 로딩될 당시에 Foo 객체는 존재하지 않는다.

해결책 :

  • Boo 도 싱글턴 객체로 스프링에서 관리되게 등록해준다.
  • Boo 객체가 생성될 때 Foo 객체를 가져오고, static 필드에 넘겨준다.

먼저 첫번째 방법은 @PostContruct를 이용해서 static 필드에 값을 넘겨주는 방법이다.

@Component
public class Boo {

    public static Foo foo;

    @Autowired
    private Foo fooInstance;

    @PostConstruct
    private void init() {
        foo = fooInstance;
    }
}

두번째 방법은 생성자에 @Autowired를 사용하는 방법이다.

@Component
public class Boo {

    public static Foo foo;

    @Autowired
    private Boo(Foo foo) {
        this.foo = foo;
    }
}

생성자나 init 같은 함수들은 private으로 하더라도 Spring이 알아서 접근해서 호출해준다. 생성자에 @Autowired는 파라미터로 들어오는 bean을 주입시켜 준다.

참고 : http://stackoverflow.com/questions/17659875/autowired-and-static-method

[sublime] 매크로 만들기

개요

자주 사용하는 서브라임 기능들의 묶음을 매크로로 만들어서 쓸 수 있다.

Tools > Record Macro 선택 후 원하는 작업들을 한 후에 Tools > Stop Recording Macro 를 선택하면 작업들이 임시 저장되고, 영구 저장하고 싶다면 Tools > Save Macro를 해서 파일로 저장하면 된다.

한가지 예제를 만들어보자.

예제

평소에 DTO나 VO 같은 객체를 생성할 때, 모든 필드에 private을 써주는 것은 참 번거로운 일이다. 그래서 문자열 앞에 private을 붙여주는 매크로를 하나 만들었다. 첨부된 매크로를 ‘서브라임/Data/Packages/User/‘ 밑에 저장한다.


privateModifier.sublime-macro


다음 서브라임 메뉴에서 Preferences > Key Bindings - User를 열고 아래 소스를 붙여넣어 준다.

[
    { "keys": ["ctrl+k", "ctrl+p"], "command": "run_macro_file", "args": {"file": "Packages/User/privateModifier.sublime-macro"} }
]

이제 준비 완료다. 이제 어디서든 ctrl+k, ctrl+p 를 누르면 모든 문자열 앞에 private 이 붙는다.

아래와 같은 코드가 있다고 가정해보자.

int memberNo;
String memberId;
String memberName;
int age;

ctrl+k, ctrl+p를 누른 결과는 다음과 같다.

private int memberNo;
private String memberId;
private String memberName;
private int age;

결론

공통으로 뺄 수 있는 작업들은 매크로로 빼고, 단축키로 사용하면 훨씬 작업 속도가 빨라질 것이다. 아쉽지만 replace 기능은 매크로로 저장되지 않는다. 그래도 최대한 있는 기능들을 활용해서 조금이나마 야근을 안하는데 도움이 됐으면 좋겠다.


[Java 1.8] 두개의 리스트 조인(병합)하기

자바에서 타입이 다른 두개의 리스트를 조인하고 싶다. 데이터베이스에서 조인을 해서 가져오는 것이 최선이지만 어플에서 조인을 해야하는 경우도 종종 생기는 것 같다. 마침 자바 1.8 에서 제공하는 람다 표현식을 써보고 싶어서 테스트 겸해서 하나 만들어 보았다.

예제

게시판 리스트 화면을 예로 들겠다. 게시글 리스트에서 회원이름을 출력하고 싶다. 게시판인 Article 클래스에는 memberNo를 가지고 있고, Member클래스에는 memberNo와 memberName이 있다. List<Article> 와 List<Member>를 조인해보자.




List<Article>안에 memberName이 전부 null이 였는데, Member 클래스에서 회원번호를 찾아서 이름을 넣어주는 코드다.


List<Article> articles;
List<Member> member;

for (Article article : articles) {
    for (Member member : members) {
        if (article.getMemberNo() == member.getMemberNo()) {
            article.setMemberName(member.getMemberName());
        }
    }
}

이중 루프를 썼더니 잘 된다. 이제 같은 동작을 1.8 의 람다 표현식을 사용해서 구현해보자. CollectionJoiner 라는 클래스를 만들어서 안에서 루프를 돌리고, 외부에서는 조건문과 할당해주는 부분만 넘기면 되게 해보자.

먼저 테스트할 코드부터 작성해보자.


List<Article> newList = CollectionJoiner.collections(articles, members)
    .when((article, member) -> article.getMemberNo() == member.getMemberNo())
    .then((article, member) -> {
        article.setMemberName(member.getMemberName());
        return article;
    });

2중루프와 같은 동작을 하는 CollectionJoiner 클래스를 사용할 때의 코드이다. 위의 코드가 작동하게 하기 위해서는 두가지의@FunctionalInterface를 사용해야 한다.

  • BiPredicate<T, U> 클래스의 boolean test(T, U);
  • BiFunction<T, U, R> 클래스의 R apply(T, U);

이제 아래 클래스를 생성해보자.

CollectionJoiner.java

package com.test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;

public class CollectionJoiner{

    private CollectionJoiner() {
    }

    public static <T, U> CollectionJoinerHelper<T, U> collections(Collection<T> t, Collection<U> u) {
        return new CollectionJoinerHelper<T, U>(t, u);
    }

    public static class CollectionJoinerHelper<T, U> {

        private Collection<T> t;
        private Collection<U> u;
        private BiPredicate<T, U> condition;

        private CollectionJoinerHelper(Collection<T> t, Collection<U> u) {
            this.t = t;
            this.u = u;
        }

        public CollectionJoinerHelper<T, U> when(BiPredicate<T, U> condition) {
            this.condition = condition;
            return this;
        }

        public <R> List<R> then(BiFunction<T, U, R> function) {

            List<R> list = new ArrayList<>();

            for (T tOne : t) {
                for (U uOne : u) {
                    if (condition.test(tOne, uOne)) {
                        list.add(function.apply(tOne, uOne));
                    }
                }
            }

            return list;
        }

    }

}

클래스를 추가하고 실행해보면, 결과는 이중루프를 돌린 것과 같다.

처음에는 CollectionJoiner 같은 클래스로 만들면 사용할 때, 코드 양이 줄어들 줄 알았는데, 비슷한 것 같다. 그래도 자바 1.8의 람다를 연습하는 데에는 도움이 되었다.
지금은 then을 호출하자마자 List<R>를 리턴하는데, 나중에는 리턴타입도 Collectors 클래스를 통해서 정할 수 있도록 만들어 봐야겠다.

[maven] 메이븐 기본 properties

pom/project

빌드 : ${project.build.directory} = ${pom.build.directory} = ‘target’;
빌드 : ${project.build.outputDirectory} = ‘target/classes’;
프로젝트 이름 : ${project.name} = ${pom.name} = / 엘리먼트 설정 값;
프로젝트 버전 : ${project.version} = ${pom.version} = ${version} = / 엘리먼트 설정 값;
최종 파일 이름 : ${project.build.finalName}= // 엘리먼트 설정 값

settings

로컬 저장소 경로 : ${settings.localRepository}

environment variable

시스템 PATH : {env.PATH}
시스템 JAVA_HOME : {env.JAVA_HOME}

etc

pom.xml이 위치하는 디렉토리 : ${basedir}

[spring security + uaa] 로그인용 OAuth 서버 사용하기

목표

로그인 전용 서버를 따로 두고, 여러 어플리케이션에서 로그인을 처리하지 않고, 로그인 전용 서버에 역할을 위임해서 처리하고 싶다. 이 글에서는 OAuth를 사용하여 로그인 전용 서버로 사용했다.





App 이라는 웹 어플리케이션에서 로그인 서버의 인증을 사용하는 케이스의 sequence diagram 이다. 인증 방법은 authorization_code 방법이다.

App에서는 사용자의 정보를 가지고 있지 않고, 로그인 서버에서 사용자 정보를 가져온 후, 세션에 저장한다.

코드 관점에서 보면 App이 Spring Security를 사용한다면, SecurityContext에 외부에서 가져온 데이터를 채워 넣어야 한다.

방법

CloudFoundry의 User Account and Authentication Service(UAA) 에 이러한 서비스를 제공하는 라이브러리가 있다.

<dependency>
    <groupId>org.cloudfoundry.identity</groupId>
    <artifactId>cloudfoundry-identity-common</artifactId>
    <version>1.4.3</version>
</dependency>

이 라이브러리 안에는 ClientAuthenticationFilter가 있는데, UsernamePasswordAuthenticationFilter 대신 사용하면 된다.

샘플용 소스를 하나 만들어서 github에 올렸다. https://github.com/jekalmin/samples 에서 uaa 폴더 안에 프로젝트가 두개 있는데, 아래처럼 둘 다 받아서 서버에 올리면 된다.

여기서는 oauth_consumer가 위에서 설명한 App이고, oauth_provider가 Login Server이다.

서버를 실행하고, 위의 Authentication Sequence를 보면서 테스트해보자.

먼저 http://localhost:8080/oauth_consumer를 호출하면, Authentication Sequence의 1,2,3,4 번에 해당하는 작업을 수행한다. 그래서 아래 이미지와 같이 4번에 해당하는 로그인 페이지에 오게 되었다.

min / min 계정으로 로그인을 해보면 아래 이미지와 같이 권한 승인 페이지로 가게 되는데, 이 부분은 Authentication Sequence의 6번에 해당하는 권한 승인 페이지이다.

승인을 하게 되면, Authentication Sequence의 7번부터 15번까지 실행되고 아래와 같이 http://localhost:8080/oauth_consumer 에 해당하는 페이지가 출력된다.

User Account and Authentication Service(UAA)

위의 예제에서는 App 부분인 oauth_consumer에만 UAA의 라이브러리를 사용했고, 로그인 서버부분인 oauth_provider는 예전에 포스팅했던 http://jekalmin.tistory.com/entry/spring-bootoauth-%EC%84%B8%ED%8C%85-%ED%85%8C%EC%8A%A4%ED%8A%B8 글의 코드와 같다.

oauth_consumer에서 UAA의 ClientAuthenticationFilter가 Authentication Sequence의 핵심인 2,10,12,14 번에 해당하는 부분들을 담당하고 있다.

UAA에서는 클라이언트 사이드 뿐만 아니라 서버사이드도 많은 기능을 제공하는데(revoke token, RemoteTokenStore, etc..), 사실 서버쪽의 기능들이 더 핵심인 것 같다.

p.s.) ClientAuthenticationFilter에서 redirect와 /oauth/token을 요청하는 부분은 https://github.com/dsyer/uaa/blob/feature/bootify/common/src/main/java/org/cloudfoundry/identity/uaa/client/SocialClientUserDetailsSource.java 의 99번째 줄인 restTemplate에 프록시로 걸려있다.

Sample Code

https://github.com/jekalmin/samples

Reference


[eclipse] checkstyle error : expecting EOF found '{'

Java 1.8에서 Checkstyle을 사용할 때, 람다 표현을 쓰면 expecting EOF found '{' 와 같은 에러가 난다.

Checkstyle 5.9 버전부터 자바 8을 지원한다고 하니 업그레이드 하고 나서 refresh 하면 된다.


Reference : 

https://jira.codehaus.org/browse/MCHECKSTYLE-253