Model, Model를 요소로 두는 Array, Map, List를 쉽게 테스트할 수 있는 AssertJ 테스트 코드이다.


import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.junit.Test;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.StrictAssertions.entry;


public class AssertJTest {
@Test
public void StringTest() {
Model model = new Model(10, "Jackson");
assertThat(model.getName()).isEqualTo("Jackson");

assertThat(model.getName()).startsWith("Ja")
.endsWith("on")
.isEqualToIgnoringCase("jackson");
}

@Test
public void ListTest() {
Model samuel = new Model(1, "Samuel");
Model jackson = new Model(2, "Jackson");
Model matt = new Model(3, "Matt");
Model nobody = new Model(4, "nobody");

List<Model> models = Stream.of(samuel, jackson, matt)
.collect(toList());

assertThat(models).hasSize(3)
.contains(samuel, matt, jackson)
.doesNotContain(nobody);
}

@Test
public void MapTest() {
Model samuel = new Model(1, "Samuel");
Model jackson = new Model(2, "Jackson");
Model matt = new Model(3, "Matt");
Model nobody = new Model(4, "nobody");

// when
List<Model> models = Stream.of(samuel, jackson, matt)
.collect(toList());

Map<Integer, String> modelMap = models.stream()
.peek(System.out::println) // debug
.collect(Collectors.toMap(Model::getId, Model::getName));

assertThat(modelMap).hasSize(3)
.contains(entry(1, "Samuel"), entry(2, "Jackson"), entry(3, "Matt"))
.doesNotContainEntry(4, "nobody");
}
}

class Model {
private int id;
private String name;

public Model(int id, String name) {
this.name = name;
this.id = id;
}

public String getName() {
return name;
}

public int getId() {
return id;
}

public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}

}


Posted by '김용환'
,


spring boot 의 Application.java의 위치는 중요하다!!!!



spring boot 문서에 따르면, Application 클래스는 root 패키지에 위치하라고 추천하고 있다.

http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-locating-the-main-class



com +- example +- myproject +- Application.java | +- domain | +- Customer.java | +- CustomerRepository.java | +- service | +- CustomerService.java | +- web +- CustomerController.java





Application.java를 com.example.myproject.conf 패키지 밑에 두고 Spring boot 을 실행을 해보았다.

JPA 없는 환경에서는 동작이 잘 되지만, JPA 쓰는 상황에서는 JPA Entity 을 읽지 못하는 이슈가 발생할 수 있다. 


따라서, spring boot 의 Application 클래스는 root 패키지에 위치하도록 한다. 

예제) 

https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples/spring-boot-sample-simple/src/main/java/sample/simple  



Posted by '김용환'
,


Spring Boot 애플리케이션을 개발할 때, DB를 쓰지 않을 때는 @EnableAutoConfiguration(exclude='') 를 이용하여 초기화 할 때, DB 연결이 되지 않도록 한다.


@Configuration
@ComponentScan
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, RabbitAutoConfiguration.class,
FreeMarkerAutoConfiguration.class})
@ImportResource("classpath:application-context.xml")
//@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}



이렇게 아무 생각없이 사용하다가, DB 관련 설정을 추가하여 개발하려고 테스트하다
다음과 같이 NoSuchBeanDefinitionException [javax.sql.DataSource] 이 발생할 수 있다. 
(JPA 연동 설명 : https://mhdevelopment.wordpress.com/2014/02/09/bootstrap-an-application-with-spring-boot-part1-command-line/)

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}



이럴 때는 DataSource관련 클래스를 @EnableAutoConfiguration(exclude='')에서 제외시킨다.
 

@Configuration
@ComponentScan
@EnableAutoConfiguration(exclude = { RabbitAutoConfiguration.class,
        SecurityAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class})
@ImportResource("classpath:application-context.xml")
//@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

에러 없이 DB 연결되고, DataSource에 적절한 객체가 바인딩될 수 있다. 


https://mhdevelopment.wordpress.com/2014/02/09/bootstrap-an-application-with-spring-boot-part1-command-line/


Posted by '김용환'
,



Jenkins 재시작 후 아래와 같은 에러 발생시, Java8이 아닌지 확인한다.

모든 문제의 해결이 아닐 수 있지만, Java8에서는 Jenkins 일부 버전은 작동이 되지 않는다. Java8에서 Java7으로 바꾸니. 동작했다. 





hudson.util.HudsonFailedToLoad: org.jvnet.hudson.reactor.ReactorException: java.io.IOException: Unable to read /home/www/.jenkins/config.xml

at hudson.WebAppMain$3.run(WebAppMain.java:234)

Caused by: org.jvnet.hudson.reactor.ReactorException: java.io.IOException: Unable to read /home/www/.jenkins/config.xml

at org.jvnet.hudson.reactor.Reactor.execute(Reactor.java:269)

at jenkins.InitReactorRunner.run(InitReactorRunner.java:44)

at jenkins.model.Jenkins.executeReactor(Jenkins.java:910)

at jenkins.model.Jenkins.<init>(Jenkins.java:809)

at hudson.model.Hudson.<init>(Hudson.java:82)

at hudson.model.Hudson.<init>(Hudson.java:78)

at hudson.WebAppMain$3.run(WebAppMain.java:222)

Caused by: java.io.IOException: Unable to read /home/www/.jenkins/config.xml

at hudson.XmlFile.unmarshal(XmlFile.java:165)

at jenkins.model.Jenkins$17.run(Jenkins.java:2561)

at org.jvnet.hudson.reactor.TaskGraphBuilder$TaskImpl.run(TaskGraphBuilder.java:169)

at org.jvnet.hudson.reactor.Reactor.runTask(Reactor.java:282)

at jenkins.model.Jenkins$7.runTask(Jenkins.java:899)

at org.jvnet.hudson.reactor.Reactor$2.run(Reactor.java:210)

at org.jvnet.hudson.reactor.Reactor$Node.run(Reactor.java:117)

at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

at java.lang.Thread.run(Unknown Source)

Caused by: com.thoughtworks.xstream.converters.ConversionException: -1 : -1

---- Debugging information ----

message             : -1

cause-exception     : java.lang.ArrayIndexOutOfBoundsException

cause-message       : -1

class               : hudson.model.Hudson

required-type       : hudson.model.Hudson

converter-type      : hudson.util.RobustReflectionConverter

path                : /hudson/disabledAdministrativeMonitors

line number         : 3

version             : not available

-------------------------------

at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:79)

at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:65)

at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:66)

at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:50)

at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:134)

at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:32)

at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1061)

at hudson.util.XStream2.unmarshal(XStream2.java:113)

at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1045)

at hudson.XmlFile.unmarshal(XmlFile.java:163)

... 9 more

Caused by: java.lang.ArrayIndexOutOfBoundsException: -1

at com.thoughtworks.xstream.core.util.OrderRetainingMap.entrySet(OrderRetainingMap.java:77)

at java.util.HashMap.putMapEntries(Unknown Source)

at java.util.HashMap.putAll(Unknown Source)

at com.thoughtworks.xstream.core.util.OrderRetainingMap.<init>(OrderRetainingMap.java:36)

at com.thoughtworks.xstream.converters.reflection.FieldDictionary.buildMap(FieldDictionary.java:135)

at com.thoughtworks.xstream.converters.reflection.FieldDictionary.fieldOrNull(FieldDictionary.java:113)

at com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider.getFieldOrNull(PureJavaReflectionProvider.java:186)

at hudson.util.RobustReflectionConverter.fieldDefinedInClass(RobustReflectionConverter.java:344)

at hudson.util.RobustReflectionConverter.doUnmarshal(RobustReflectionConverter.java:283)

at hudson.util.RobustReflectionConverter.unmarshal(RobustReflectionConverter.java:228)

at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:72)

... 18 more





Posted by '김용환'
,


jenkins 에서 가장 중요한 것은 설정 백업인 것 같다.

예전에는 rsync 나 scp(rcp)로 백업 장비로 복사했었는데,

git repository에 저장하는 것도 좋다.


shell script/git command를 사용하는 방식도 좋지만, thinBackup 플러그인을 사용한다. 


thinBackup 플러그인을 jenkins에 설치하고, 설정된 값으로 git repository에 저장할 수 있다.




1. Jenkin 플러그인 관리에서 Available 탭에서 thinBackup을 설치하고 재시작한다.

참조 : http://www.asktomash.com/configure-jenkins-thinbackup-configuration-plugin/



2. ThinBackup 설정하기


 Jenkins 관리 - ThinBackup 설정하기 - Settings 선택 해서 아래와 같이 설정했다. 'backup build results'를 선택하면,  zip으로 만들기 때문에 build 디렉토리 밑의 로그도 함께 백업되기 때문에 용량이 커진다. 

따라서,간단하게만 아래와 같이 설정했다.



여러 대의 slave jenkins을 쓰는 경우 다른 서버에서 동작될 수 있으니, master에서만 동작하게 한다.







그리고, 나머지는 다음 블로그의 내용대로 하면 된다.

http://labs.ssen.name/Server/Jenkins/Jenkins%20%EC%9E%90%EB%8F%99%20%EB%B0%B1%EC%97%85%ED%95%98%EA%B8%B0.html




* 참조 자료

https://wiki.jenkins-ci.org/display/JENKINS/thinBackup

https://github.com/jenkinsci/thin-backup-plugin

http://labs.ssen.name/Server/Jenkins/Jenkins%20%EC%9E%90%EB%8F%99%20%EB%B0%B1%EC%97%85%ED%95%98%EA%B8%B0.html

Posted by '김용환'
,




play1.3-jdk8-spring4.1.6 테스트 한 내용이다.

결론은 '그대로는 안되고, 동작되게 한다 해도 그걸 위해 해야 하는 오버헤드가 있다는 점 '


spring 3에서는 classpath 를 엄격히 따지지 않았는데, spring 4 부터는 해당 클래스가 classpath에 있는지 엄격히 본다.


spring 3에서는 문제가 없었는데, spring 4는 precompile된 클래스들을 scanning하고 잘 하고, spring container 초기화할 때, classpath에 있는지 체크한다. 따라서 아래와 같은 에러가 발생할 수 있다.

'

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.google.map.Location]; nested exception is java.io.FileNotFoundException: class path resource [com.google.map.Location] cannot be opened because it does not exist

'


다양한 테스트 끝에 precompile된 클래스를 jar로 묶어 lib/ 디렉토리안에 복사하니, 잘 동작한다.


참고로 play classpath는 아래와 고정되어 있고, 사용자 정의 classpath를 추가하거나(play 소스를 고쳐야 한다), play의 spring 사용 부분은 webapp의 spring 관련 패키지를 모두 jar로 바꾸면 된다. 

'

The conf/ directory for the application

The $PLAY_PATH/framework/play-$version.jar

All JAR files found in your application’s lib/ directory

All JAR files found in the $PLAY_PATH/framework/lib/ directory

참조 : https://www.playframework.com/documentation/1.2.2/ide#Classpathsettings

'


구질구질해서 java7 + spring 3 + play 1.3 쓰는 게 나을 것 같다. 


참고로 play2 spring 연동 모듈을 살펴보면, scanning하는 코드가 없는 이유가 있다. 

https://github.com/ydeshayes/play-2.3.x-spring-module/blob/master/app/play/api/modules/spring/SpringPlugin.scala

그냥 컴파일하고 쓰기 때문에 복잡하지 않다.play1은 precompile이라는 특이한 기능때문에 classpath 연동 이슈가 생길 수 있다. 


Posted by '김용환'
,


Spring Boot/Loaded 개발을 원활히 하기 위해서는 Intellij 14.1.0 이상의 버전을 사용하는 것이 좋다.

Intellij 14.1.0 부터 Spring Boot 애플리케이션을 공식적으로 지원한다.


Preference >

Build,Execution,Deployment >

Compiler  에서


Make project automatically 체크 박스에에 check on을 한다.




14.1.0 이번 버전에서는 파일을 수정한 후, 

Build > Compile "파일.java"를 해야 한다.




참고로, java8 환경으로 에러가 난다면, 




다음과 같이 셋팅해주시는 것이 좋다. 






Posted by '김용환'
,


spring 3.2에서 4.1 migration시 아래 에러가 발생했다.


org.springframework.mail.javamail.JavaMailSenderImpl cannot be resolved



pom.xml 파일에 아래 dependency를 추가한다.


<dependency>

            <groupId>org.springframework</groupId>

            <artifactId>spring-context-support</artifactId>

            <version>4.1.6.RELEASE</version>

</dependency>






Posted by '김용환'
,



* cglib default 생성자


spring의 한계로,

spring 3 버전 이하 버전에서는 cglib 으로 default 생성자를 생성할 수 없는 한계가 있었으나,

spring 4 버전 이상 부터는 cglib으로도 default 생성자로 생성할 수 있게 되었다. 즉, (objenesis 라이브러리의 도움을 얻은)cglib을 사용하기 위한 default 생성자를 요구하지 않는다. 




  • CGLIB-based proxy classes no longer require a default constructor. Support is provided via the objenesis library which is repackaged inline and distributed as part of the Spring Framework. With this strategy, no constructor at all is being invoked for proxy instances anymore.


http://docs.spring.io/spring/docs/current/spring-framework-reference/html/new-in-4.0.html#_core_container_improvements


http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop-api.html#aop-pfb-proxy-types



* cglib maven dependency

spring 3.2부터는 cglib dependency를 정의할 필요가 없다. (cglib 3.0)


* cglib maven exclude 

hibernate를 쓰지 않는다면, cglib을 exclude 해도 된다. 


Posted by '김용환'
,




spring 4.1 올리면서 테스트 코드 실행시 다음 에러가 발생한다.


java.lang.NoClassDefFoundError: org/junit/runners/model/MultipleFailureException 


java.lang.NoClassDefFoundError: org/junit/runners/model/MultipleFailureException

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.withAfterClasses(SpringJUnit4ClassRunner.java:188)

at org.junit.runners.ParentRunner.classBlock(ParentRunner.java:145)

at org.junit.runners.ParentRunner.run(ParentRunner.java:235)

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)

at org.junit.runners.Suite.runChild(Suite.java:128)

at org.junit.runners.Suite.runChild(Suite.java:24)

at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)

at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)

at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)

at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)

at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)

at org.junit.runners.ParentRunner.run(ParentRunner.java:236)

at org.junit.runner.JUnitCore.run(JUnitCore.java:157)

at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)

at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)

at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

Caused by: java.lang.ClassNotFoundException: org.junit.runners.model.MultipleFailureException



해결은 pom.xml에 최신버전의 4.9이상의 junit 버전을 추가하면 된다. 나는 최신버전의 4.12로 수정했더니 동작이 잘된다.


<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

</dependency>



Posted by '김용환'
,