잊지 않겠습니다.

구성된 project는 아주 기본적인 Hibernate를 이용한 ORM 구성을 가지게 된다. 

추가로, Transaction과 Connection Polling을 구성하도록 한다. 


1. Transaction의 추가


<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-tx</artifactId>

<version>3.1.2.RELEASE</version>

</dependency>


<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aop</artifactId>

<version>3.1.2.RELEASE</version>

</dependency>


spring trasaction을 위한 jar와 aop를 위한 jar를 추가한다.

생성된 PersonService의 interface를 선언한다. 

public interface IPersonService {
Person add(String firstName, String lastName, Double money);
List<Person> getAll();
Person get(Integer id);
Person edit(Integer id, String firstName, String lastName, Double money);
void delete(Integer id);
}

PersonService에 interface구현을 추가하고, @Transaction annotation을 붙인다. 
구현된 PersonService는 다음과 같다.

@Service("personService")
@Transactional
public class PersonService implements IPersonService {
@Resource
private SessionFactory sessionFactory;
@SuppressWarnings("unchecked")
public List<Person> getAll() {
Session session = sessionFactory.getCurrentSession();
List<Person> persons = (List<Person>) session.createCriteria(Person.class).list();
return persons;
}
public Person get(Integer id) {
Session session = sessionFactory.getCurrentSession();
return (Person) session.get(Person.class, id);
}
public Person add(String firstName, String lastName, Double money) {
Person person = new Person();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setMoney(money);
Session session = sessionFactory.getCurrentSession();
session.save(person);
return person;
}
public Person edit(Integer id, String firstName, String lastName, Double money) {
Person person = get(id);
Session session = sessionFactory.getCurrentSession();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setMoney(money);
session.update(person);
return person;
}
public void delete(Integer id) {
Person person = get(id);
Session session = sessionFactory.getCurrentSession();
session.delete(person);
}
}

위 코드와 기존코드의 가장 큰 차이는 sessionFactory.getCurrentSession()이다. 기존 코드는 sessionFactory.openSession()을 사용하지만, transaction을 사용하는 Service는 sessionFactory.getCurrentSession()이 된다. 그 이유는 다음과 같다. 

Transaction annotation을 사용하는 경우에 annotation에 의해서 다음과 같은 코드로 변경이 되어서 실행이 되는것과 동일해진다. 


public Person edit(Integer id, String firstName, String lastName, Double money) {

Session s = sessionFactory.openSession();
Transaction transaction = s.beginTransaction();

Person person = get(id);
Session session = sessionFactory.getCurrentSession();
person.setFirstName(firstName);
person.setLastName(lastName);
person.setMoney(money);
session.update(person);

transaction.commit();
return person;
}

Transaction AOP에 의해서 항시 openSession, beginTransaction 이 실행되는것과 동일하게 구성이 되게 된다. 그리고, method의 종료시에 transaction의 commit가 자동으로 이루어지기 때문에 Transaction이 구현되는 서비스에서는 sessionFactory에서 getCurrentSession을 통해서 transaction이 적용된 session을 얻어내야지된다.

마지막으로 spring bean annotation설정을 위해서 spring-context.xml에 다음과 같은 내용을 추가하도록 한다.
<!-- sessionFactory에 transaction 구성 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager" 
                         p:sessionFactory-ref="sessionFactory" />
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- sessionFactory 구성 -->

테스트를 구동하고, 정상적으로 동작하는지 확인한다. 



2. Connection Pool의 추가


hibernate에서 주로 사용하는 c3p0를 connection pool로 사용하도록 한다. c3p0는 사용하기 쉽고, 가벼워서 WAS에서 제공하는 connection pool을 사용하지 않는 경우에 주로 추천되는 connection pool이다. 


c3p0 dependency를 pom.xml에 다음과 같이 추가한다.


<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-c3p0</artifactId>

<version>4.1.7.Final</version>

</dependency>



spring-context에서 기존 dataSource를 제거하고 c3p0에서 제공하는 Pooling DataSource로 대체한다. 

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="${app.jdbc.driverClassName}"
p:jdbcUrl="${app.jdbc.url}"
p:user="${app.jdbc.username}"
p:password="${app.jdbc.password}"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="10"
p:maxStatements="50"
p:minPoolSize="5"/>


테스트를 구동하고, 정상적으로 테스트를 통과하면 완료된다.


일단, 기본 WEB Application을 개발할 수 있는 환경을 만들었지만, 하나 빼먹은 것이 계속해서보인다.; 

그것은 바로... Log!!! 


3. Log의 추가


요즘 java의 추세인 logback와 slf4j를 사용하도록 한다.; 일단 log4j보다 속도가 좋고, 거기에 log4j와 동일한 방법으로 사용이 가능하다는 장점때문에 logback를 slf4j를 이용해서 facade pattern으로 사용하게 된다. 


pom.xml에 다음을 추가한다. 

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

<version>${slf4j.version}</version>

</dependency>

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-classic</artifactId>

<version>${logback.version}</version>

</dependency>


<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-core</artifactId>

<version>${logback.version}</version>

</dependency>


그리고,  resource에 logback.xml 파일을 추가하도록 한다. logback.xml은 기본적으로 logback이 사용될때, 기본적으로 load 되는 설정 xml이다. 주로 사용하는 RollingFileAppender를 이용해서 구성하도록 한다. 


logback의 Appender의 종류와 사용법들은 다음 site에서 참고하도록 한다.

http://logback.qos.ch/manual/appenders.html



<?xml version="1.0" encoding="UTF-8"?>

<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

<encoder>

<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n

</pattern>

</encoder>

</appender>

<appender name="FILEOUT" class="ch.qos.logback.core.rolling.RollingFileAppender">

<file>c:\\Logs\\BackLog\\logFile.log</file>

<maxHistory>30</maxHistory>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<fileNamePattern>c:\\Logs\\BackLog\\logFile.%d{yyyy-MM-dd}.log</fileNamePattern>

<maxHistory>30</maxHistory>

</rollingPolicy>

<encoder>

<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n

</pattern>

</encoder>

</appender>

<root level="INFO">

<appender-ref ref="STDOUT" />

<appender-ref ref="FILEOUT" />

</root>

</configuration>



이제 test를 실행하면 Console과 LogFile이 정상적으로 만들어지는 것이 확인된다. 이로서 개발전 기본 설정은 모두 마쳐지게 된다. 



Posted by Y2K
,

wikibook에 spring과 hibernate를 연결하는 작업을 진행한다.


1. maven dependency 설정

최종적으로는 spring web mvc를 사용할 예정이기 때문에, wikibook project에 다음과 같은 dependency를 추가한다. 


pom.xml

<!-- spring test jar -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>3.1.2.RELEASE</version>

<scope>test</scope>

</dependency>

<!-- spring web mvc jar -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>3.1.2.RELEASE</version>

</dependency>

<!-- spring orm jar -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-orm</artifactId>

<version>3.1.2.RELEASE</version>

</dependency>

<!-- mysql connector -->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.21</version>

</dependency>

<!-- hibernate jar -->

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-core</artifactId>

<version>4.1.7.Final</version>

</dependency>

추가 후, maven eclipse:eclipse goal을 실행해서, eclipse project로 재등록시키고, eclipse에서 refresh를 하면 모든 jar 파일이 올라오는 것을 알 수 있다. 


2. entity class 생성


테스트로 Person이라는 객체를 생성한다. 객체 코드는 다음과 같다.

@Entity

@Table(name="PERSON")

public class Person {

@Id

@GeneratedValue

@Column(name="ID")

private Integer id;

@Column(name="FIRST_NAME")

private String firstName;

@Column(name="LAST_NAME")

private String lastName;

@Column(name="MONEY")

private Double money;

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;  

}

public String getFirstName() {

return firstName;

}

public void setFirstName(String firstName) {

this.firstName = firstName;

}

public String getLastName() {

return lastName;

}

public void setLastName(String lastName) {

this.lastName = lastName;

}

public Double getMoney() {

return money;

}

public void setMoney(Double money) {

this.money = money;

}

}



3. PersonService를 구성한다. 

기본적인 CRUD만을 가진 Service로 구성한다.


@Service("personService")

public class PersonService implements IPersonService {

@Resource

private SessionFactory sessionFactory;

@SuppressWarnings("unchecked")

public List<Person> getAll() {

Session session = sessionFactory.openSession();

List<Person> persons = (List<Person>) session.createCriteria(Person.class).list();

return persons;

}

public Person get(Integer id) {

Session session = sessionFactory.openSession();

return (Person) session.get(Person.class, id);

}

public Person add(String firstName, String lastName, Double money) {

Person person = new Person();

person.setFirstName(firstName);

person.setLastName(lastName);

person.setMoney(money);

Session session = sessionFactory.openSession();

session.save(person);

return person;

}

public Person edit(Integer id, String firstName, String lastName, Double money) {

Person person = get(id);

Session session = sessionFactory.openSession();

person.setFirstName(firstName);

person.setLastName(lastName);

person.setMoney(money);

session.update(person);

session.flush();

return person;

}

public void delete(Integer id) {

Person person = get(id);

Session session = sessionFactory.openSession();

session.delete(person);

session.flush();

}

}


4. test code를 작성한다.

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="/spring-context.xml")

public class PersonServiceTest {


@Autowired

private IPersonService personService;

@Test

public void addPersons() {

for(Integer i = 0 ; i < 10 ; i++) {

String firstName = "FIRST_NAME " + i.toString();

String lastName = "LAST_NAME " + i.toString();

Double money = i.doubleValue();

Person addedPerson = personService.add(firstName, lastName, money);

assertThat(addedPerson.getId(), not(0));

}

}

@Test

public void getPerson() {

addPersons();

Person person = personService.getAll().get(0);

Integer id = person.getId();

assertThat(id, not(0));

Person newPerson = personService.get(id);

assertThat(person.getId(), is(newPerson.getId()));

}

@Test

public void editPerson() {

addPersons();

Person person = personService.getAll().get(0);

Integer id = person.getId();

assertThat(id, not(0));

String editedFirstName = person.getFirstName() + "__edited";

personService.edit(id, editedFirstName, person.getLastName(), person.getMoney());

Person editedPerson = personService.get(id);

assertThat(editedPerson.getFirstName(), is(editedFirstName));

}

@Test

public void deletePerson() {

addPersons();

Person person = personService.getAll().get(0);

Integer id = person.getId();

assertThat(id, not(0));

personService.delete(id);

assertNull(personService.get(id));

}

}


5. test code를 작성하면 maven 설정에 따라, src/test/resources 에 다음 파일들을 위치시킨다. 


spring-context.xml : spring bean 설정

spring.properties : spring bean에 설정된 properties 파일

hibernate.cfg.xml : hibernate dialect 등 option 정보


spring-context.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:aop="http://www.springframework.org/schema/aop"

xmlns:mvc="http://www.springframework.org/schema/mvc"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:p="http://www.springframework.org/schema/p"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd

http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<!-- person Service bean 정의 -->

<bean id="personService" class="com.xyzlast.services.PersonService"/>

<!-- property file load -->

<context:property-placeholder location="spring.properties" />

<!-- sessionFactory 구성 -->

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"

p:dataSource-ref="dataSource"

p:configLocation="${hibernate.config}"

p:packagesToScan="com.xyzlast.entities"/>

<!-- MySql dataSource 구성 -->

<bean id="dataSource"

class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 

<property name="driverClassName" value="${app.jdbc.driverClassName}" />

<property name="url" value="${app.jdbc.url}" />

<property name="username" value="${app.jdbc.username}" />

<property name="password" value="${app.jdbc.password}" />

</bean>

</beans>


spring.properties

# database properties

app.jdbc.driverClassName=com.mysql.jdbc.Driver

app.jdbc.url=jdbc:mysql://localhost:3306/test

app.jdbc.username=root

app.jdbc.password=qwer12#$

 

#hibernate properties

hibernate.config=hibernate.cfg.xml



hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create</property>

<property name="hibernate.connection.release_mode">after_transaction</property>

</session-factory>

</hibernate-configuration>


6. 테스트를 실행한다. 

eclipse JUnit plugin을 이용하건, maven command를 이용하던, 모든 방법에서 test 가 ok가 나와야지 된다.






다음은 Controller 구성과 REST 서비스를 구현해보도록 한다. 

Posted by Y2K
,

maven에서 제공하는 maven-archetype-webapp 의 경우, 최신 eclipse인 JUNO와 tomcat7에서 동작하지 않는 문제가 있다. 

기본적으로, java 버젼을 1.4로, jst.web 버젼을 2.4로 잡아버리기 때문에 발생하는 문제인데, 이를 해결하기 위해서는 m2eclipse와 같은 eclipse plug-in을 이용하는 방법이 주로 추천되고 있다. 


maven을 오래 사용한 것은 아니지만, 일단 eclipse plugin을 사용하는 것보다는 console을 이용하는 것이 좀 더 나아보인다. test, deploy 등의 작업을 좀 더 편하게 할 수 있고, m2eclipse를 사용하더라도 손으로 하나하나 다시 설정을 잡아줘야지 되는 불편함이 존재하기 때문에, pom 자체를 이용해서 이를 수정해주는 것이 더 나아보이기 때문이다. 


단계는 다음과 같다. (자바세상의 빌드를 이끄는 메이븐 을 보면서 공부를 해서... 예시는 책에 나온 wikibook을 사용한다.)


1. web app의 skeleton code를 다음 maven command를 이용해서 작성한다.


mvn archetype:generate -DgroupId=com.xyzlast -DartifactId=wikibook -DarchetypeArtifactId=maven-archetype-webapp


작성된 project의 pom 파일은 다음과 같다.


<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/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.xyzlast</groupId>

  <artifactId>wikibook</artifactId>

  <packaging>war</packaging>

  <version>1.0-SNAPSHOT</version>

  <name>wikibook Maven Webapp</name>

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

  <dependencies>

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <version>3.8.1</version>

      <scope>test</scope>

    </dependency>

  </dependencies>

  <build>

    <finalName>wikibook</finalName>

  </build>

</project>


2. 먼저, 사용하는 java compiler의 버젼은 1.7을 사용하고 있기 때문에 compiler의 버젼을 넣어준다.

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>

이 상태에서 mvn eclipse:eclipse를 이용해서 eclipse용 설정을 생성하게 되면, 다음과 같은 문제가 발생하게 된다.
* M2_REPO/junit/junit/4.11/junit-4.11.jar will not be exported or published. Runtime ClassNotFoundExceptions may result. 
* Project Facets가 정상적으로 맞지 않는 문제(최신 자바 1.7만 설정이 된 경우)

3. 사용되는 jar 파일들을 WEB-INF/libs 에 위치하게 하기 위해서 다음과 같은 설정을 추가한다. 또한, javadoc과 source를 보기 위한 설정 역시 추가한다.

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
<projectNameTemplate>[artifactId]</projectNameTemplate>
<wtpapplicationxml>true</wtpapplicationxml>
<wtpversion>2.0</wtpversion>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
<useProjectReferences>true</useProjectReferences>
<workspace>..</workspace>
</configuration>
</plugin>


4. 이 상태에서 mvn eclipse:eclipse를 실행하면, .setting/org.eclipse.wst.common.project.facet.core.xml 파일의 내용은 다음과 같다.

<faceted-project>
  <fixed facet="jst.java"/>
  <fixed facet="jst.web"/>
  <installed facet="jst.web" version="2.4"/>
  <installed facet="jst.java" version="1.4"/>
</faceted-project>

여기서 eclipse로 import하기 전에 text 파일을 고쳐주면 문제가 해결되긴 하지만, 자동화를 위해서 다음과 같은 파일을 작성한다.

java_1.7_web_3.0.xml
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
     <fixed facet="jst.web"/>
     <fixed facet="java"/>
     <fixed facet="wst.jsdt.web"/>
     <installed facet="java" version="1.7"/>
     <installed facet="jst.web" version="3.0"/>
     <installed facet="jst.jsf" version="2.0"/>
     <installed facet="wst.jsdt.web" version="1.0"/>
</faceted-project>

또한, 생성된 skeleton project의 web.xml은 web app version 2.5의 것을 사용하고 있기 때문에 이것을 3.0으로 넣어줄 기본 web.xml 파일을 작성한다.

web_3.0_default.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                             http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
         id="WebApp_ID" version="3.0">

<display-name>wikibook</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

작성된 2개의 파일을 MAVEN_HOME의 app-conf Directory에 카피한다.

5. maven-eclipse-plugin 정보에 다음과 같은 추가 설정을 한다.

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
<projectNameTemplate>[artifactId]</projectNameTemplate>
<wtpapplicationxml>true</wtpapplicationxml>
<wtpversion>2.0</wtpversion>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
<useProjectReferences>true</useProjectReferences>
<workspace>..</workspace>
<additionalConfig>
<file>
<name>.settings/org.eclipse.wst.common.project.facet.core.xml</name>
<location>file:///${MAVEN_HOME}/app-conf/java_1.7_web_3.0.xml</location>
</file>
<file>
<name>src/main/webapp/WEB-INF/web.xml</name>
<location>file:///${MAVEN_HOME}/app-conf/web_3.0_default.xml</location>
</file>
</additionalConfig>
</configuration>
</plugin>

추가 설정의 내용은 단순하다. MAVEN_HOME에 위치한 facet 파일과 web.xml을 이용해서 eclipse plugin에 의해 생성되는 파일을 대치해버리는 것이다. 

최종적인 pom.xml파일 내용은 다음과 같다. (JUnit 4를 사용하기 위해서, JUnit버젼 역시 3.8.1에서 4.11로 변경하였다.)

<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xyzlast</groupId>
<artifactId>wikibook</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>wikibook Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>servlet-api</artifactId>
<version>6.0.35</version>
</dependency>
</dependencies>
<build>
<finalName>wikibook</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
<projectNameTemplate>[artifactId]</projectNameTemplate>
<wtpapplicationxml>true</wtpapplicationxml>
<wtpversion>2.0</wtpversion>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
<useProjectReferences>true</useProjectReferences>
<workspace>..</workspace>
<additionalConfig>
<file>
<name>.settings/org.eclipse.wst.common.project.facet.core.xml</name>
<location>file:///${MAVEN_HOME}/app-conf/java_1.7_web_3.0.xml</location>
</file>
<file>
<name>src/main/webapp/WEB-INF/web.xml</name>
<location>file:///${MAVEN_HOME}/app-conf/web_3.0_default.xml</location>
</file>
</additionalConfig>
</configuration>
</plugin>
</plugins>
</build>
</project>


6. 이제 console에서 mvn eclipse:eclipse를 실행한다. 
실행후, eclipse에서 Import를 이용해서 project를 load 한다. 결과는 다음과 같다.



test에 사용되는 junit부분 warning이고, 이 것은 무시 가능하다. delete로 지워버려도 된다.


이제 Tomcat을 구성하고, Tomcat에서 project를 추가하면 다음과 같은 페이지를 보는 것이 가능하다. 



Posted by Y2K
,

InvocationHandler

Java 2012. 9. 12. 00:00

JAVA와 .NET의 차이점을 볼때, 가장 특이한 점이라고 보이는 Interface.


Dynamic Proxy의 핵심 Interface이고, Proxy.newProxyInstance method를 통해서 기본적인 Dynamic Proxy 객체를 생성해주는 것이 가능하다.

코드는 다음과 같다.


Hello proxyHello = (Hello) Proxy.newProxyInstance(

                             getClass().getClassLoader(),

                             new Class[] { Hello.class },

                             new InvocationHandler(new HelloTarget()));


재미있어보이는 코드이고, 일단 Target의 Interface를 모두 상속받은 Proxy객체를 만들어준다는 것이 엄청나다는 느낌이 든다. 


물론. 이 코드를 바로 사용하는 일은 거의 존재하지를 않는다. 

Spring에서 제공하는 ProxyFactory를 이용해서, Dynamic Proxy를 구성하는 것이 일반적이기 때문이다. 

Posted by Y2K
,

1. S/W 개발에서 절대로 바꾸지 않는 것은 없기 때문에.

Class 대신이 Interface를 사용하고, New 대신에 DI를 이용하는 작업에 대한 비용보다 후에 바뀌게 되어서 지불하게 되는 비용이 더욱더 크다.


2. Class의 구현방식은 바뀌지 않는다고해도, Interface를 이용하면 다른 차원의 서비스가 구현 가능하다

method의 호출전, 후에 다른 일을 한다던지, 아니면 count를 늘려주는 사소한 일을 하더라도, 기존 class의 변경 없이 개발이 가능하게 할 수 있다.


3. TEST가 용이하다

단지 효율적인 테스트를 하기 위해서라도 DI를 적용해야지 된다.

Posted by Y2K
,

Framework는 분명한 제어의 역전기능이 적용되어 있어야지 된다.

Application code는 Framework가 짜놓은 틀에서 수동적으로 동작할 수 있어야지 된다.


라이브러리를 사용하는 Application 코드는 Application의 흐름을 직접 제어한다.

단지 동작하는 도중에 필요한 기능이 있을 때, 능동적으로 Library를 사용한다. 


반면에 프레임워크는 거꾸로 애플리케이션 코드가 프레임워크에 의해서 사용된다. 

보통 프레임워크 위에 개발된 클래스를 등록해두고, 프레임워크가 흐름을 주도하는 중에 개발자가 만든 애플리케이션 코드를 사용하도록 만드는 방식이다. 

Posted by Y2K
,

1. 속도가 빨라졌다. 

전에 VS2010이 뭐같은 녀석이였는지, VS2010에서 시작되는 화면을 보다보면 속이 터져버릴것만 같았다. 

VS2012의 경우에는 속도 자체가 빨라진것보다 Solution을 Lazy Loading을 시켜서 좀 더 빨라보이는 트릭을 사용한다. 

물론 트릭뿐 아니라, 실질적인 속도 자체도 더 빨라졌다. UI의 반응 속도라던지, compile 시의 속도가 향상된 것이 눈에 보인다. 

(물론 측정한것은 아니다. 체감으로.. )


2. 코드의 가독성이 높아졌다.

VS2010의 화려한 인터페이스에서 VS2012의 조금은 투박한 인터페이스를 보면 이건 퇴보가 아닌가. 하는 생각이 들지만, 이 상태로 3~4 시간 코딩을 해보면 확실히 느낌이 다르다. 전에 보이던 인터페이스의 화려한 색이 은근히 가독성을 떨어트리고 있었던 것이 아닌가 하는 생각이 들 정도로 UI 자체에서의 코드에 대한 집중도를 높여준다. 


3. Solution Explorer가 향상되었다.

기존에는 Pending Explorer, Solution Explorer 등의 여러 파일 탐색기에서 각각의 다른 기능을 수행하고 있다. 그런데 거의 모든 기능이 Solution Explorer에 통합이 되었다. 간단한 Toggle 동작만으로 SVN에 올려야지 되는 파일리스트 및 변경사항을 알아내는데 도움을 준다. 

또한, Solution Explorer에서 Class의 method tree를 열수 있는 기능이 추가 되어서, 파일을 하나하나 열지 않고 객체의 method를 직접 찾아가는데 도움을 준다. 


4. Console Application과 Class Library의 default platform이 "Any CPU"로 변경되었다.

개인적으로는 이 점 하나만으로도 VS2010을 버리고, VS2012로 갈아타야지 된다고 생각한다. VS2010은 default platform이 "x86"이기 때문에 개발후 배포작업에 있어서 Reference Linking 작업을 실수를 하거나, x64 system에서 개발하는 개발자와 x86 system에서 개발하는 개발자간의 suo 파일이 달라져서 전체적인 시스템이 달라진것과 다름없는 형태를 보이고 있었다. 특히 svn이나 git에 올리지 않는 suo 파일에 설정된 platform 정보가 딸려다니기 때문에 타개발자와의 소스 호환에도 어려움이 있고, 이 부분이 꼬이면 한 30여개가 넘는 project들 중에서 어떤 msil이 32bit로 따로 설정된것인지 찾는 귀찮고 초보 개발자들은 매우 당황하는 일들을 원천적으로 막을 수 있다. (무조건 "Any CPU"로 개발하는거다!)


5. solution 파일과 proj 파일이 VS2010과 공유된다.

이건 MS가 VS2010 SP1에서부터 준비한 결과인것 같은데, solution과 proj 파일이 VS2010과 VS2012간에는 차이가 없게 열리거나, VS2012에서 migration된 proj 파일이 VS2010에서 아무런 문제 없이 사용이 가능하다. 단 NuGet 1.X 를 이용한 package가 있는 경우에는 VS2012에서 에러가 발생한다. NuGet을 이용하지 않은 project라면 당장 VS2012로 사용해도 무방하다. 


간만에 MS가 Tool을 잘만들어서 내보낸것 같다. 오늘 하루종일 VS2012와 Windows 8으로 삽질을 하고 있었는데... Windows 8에 대해서는 좀 더 사용해보고 글을 하나 적어볼만 한 내용들이 좀 있는것 같고. 


PS. MSSQL이 분리가 되었다. 이건 굉장히 바람직한것이 아닐까 싶다. 몇몇 개발자들은 자신의 컴에 MSSQL Express가 설치가 자동으로 되어있는지도 모르는 사람들이 꽤 있기도 했고. 


PS2. MSSQL2012도 매우 향상된 느낌이 든다. 속도 뿐 아니라, 시스템에서의 안정성 자체도 좀 나아진것 같아서 좀더 가지고 놀면서 테스트를 해봐야지 될 것 같다.

Posted by Y2K
,

NoSQL Solution 검토중, Cassandra 설치 부분에 대해서 번역 및 참고 사항 추가


  • 설정환경 : JAVA 1.6 이상

설치 Progress

  • Download Cassandra application
  • conf/cassandra.yaml 파일 수정
    항목설명Example
    data_file_directoriesDB 파일이 저장될 위치 지정C:\cassandra\data
    commitlog_directoryDB Commit이 발생될 때, 메세지가 저장되는 위치 지정C:\cassandra\commitLogs
    saved_caches_directoryCache의 저장위치C:\cassandra\cache
    storage_portCluster 구성시에 저장 메체들간의 통신 Portdefault:7000
    rpc_portRemote Client Port. Client의 접속 정보를 저장하는 Portdefault:9160
    initial_tokenCluster를 구성시, 첫번째노드로 사용되는 경우에만 0를 설정한다. 다른 경우에는 모두 빈값으로 표시default:(공백)
  • conf/log4j.properties 파일 수정
    항목설명Example
    log4j.appender.R.Filelog4j의 파일 저장 위치/var/log/cassandra/system.log
  • cassandra-env.sh 수정
    항목설명Example
    MAX_HEAP_SIZEcassandra에서 사용할 heap의 사이즈 설정default : 4G, 절대로 작은 값으로 production에 보내지 말것!
    HEAP_NEWSIZEcassandra에서 새로운 heap 요청이 발생하였을 때에, 생성될 sizedefault : 800M, 절대로 작은 값으로 production에 보내지 말것!

위 값들이 너무 작은 경우, OutOfMemory exception이 발생할 가능성이 매우 높음


시작 progress

bin/cassandra.bat -f -- foreground로 실행, CTRL+C로 중지 가능
bin/cassandra.bat    -- background로 실행, Process kill로만 중지 가능

Posted by Y2K
,

Redmine을 Backup하기 위해서는 file 폴더와 mysql db backup이 필요하다. 

일별 backup 을 관리하기 위해서 간단한 powershell script를 작성했다.


#utf8 포멧으로 windows command 변경

& CHCP 65001


$backupRoot = 'H:\redmine_backup\';

$mysqldump = 'C:\Program Files\MySQL\MySQL Server 5.5\bin\mysqldump.exe'

$fileFolder = 'G:\Redmine\Files\*.*';


$date = (Get-Date).ToString("yyyyMMdd");

$desFolder = $backupRoot + $date;

$attachedFiles = $desFolder + '\files';

$dbBackup = $desFolder + '\redmine.sql';


mkdir $desFolder;

mkdir $attachedFiles;

cp $fileFolder $attachedFiles;

& $mysqldump -u root -pqwer12#$ redmine --default-character=utf8 | Out-File $dbBackup -Encoding UTF8;

# 기본 설정. MySQL/bin 의 위치와 Backup 위치, Redmine의 Attach된 파일 위치를 지정

$backupRoot = 'G:\TracBackup\';

$mysqldump = 'C:\Program Files\MySQL\MySQL Server 5.5\bin\mysqldump.exe'

$fileFolder = 'G:\Redmine\Files\*.*';


#Daily Backup을 날짜별로 하기 위해서 Get-Date 함수 이용

$date = (Get-Date).ToString("yyyyMMdd");

$desFolder = $backupRoot + $date;

$attachedFiles = $desFolder + '\files';

$dbBackup = $desFolder + '\redmine.sql';


#Folder 생성 및 Attach된 파일 copy

mkdir $desFolder;

mkdir $attachedFiles;

cp $fileFolder $attachedFiles;


#MySQL dump 명령어 실행

& $mysqldump -u root -p[MySQL Password] redmine --default-character=utf8 | Out-File $dbBackup -Encoding UTF8;



작성된 script를 Windows Scheduler에 등록하고, 매일 실행하도록 설정하면 완료.

Posted by Y2K
,

출처 : http://www.gpparty.com/viewtopic.php?f=21&t=102&start=0


기초를 중요하게 여겨라
- 딴건 몰라도 기초 지식은 달달 외워라. 대뇌가 아니라 소뇌와 연수에 새겨넣어라.
- 기초부터 탄탄하지 않은 사람은 하드코어 기술을 다룰 자격이 없다. 건방떨다가 툭하면 사고치고 수습 못한다.

다양한 알고리즘을 머리속에 담아라.
- 자동차 운전은 2년 넘으면 그후부터 그게 그거다. 하지만 레이싱은 2년차와 10년차가 크게 차이난다.
- 코딩도 2년 넘으면 그후부터 그게 그거다. 하지만 알고리즘 구사 능력은 2년차와 10년차가 크게 차이난다.
- 현대 모든 기술은 수십년전에 나온 알고리즘들의 실현에 지나지 않는다.
- 다양한 알고리즘을 머리에 담고 있으면 새로운 기술이 나와도 쉽게 적응한다.
- 최신 알고리즘만 공부하지 말고, 퇴역해버린 알고리즘과 그것이 어떠한 새로운 알고리즘으로 교체됐는지도 놓치지 말고 공부하라. 

내 경력을 믿어라
- 경력 많은 프로그래머는 말로 표현하기 어려운 각종 노하우가 근육에 담겨있다.
- (경력상) 젊은 사람이 신기술 배워 오더라도 두려워말라. 경력 많은 프로그래머는 젊은 사람의 신기술 정도는 쉽게 자기것으로 만든다. 그리고 깊은 원론적 혁신을 개발하고 응용해낸다. 이런것은 젊은 사람이 절대 할 수 없다.

코딩의 기초부터 탄탄히 다져라
- 잘 지은 이름 하나 열줄 주석 안부럽다
- 이름과 실제 하는 역할이 다르면 능지처참
- 함수,변수,클래스 이름도 제대로 짓는 습관도 안되어 있으면, 디자인 패턴이네, 리팩토링이네, 객체지향설계를 공부할 자격이 없다. 
- 의외로 이거 잘 못하는 사람들 정말 많으니 주의하라.

툴을 잘 활용하라
- Visual Assist, 각종 측정 도구, 각종 개발툴을 잘 활용하라.
- "나는 그런 툴에 별로 의지 안해도 잘 해요" => 달리 말하면 그런 툴을 제대로 쓰는 법을 모른다는 소리와 같다.
- 툴 잘 활용해서 개발 속도 15% 올려놓고 집에 일찍 가라. 
- 툴 값보다 당신 인건비가 더 비싸다.

알아보기 쉽게 코딩해라
- 소스 한줄 줄인답시고 알아보기 어렵게 하지 마라. 개발은 팀웍이다. 자기혼자 써커스 하지 마라.
- 괜히 재주 부리지 말고 알기 쉽게 짜라.
- 알아보기 어려운 소스는 누구나 쉽게 짠다. 알아보기 쉬운 소스를 짜는 것은 자기 생각 정리가 뛰어남을 의미한다.

- 내가 남의 어떤 소스를 잘 이해했다면 그것은 내 실력이 뛰어나서가 아니라 그 소스를 짠 사람이 뛰어나서이다.

신중하게 생각하고 코딩하라
- 로직을 상세히 구상하고 천천히 꼼꼼하게 소스를 타이핑하라
- 코딩 빠른 프로그래머는 코딩하는 속도보다 디버깅하는 횟수를 최소화한다.
- 덤벙대는 프로그래머는 초반 코딩을 빨리 하지만 디버깅하다가 한달 야근을 못 피한다. 그 사이에 신중한 프로그래머는 같은 목적의 프로그램을 천천히 짠 후, 2주일만에 디버깅을 다 마치고 휴가를 간다.
- 마지막 작은 버그까지 디버깅해 없애는 기간이 총 기간의 90%를 차지한다. 최소한의 디버깅을 하라.
- 자기가 짠 코드는 함수 머리부터 하나 하나 체크해서 잘 돌아가게 만들어졌는지 중복 체크하라.

자기 관리를 잘 하라
- 최상의 컨디션으로 정시에 출근하라.
- 그리고 최악의 컨디션으로 정시에 퇴근하라. 
- 하루 7~8시간 수면은 반드시 챙겨라. 반드시 아침을 먹어라.
- 최상의 컨디션을 유지하지 못하면 야근을 해도 마땅하다.



다른 것보다 "자기 관리를 잘하라", "코딩의 기초부터 탄탄히 다져라" 는 정말 고민해야지 될 문제이다. 이 부분에 대해서는 끊임없이 자신을 돌아보는 자기 성찰적 자세가 없으면 하기 힘든 점이니까.


Posted by Y2K
,