잊지 않겠습니다.

gradle이 major update를 할 때마다 querydsl Q-class들의 생성방법이 계속해서 바뀌고 있습니다. 이제는 좀 더 자연스러운 방법으로 처리가 되네요. 더 이상 추가 task를 만들 필요없이 지원이 가능합니다.

변경된 build.gradle입니다. lombok querydsl을 모두 적용한 상태입니다.

dependencies {
    annotationProcessor(
            "com.querydsl:querydsl-apt:${rootProject.ext.querydslVersion}:jpa",
            "org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final",
            "javax.annotation:javax.annotation-api:1.3.2",
            "org.projectlombok:lombok"
    )
}

sourceSets {
    main.java.srcDirs += [ generated ]
}

tasks.withType(JavaCompile) {
    options.annotationProcessorGeneratedSourcesDirectory = file(generated)
}

clean.doLast {
    file(generated).deleteDir()
}

간단한 설명입니다. annotationProcessor는 compile시의 annotationProcessor를 gradle에서 자동으로 추가해줍니다. 이때 사용되는 각각의 jar를 추가해주면 됩니다. 기존의 generateQueryDsl을 전혀 할 필요가 없습니다. 다음 JavaCompile환경에 생성될 Q-class들의 위치를 지정해주고 그 위치를 sourceSets에 추가합니다. 마지막으로 clean으로 생성된 Q-class들을 제거해주는 코드를 넣어줍니다.

gradle에서 annotationProcessor를 지원함으로서 더욱 편하게 QueryDsl을 지원할 수 있게 되었습니다.

Posted by Y2K
,

queryDSL이 4.x대로 변경이 되면서 package명에 큰 변화가 생겼습니다. 기존 com.mysema.querydsl 에서 com.querydsl로 package명이 바뀌어서 간략화 되었지요.

기존 querydsl 처리 부분에 대한 build.gradle을 변경한다면 다음과 같습니다.


dependencies {
compile("org.springframework.boot:spring-boot-starter-web") {
exclude module: "spring-boot-starter-tomcat"
}
compile 'com.graphql-java:graphql-java:2.1.0'
compile("org.springframework.boot:spring-boot-starter-jetty")
compile("org.springframework.boot:spring-boot-starter-actuator")
testCompile("junit:junit")
testCompile("org.springframework.boot:spring-boot-starter-test")
compile 'com.querydsl:querydsl-apt:4.1.4'
compile 'com.querydsl:querydsl-jpa:4.1.4'
compile 'org.springframework.data:spring-data-jpa:1.10.4.RELEASE'
compile group: 'org.hibernate', name: 'hibernate-core', version: '5.2.3.Final'
compile "org.hibernate:hibernate-entitymanager:5.2.3.Final"
compile 'com.h2database:h2:1.4.187'
compile group: 'org.aspectj', name: 'aspectjrt', version: '1.8.9'
compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.9'
compileOnly "org.projectlombok:lombok:1.16.12"
}


sourceSets {
main {
java {
srcDirs 'src/main/java', 'src/main/generated'
}
}
}

task generateQueryDSL(type: JavaCompile, group: 'build', description: 'Generates the QueryDSL query types') {
file(new File(projectDir, "/src/main/generated")).deleteDir()
file(new File(projectDir, "/src/main/generated")).mkdirs()
source = sourceSets.main.java
classpath = configurations.compile + configurations.compileOnly
options.compilerArgs = [
"-proc:only",
"-processor", "com.querydsl.apt.jpa.JPAAnnotationProcessor"
]
destinationDir = file('src/main/generated')
}

compileJava {
dependsOn generateQueryDSL
}

clean.doLast {
file(new File(projectDir, "/src/main/generated")).deleteDir()
}


기존의 generateQueryDSL task를 유지할 필요 없이, java compile option으로 QClass 생성을 넣어주는것으로 script를 간략화시키는것이 가능합니다. 또한 Q class가 존재할 때, 기존 Q Class를 지워주지 않으면 compile 시에 에러를 발생하게 되기 때문에 지워주는 작업역시 doFirst에 같이 실행하고 있습니다.

기존의 build.gradle보다 간략해보여서 전 개인적으로 맘에 드네요.

Posted by Y2K
,
지금 저는 Gradle을 이용한 web application 배포시에 FTP를 사용하고 있습니다.

그런데, 이 부분에 조금 문제가 있는 것이… 기존 gradle의 ant ftp module의 경우 가끔씩 hang이 걸려서 FTP upload를 실패하는 경우가 왕왕 보입니다. 그리고 FTP upload 시에 아무런 로그가 표시되지 않고, hang이 걸려버려서 정상적으로 upload가 되고 있는지에 대한 확인이 불가능했습니다.

그래서, FTPClient를 이용해서 File upload를 한번 만들어봤습니다.

기본적으로 commons-net의 FTPClient를 이용해서 처리합니다. 먼저 buildscript에 common-net을 등록시킵니다.

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'commons-net:commons-net:3.3'
        classpath 'commons-io:commons-io:2.4'
    }
}

다음은 FTPClient를 이용한 upload code입니다. groovy 스러운 문법은 전 아직 잘 안되더군요.;;


import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStream
import java.io.PrintWriter
import org.apache.commons.net.PrintCommandListener
import org.apache.commons.net.ftp.FTP
import org.apache.commons.net.ftp.FTPClient
import org.apache.commons.net.ftp.FTPReply
import org.apache.commons.io.IOUtils


void ftpUpload(ftpUrl, ftpUsername, ftpPassword, targetPath) {
    def ftp = new FTPClient()
    ftp.connect(ftpUrl)
    int reply = ftp.getReplyCode()
    if(!FTPReply.isPositiveCompletion(reply)) {
        ftp.disconnect()
        throw new Exception("Exception in connecting to FTP Server")
    }
    ftp.login(ftpUsername, ftpPassword)
    ftp.setFileType(FTP.BINARY_FILE_TYPE)
    ftp.changeWorkingDirectory(targetPath)
    for(File f : file(getDistPath()).listFiles()) {
        upload(f, ftp)
    }
    ftp.disconnect()
}

void upload(File src, FTPClient ftp) {
    if (src.isDirectory()) {
        ftp.makeDirectory(src.getName())
        ftp.changeWorkingDirectory(src.getName())
        for (File file : src.listFiles()) {
            upload(file, ftp);
        }
        ftp.changeToParentDirectory();
    }
    else {
        InputStream srcStream = null;
        try {
            def uploadCompleted = false
            while(!uploadCompleted) {
                srcStream = src.toURI().toURL().openStream()
                println 'upload : ' + src.getName()
                uploadCompleted = ftp.storeFile(src.getName(), srcStream)
                if(!uploadCompleted) {
                    println 'upload failed : retry this file ' + src.getName()
                    IOUtils.closeQuietly(uploadCompleted)
                }
            }
        } catch(Exception ex) {
            println ex.getMessage()
        }
        finally {
            IOUtils.closeQuietly(srcStream);
        }
    }
}

java 코드 같은 느낌입니다. groovy를 사용하고 있으면 좀 더 groovy 스러운 코드여야지 될 것 같은데요. ^^;;


Posted by Y2K
,

Gradle + yo deploy script

gradle을 이용한 build script 만들기 재미가 붙어, 이번에는 yo로 만든 web application을 auto-deploy를 지원하는 script를 한번 작성해봤습니다.

지원하는 시나리오는 다음과 같습니다.

  1. 이번에 작성한 2개의 public web과 mobile web을 모두 사용 가능
  2. 다음과 같은 process를 진행 가능해야지 됨
  • grunt build
  • svn에 새로 deploy할 web application의 압축본을 add & commit
  • ftp를 이용한 web application 배포

먼저, 여러 web application을 지원하기 위한 조건은 외부 parameter를 받는 방법이 가장 좋아서 target을 지정하기로 했습니다.

작성된 web aplication들의 구조는 다음과 같습니다.

.
├── admin-web
├── mobile-web
├── public-web
└── sample-web

여기서 sample-web의 경우에는 팀원들에게 같이 공유할 sample web application이기 때문에 배포 대상이 되지 못하고, admin, mobile, public의 경우에는 배포 대상이 될 수 있습니다. 간단히 이름을 기준으로 admin, mobile, public을 기준으로 삼고, 이를 parameter로 받으면 될 수 있다는 판단이 내려졌습니다.

이제 다음 조건들이 필요합니다.

  • grunt,svn 과 같은 command의 실행
  • grunt deploy가 된 후에 zip compress
  • ftp를 이용한 data copy

gradle을 이용한 command의 실행

기본적으로 gradle을 이용한 command는 type을 Exec로 잡아주면 처리가 가능합니다. 이와 같은 sub directory 내에서 실행되어야지 될 command는 workingDir 값을 지정해줘서 처리가 가능합니다. 다음은 grunt build를 하는 code입니다.

task build(type: Exec) {
    workingDir getWebDir()
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        commandLine 'cmd', '/c', 'grunt', 'build'
    } else {
        commandLine 'grunt', 'build'
    }
    ext.output = {
        return standardOutput.toString()
    }
}

윈도우즈 계통과 linux/mac을 모두 지원하기 위해서 OS의 type을 설정해줘야지 됩니다. windows에서 cmd와 /c를 이용하는 것만을 주의하면 매우 간단한 코드입니다.

zip 파일 압축

zip 압축은 gradle에서 기본으로 제공하고 있는 Zip type을 이용하면 됩니다. zip type의 경우, from과 destinationDir만을 주의해서 처리하면 됩니다. 다음은 만들어진 distPath의 모든 내용을 yyyyMMddHHmm-hostname.zip 형식으로 압축을 하는 코드입니다.

task compressDist(type: Zip) {
    dependsOn 'build'

    from file(getDistPath())
    destinationDir file(deployPath)

    def now = new Date()
    def dateFormat = new SimpleDateFormat("yyyyMMddHHmm");
    def hostname = InetAddress.getLocalHost().getHostName().toLowerCase()

    filename = String.format("%s-%s.zip", dateFormat.format(now), hostname)
    archiveName filename
}

FTP 파일 전송

gradle은 자체적으로 FTP를 지원하지 않습니다. 다만 ant 를 지원하고 있기 때문에, ant의 FTP를 이용하면 됩니다. 그런데 ant ftp의 경우에는 send시에 hang이 걸리는 버그를 가지고 있습니다. passive mode를 true로 설정하는 것으로 hang이 걸리는 경우를 조금 덜 하게 할 수 있긴 하지만, 그래도 send에서 hang이 걸리는 것을 완벽하게 막지는 못합니다. 이 부분에 대해서는 좀 더 논의가 필요할 것 같습니다. 다음은 ftp 전송 코드입니다.

task 'upload' {
    dependsOn 'commitSvn'
    doLast {
        def remoteDir = getFtpPath()
        ant {
            taskdef(name: 'ftp', classname: 'org.apache.tools.ant.taskdefs.optional.net.FTP', classpath: configurations.ftpAntTask.asPath)
            ftp(action: 'mkdir', remotedir: remoteDir, server: ftpUrl, userid: ftpUsername, password: ftpPassword)
            ftp(action: 'delete', remotedir: remoteDir, server: ftpUrl, userid: ftpUsername, password: ftpPassword) {
                fileset() { include(name: '**/*') }
            }
            ftp(action: 'send', remotedir: remoteDir, verbose: true,
                    depends: true, binary: true, passive: true,
                    server: ftpUrl, userid: ftpUsername, password: ftpPassword) {
                fileset(dir: getDistPath()) {
                    include(name: '**/**/*')
                }
            }
        }
    }
}

각 build task에 대한 dependency를 추가하고, target argument를 받아서 처리하도록 코드 수정을 마져 완료한 최종 코드는 다음과 같습니다.

import org.apache.tools.ant.taskdefs.condition.Os

import java.text.SimpleDateFormat

configurations {
    ftpAntTask
}

repositories {
    mavenCentral()
}

dependencies {
    ftpAntTask("org.apache.ant:ant-commons-net:1.8.2") {
        module("commons-net:commons-net:1.4.1") {
            dependencies "oro:oro:2.0.8:jar"
        }
    }
}

def ftpUrl = '192.168.13.210'
def ftpUsername = 'ykyoon'
def ftpPassword = 'qwer12#$'
def filename = ''

def getDistPath() {
    return String.format('%s/dist', getWebDir())
}

def getWebDir() {
    return String.format('%s-web', project.target)
}

def getDeployPath() {
    return String.format('../deployed/%s-web/', project.target);
}

def getFtpPath() {
    return String.format('www-fms/%s', project.target);
}

task build(type: Exec) {
//    dependsOn 'upSvn'
    workingDir getWebDir()
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        commandLine 'cmd', '/c', 'grunt', 'build'
    } else {
        commandLine 'grunt', 'build'
    }
    ext.output = {
        return standardOutput.toString()
    }
}

task compressDist(type: Zip) {
    dependsOn 'build'

    from file(getDistPath())
    destinationDir file(deployPath)

    def now = new Date()
    def dateFormat = new SimpleDateFormat("yyyyMMddHHmm");
    def hostname = InetAddress.getLocalHost().getHostName().toLowerCase()

    filename = String.format("%s-%s.zip", dateFormat.format(now), hostname)
    archiveName filename
}

task upSvn(type: Exec) {
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        commandLine 'cmd', '/c', 'svn', 'up'
    } else {
        commandLine 'svn', 'up'
    }
    ext.output = {
        return standardOutput.toString()
    }
}

task addSvn(type: Exec) {
    dependsOn 'compressDist'

    String svnParam = getDeployPath() + filename
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        commandLine 'cmd', '/c', 'svn', 'add', svnParam
    } else {
        commandLine 'svn', 'add', svnParam
    }
    ext.output = {
        return standardOutput.toString()
    }
}

task commitSvn(type: Exec) {
    dependsOn 'addSvn'
    String svnParam = getDeployPath() + filename
    String svnLog = '-mCommit file before deployed'

    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        commandLine 'cmd', '/c', 'svn', 'add', svnParam, svnLog
    } else {
        commandLine 'svn', 'commit', svnParam, svnLog
    }
    ext.output = {
        return standardOutput.toString()
    }
}

task 'upload' {
    dependsOn 'commitSvn'
    doLast {
        def remoteDir = getFtpPath()
        ant {
            taskdef(name: 'ftp', classname: 'org.apache.tools.ant.taskdefs.optional.net.FTP', classpath: configurations.ftpAntTask.asPath)
            ftp(action: 'mkdir', remotedir: remoteDir, server: ftpUrl, userid: ftpUsername, password: ftpPassword)
            ftp(action: 'delete', remotedir: remoteDir, server: ftpUrl, userid: ftpUsername, password: ftpPassword) {
                fileset() { include(name: '**/*') }
            }
            ftp(action: 'send', remotedir: remoteDir, verbose: true,
                    depends: true, binary: true, passive: true,
                    server: ftpUrl, userid: ftpUsername, password: ftpPassword) {
                fileset(dir: getDistPath()) {
                    include(name: '**/**/*')
                }
            }
        }
    }
}

task 'deploy' {
    dependsOn 'build'
    dependsOn 'upload'
    doLast {
        println 'grunt build and upload'
    }
}

task help << {
    println '--------- gradle web command help ----------------------'
    println 'gradle deploy -Ptarget=public : public web build'
    println 'gradle deploy -Ptarget=mobile : mobile web build'
    println '--------- end line -----------------------------------------'
}

모두들 즐거운 코딩 되세요. ^^

Posted by Y2K
,

querydsl 4.x대의 경우, 다음 링크 글을 참고해주세요.

http://netframework.tistory.com/entry/gradle-%EC%A0%95%EB%A6%AC-queryDSL-code-generate-v-4x


기존 Gradle을 이용한 개발환경구성글에서 다음이 변경되었습니다.

Gradle 2.0의 지원

Gradle 2.0에 호환되도록 Project를 변경했습니다. Gradle 2.0의 가장 큰 변화는 groovy 문법의 변경으로 인한 += operator가 변경된 점입니다. 기존 sourceSet에 추가 path를 할 때, 코드는

sourceDir += file('src/main/generated')

로 구성되었지만, groovy의 변경으로 += operator는 모두 같은 형태의 객체들에서만 사용되도록 변경되었습니다. 그래서 이 부분을 다음과 같이 수정하면 됩니다.

sourceDir += [ file('src/main/generated') ]

Java 8의 지원

java 8이 지원될 수 있도록 souceCompatibility와 targetCompatibility가 모두 변경되었습니다.

build.gradle의 간소화

기존에는 base.gradle, domain.gradle과 같이 여러개의 build.gradle 파일로 나뉜 상태를 간소화시켰습니다.
build.gradle과 각 module에서 필요한 gradle 파일을 각기 나누어 처리하도록 변경하였습니다.


web deploy container의 변경

기존 gradle tomcat plugin에서부터 gretty로 변경했습니다. gretty의 특징은 다음과 같습니다.

  • tomcat/jetty 지원
  • tomcat8, jetty9 지원
  • hot-deploy의 지원
  • jvmargs의 지원

무엇보다도 기존 gradle tomcat plugin보다 사용이 편리한 것이 장점입니다. web server를 실행시키는 명령어는 다음과 같습니다.

gradle :privateWeb:jettyRun   //Jetty9을 이용한 web server 실행
gradle :privateWeb:tomcatRun  //tomcat8을 이용한 web server 실행

git hub에 코드를 올려뒀습니다. 모두들 Happy coding~

https://github.com/xyzlast/gradle_multi_project_test

Posted by Y2K
,

Spring을 사용할 때, Proxy mode에서의 @Transactional의 문제점에 대해서 전 글에서 알아봤습니다.

Proxy 모드에서의 가장 큰 문제는 @Transaction이 적용되지 않은 method 내부에서의 @Transaction의 호출입니다. 이 문제를 해결하기 위해서는 Proxy mode에서의 @Transaction이 아닌, AspectJ mode에서 @Transaction을 사용해줘야지 됩니다.

AspectJ mode는 2가지 방법을 제공합니다.

Load-Time Weaver

객체를 Load 할때, AspectJ에 의해서 wearving된 객체를 넘겨주는 방식입니다. 아래와 같은 방식으로 동작하게 됩니다.

  1. application context에 로드된 객체의 loading
  2. aspectj weaver에 의한 객체 weaving (@Transaction annotation이 있는 class, method에 대한 transaction 처리가 된 객체로 변경)
  3. 객체의 이용

위 순서를 보시면 아실 수 있듯이, 이는 객체의 사용에 대해서 약간의 performance의 하락을 가지고 오게 됩니다. application context에서 객체를 load 할 때, aspectj weaver에서 하는 또 다른 일들을 지정하게 됩니다.

Compile-Time Weaver

객체를 Load 할 때, 위와 같은 문제가 있기 때문에, compile 시에 aspectj에서 간섭해서 필요한 객체에 weaving을 시켜서 class를 만들어내는 방식입니다. 이렇게 되면, application context의 load시에 다른 절차가 없기 때문에, performance의 하락도 없는 거의 완벽한 방법으로 구성이 가능합니다.

LTW vs CTW

Load-Time Weaver의 단점은 다음과 같습니다.

  1. application context에 객체가 로드될 때, aspectj weaver와 spring-instrument에 의한 객체 handling이 발생하기 때문에 performance가 저하된다.
  2. web container의 실행시, LTW를 위한 설정이 필요하다.

반면에 Compile-Time Weaver의 단점은 다음과 같습니다.

  1. 개발환경의 구성이 어렵다.
  2. lombok과 같은 compile시에 간섭하는 여러 plugin들과의 매우 다채로운 충돌이 발생한다. 특히 lombok과는 같이 사용하지 못한다고 생각해도 과언이 아니다.

LTW는 운영상의 문제를 발생시킬 수 있고, CTW는 개발상의 문제를 발생시킬 수 있다는 생각이 듭니다. (이런 생각이 들면 무조건 CTW로 가야지 되긴 하는데….;;;)

LTW를 이용한 @Transaction의 처리

먼저 DomainConfiguration에 LTW를 이용한 @Transaction을 다음과 같이 지정해줍니다.

@EnableTransactionManagement(mode = AdviceMode.ASPECTJ, order = 0)

그리고, LTW를 이용하기 위해서 LTW를 활성화시켜야지 됩니다. LTW활성화는 다음과 같습니다.

@EnableLoadTimeWeaving

이것만으로 끝이 아닙니다. LTW는 반드시 다음 jvm option을 가져야지 됩니다. 위에서 서술했듯이, 객체가 로드 될 때, aspectj weaver와 spring-instrument가 각각 객체에 대한 처리를 해줘야지 됩니다. 먼저 aspectj weaver는 실질적으로 일을 하는 객체들이 모여있고, spring-instrument의 경우에는 aspectj weaver에 class loader를 위임하는 일을 맡아서 하게 됩니다. jvm argument에 다음 option을 추가시켜줍니다.

-javaagent:/fullpath/aspectjweaver-1.8.1.jar
-javaagent:/fullpath/spring-instrument-4.0.6.RELEASE.jar

aspectjweaver와 spring-instrument의 뒤에 붙는 버젼은 aspectj와 spring의 버젼과 동일합니다.

intelliJ

intelliJ를 사용하고 있고, JUnit test를 돌리는 경우에는 Run/Debug Configuration에 다음 설정을 추가해줘야지 됩니다.

VM Option에 jvm argument를 default로 넣어주고, 실행시 언제나 사용하도록 구성되어야지 됩니다.

gradle

gradle의 test 시에 역시 다음 jvmargs가 필요하기 때문에 다음과 같은 설정이 필요합니다.

    test {
        jvmArgs '-javaagent:/weavers/spring-instrument-4.0.6.RELEASE.jar ' +
                '-javaagent:/weavers/aspectjweaver-1.8.1.jar'
    }
tomcat

tomcat에서 LTW를 사용하기 위해서는 2가지 방법이 있습니다. 특정 application context에서만 사용할 수 도 있고, 모든 application context에서 사용할 수 있습니다.

특정 application context에서만 LTW를 이용

먼저, spring-instrument.jar파일을 tomcat의 lib 폴더 안에 copy시켜줍니다. 그 후, application context의 context.xml안에 다음 내용을 추가합니다.

<Context path="/ltwdemo">
    <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>
모든 application context에서 LTW를 이용

tomcat 시작시, jvmargs에 -javaagent:/weavers/spring-instrument-4.0.6.RELEASE.jar를 추가해주면 됩니다.

CTW를 이용한 @Transaction의 처리

CTW를 이용하는 경우, 이는 compile시에 처리하는 것이기 때문에 code상의 변화는 거의 없습니다. @EnableLoadTimeWeaver만을 제거시켜주고, mode를 AspectJ로 설정해주면 됩니다.

@EnableTransactionManagement(mode = AdviceMode.ASPECTJ, order = 0)

우리가 개발을 할때, compile을 하는 도구는 거의 2가지입니다. IDE와 build tool(maven, gradle, ant)입니다.

IntelliJ

setting의 compile option을 다음과 같이 변경합니다.

  • Use compiler : Ajc
  • Path to Ajc compiler : AspectJtools.jar 위치 지정
  • Command line parameters : -1.8 (JavaVersion 설정)
gradle

CTW에 대해서 open source로 plugin이 존재합니다. (https://github.com/eveoh/gradle-aspectj)
사용방법이 조금 까다롭습니다. 주의점은 다음과 같습니다.

  • ext.aspectjVersion property가 apply plugin:aspectj 보다 먼저 선언되어야지 됩니다. (파일 위치상에서 line이 더 위여야지 됩니다.)
  • spring-aspectj component가 아래와 같이 두번 선언되어야지 됩니다.
      aspectpath "org.springframework:spring-aspects:${rootProject.ext.springVersion}"
      compile "org.springframework:spring-aspects:${rootProject.ext.springVersion}"
    

다음은 CTW가 적용된 build.gradle의 전체 내용입니다.

apply plugin: 'java'

sourceCompatibility = 1.8
targetCompatibility = 1.8

version = '1.0'
buildscript {
    repositories {
        maven {
            url "https://maven.eveoh.nl/content/repositories/releases"
        }
    }
    dependencies {
        classpath "nl.eveoh:gradle-aspectj:1.4"
    }
}

repositories {
    mavenCentral()
}

ext {
    javaVersion = "1.8"
    springVersion = "4.0.6.RELEASE"
    springjpaVersion = "1.6.0.RELEASE"
    querydslVersion = "3.3.2"
    hibernateVersion = "4.3.4.Final"
    springsecurityVersion = "3.2.4.RELEASE"
    aspectjVersion = '1.8.1'
}

apply plugin: 'aspectj'
dependencies {
    compile 'org.slf4j:slf4j-api:1.7.6'
    compile 'org.slf4j:jcl-over-slf4j:1.7.6'
    compile 'ch.qos.logback:logback-classic:1.0.13'
    compile 'ch.qos.logback:logback-core:1.0.13'

    compile "org.springframework:spring-context:${rootProject.ext.springVersion}"
    aspectpath "org.springframework:spring-aspects:${rootProject.ext.springVersion}"
    compile "org.springframework:spring-aspects:${rootProject.ext.springVersion}"
    compile "org.springframework.data:spring-data-jpa:$rootProject.ext.springjpaVersion"
    compile group: 'org.apache.httpcomponents', name: 'httpclient', version:'4.2.5'
    compile 'org.apache.commons:commons-lang3:3.3.2'

    compile 'org.aspectj:aspectjrt:1.8.1'
    compile 'org.aspectj:aspectjtools:1.8.1'
    compile 'org.aspectj:aspectjweaver:1.8.1'

    testCompile "junit:junit:4.11"
    testCompile 'org.mockito:mockito-core:1.9.5'
    testCompile 'org.hamcrest:hamcrest-all:1.3'
    testCompile "org.springframework:spring-test:${rootProject.ext.springVersion}"

    compile 'mysql:mysql-connector-java:5.1.31'
}

Summary

LTW와 CTW를 이용한 @Transaction에 대해서 정리해봤습니다. 이상하게 인터넷에서 대부분의 코드가 @EnableTransactionManagement에서 mode를 바꾸면 된다. 식의 글만 있고, 명확히 어떤 일들을 해줘야지 되는지 적혀 있지 않아서 한번 정리해볼 필요성을 느껴서 작성하게 되었습니다. CTW가 모든 면에서 우월성을 가지고 있지만, 저는 lombok없는 개발은 어떻게 할지 잘 모르겠다는 생각까지 들 정도로 중독되어서… 걱정중입니다.; lombok과 CTW를 같이 사용할 방법에 대한 고민이 좀 더 필요할 것 같습니다.

Posted by Y2K
,

최신 Gradle 2.0으로 업데이트 한 내용이 존재합니다.


전에 정리한 적이 있는데, 조금 글을 다듬고 정리할 필요성이 있어서 다시 옮깁니다.
먼저, 개발되는 Project는 다음과 같은 구조를 갖습니다.

rootProject
-- domainSubProject (Spring Data JPA + queryDSL)
-- webAppSubProject (Spring Web MVC)

위와 같은 Project는 개발의 편의를 위해서 다음 두 조건을 만족해야지 됩니다.

  1. queryDSL을 사용하기 위한 Q-Entity를 생성이 compileJava task전에 수행되어야지 됩니다.
  2. web application 개발을 위해, tomcat을 실행시킬 수 있어야합니다.
  3. 개발된 web application을 test 환경 또는 product 환경에 배포가 가능해야지 됩니다.

root project

root project는 sub project들의 공통 설정이 필요합니다.

  1. build시에 필요한 plugin의 repository를 설정합니다.
  2. sub project들에서 사용될 공통 dependency들을 설정합니다. (sub project들의 build.gradle 이 너무 길어지는 것을 막을 수 있습니다.)
공통 plugin 추가
  • maven의 POM과 동일한 provided과 optional의 사용을 위해 spring prop plugin을 추가합니다.
  • subprojects들에 필요한 plugin들을 모두 추가합니다. (저는 java와 groovy, idea, eclipse 등을 추가했습니다.)
  • source java version과 target java version을 정해줍니다.
  • source code의 Encoding을 정해줍니다.
  • SubProject에서 기본적으로 사용될 dependency들을 모두 적어줍니다.

build.gradle (root project)

apply plugin: 'base'

// spring prop plugin 추가
buildscript {
    repositories {
        maven { url 'http://repo.springsource.org/plugins-release' }
    }
    dependencies {
        classpath 'org.springframework.build.gradle:propdeps-plugin:0.0.5'
    }
}

subprojects {
    // Plugin 설정, 만약에 code에 대한 static analysis가 필요한 경우에 이곳에 설정.
    apply plugin: 'java'
    apply plugin: 'eclipse'
    apply plugin: 'eclipse-wtp'
    apply plugin: 'idea'
    apply plugin: 'groovy'
    apply plugin: 'propdeps'
    apply plugin: 'propdeps-maven'
    apply plugin: 'propdeps-idea'
    apply plugin: 'propdeps-eclipse'

    // 기본적으로 사용할 repository들을 정의
    repositories {
        mavenCentral()
        maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }
        maven { url "http://192.168.13.209:8080/nexus/content/repositories/releases" }
    }

    dependencies {
        def springVersion = "4.0.1.RELEASE"

        compile 'org.slf4j:slf4j-api:1.7.5'
        compile "org.springframework:spring-context:${springVersion}"
        compile "org.springframework:spring-aspects:${springVersion}"
        compile "org.springframework:spring-jdbc:${springVersion}"
        compile "org.springframework:spring-context-support:${springVersion}"

        compile 'mysql:mysql-connector-java:5.1.27'
        compile 'com.jolbox:bonecp:0.8.0.RELEASE'
        compile 'com.google.guava:guava:15.0'
        compile 'org.aspectj:aspectjrt:1.7.4'
        compile 'org.aspectj:aspectjtools:1.7.4'
        compile 'org.aspectj:aspectjweaver:1.7.4'

        testCompile 'org.springframework:spring-test:4.0.0.RELEASE'
        testCompile "junit:junit:4.11"

        groovy "org.codehaus.groovy:groovy-all:2.1.6"
        testCompile "org.spockframework:spock-core:1.0-groovy-2.0-SNAPSHOT"
        testCompile "org.spockframework:spock-spring:1.0-groovy-2.0-SNAPSHOT"
    }

    // source, target compiler version 결정
    sourceCompatibility = 1.8
    targetCompatibility = 1.8

    // source code encoding
    [compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
}
domain project
  • domain project는 queryDsl의 Q-Entity들의 생성이 필요합니다.
  • compileJava에 대한 Q-Entity 생성 Task의 Depend가 잡혀있으면 사용하기 편합니다.

build.gradle (domain project)

dependencies {
    def springVersion = "4.0.1.RELEASE"

    compile 'org.hibernate:hibernate-core:4.3.1.Final'
    compile 'org.hibernate:hibernate-entitymanager:4.3.1.Final'
    compile "org.springframework:spring-orm:${springVersion}"

    def queryDSL = '3.2.4'
    compile("com.mysema.querydsl:querydsl-core:$queryDSL")
    compile("com.mysema.querydsl:querydsl-jpa:$queryDSL")
    compile("com.mysema.querydsl:querydsl-sql:$queryDSL")
    provided("com.mysema.querydsl:querydsl-apt:$queryDSL") {
        exclude group: 'com.google.guava'
    }
    compile 'org.springframework.data:spring-data-jpa:1.5.0.RELEASE'
}

sourceSets {
    generated {
        java {
            srcDirs = ['src/main/generated']
        }
    }
}

// QEntity 생성 task
task generateQueryDSL(type: JavaCompile, group: 'build', description: 'Generates the QueryDSL query types') {
    source = sourceSets.main.java
    classpath = configurations.compile + configurations.provided
    options.compilerArgs = [
            "-proc:only",
            "-processor", "com.mysema.query.apt.jpa.JPAAnnotationProcessor"
    ]
    destinationDir = sourceSets.generated.java.srcDirs.iterator().next()
}

// compileJava task에 dependency를 걸어줍니다.
compileJava {
    dependsOn generateQueryDSL
    // compile target에 generated된 QClass들의 위치를 추가.
    source sourceSets.generated.java.srcDirs.iterator().next()
}

compileGeneratedJava {
    dependsOn generateQueryDSL
    options.warnings = false
    classpath += sourceSets.main.runtimeClasspath
}

clean {
    delete sourceSets.generated.java.srcDirs
}

idea {
    module {
        sourceDirs += file('src/main/generated')
    }
}
webapplication project

마지막으로 web application project입니다. 이는 조건이 조금 더 많습니다.

  • tomcat을 실행시켜 local 개발 환경에서 사용할 수 있어야지 됩니다.
  • 외부 테스트 또는 운영환경의 tomcat에 배포가 가능해야지 됩니다.

위 조건을 만족하기 위해서 gradle의 tomcat plugin과 cargo plugin을 이용합니다.

plugin에 대한 자료들은 다음 url에서 보다 많은 자료를 볼 수 있습니다.

tomcat plugin > https://github.com/bmuschko/gradle-tomcat-plugin
cargo plugin > https://github.com/bmuschko/gradle-cargo-plugin

build.gradle (webapp project)

apply plugin: 'war'
apply plugin: 'tomcat'
apply plugin: 'cargo'

// tomcat과 cargo plugin에 대한 repository 설정입니다.
buildscript {
    repositories {
        mavenCentral()
        jcenter()
    }

    dependencies {
        classpath 'org.gradle.api.plugins:gradle-tomcat-plugin:1.0'
        classpath 'org.gradle.api.plugins:gradle-cargo-plugin:1.4'
    }
}


dependencies {
    // tomcat plugin 설정입니다.
    String tomcatVersion = '7.0.47'
    tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}"
    tomcat "org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}"
    tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}") {
        exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj'
    }
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
    providedCompile 'javax.websocket:javax.websocket-api:1.0'
    providedCompile 'javax.servlet:jsp-api:2.0'
    providedCompile "org.apache.tomcat:tomcat-servlet-api:${tomcatVersion}"

    // cargo에 대한 설정입니다.
    def cargoVersion = '1.4.5'
    cargo "org.codehaus.cargo:cargo-core-uberjar:$cargoVersion",
            "org.codehaus.cargo:cargo-ant:$cargoVersion"


    def springVersion = "4.0.1.RELEASE"
    compile "org.springframework:spring-webmvc:${springVersion}"
    compile 'jstl:jstl:1.2'
    compile 'org.apache.tiles:tiles-jsp:3.0.3'

    compile 'org.slf4j:slf4j-api:1.7.6'
    compile 'org.slf4j:jcl-over-slf4j:1.7.6'

    compile 'ch.qos.logback:logback-classic:1.0.13'
    compile 'ch.qos.logback:logback-core:1.0.13'

    compile 'org.apache.velocity:velocity:1.7'
    compile 'org.freemarker:freemarker:2.3.20'
    compile 'com.ctlok:spring-webmvc-rythm:1.4.4'
    compile project(':bookstoreHibernate')
}

// tomcarRun을 실행시키기 위해서 war에 대한 dependency를 주입합니다.
tomcatRun {
    contextPath = ""
    URIEncoding = 'UTF-8'
    dependsOn war
}

tomcatRunWar {
    dependsOn war
}

// cargo를 이용한 배포를 위해서 war에 대한 dependency를 주입합니다.
cargoRedeployRemote {
    dependsOn war
}

cargoDeployRemote {
    dependsOn war
}

cargo {
    containerId = 'tomcat7x'
    port = 8080

    deployable {
        context = "${project.name}"
    }

    // remoteDeploy 되는 target의 tomcat 정보
    remote {
        hostname = '192.168.13.209'
        username = 'ykyoon'
        password = 'qwer12#$'
    }
}

bower를 이용한 javascript dependency

web application에서의 외부 javascript dependency를 사용하는 방법입니다. bower를 이용하는 경우, 외부에서 javascript에 대한 source code를 모두 다운받고 compile된 javascript를 dist에 저장하게 됩니다.

그런데, 우리의 web application은 dist에 저장된 특정 파일만을 사용하게 됩니다. 그럼 이 dist에 있는 file을 최종적으로 배포할 webapp folder에 넣어줘야지 됩니다. 이를 위해서 개발된 것이 bower-installer 입니다. 그런데 bower-installer의 경우에는 윈도우즈에서 동작이 정상적이지 않습니다. 아니 실행이 되지 않습니다.; 그래서 bower-installer와 동일한 동작을 하는 task를 만들어봤습니다.

먼저, bower-installer는 bower.json의 install property에 설정합니다. jquery와 bootstrap에 대한 dependency를 설정한 bower.json 입니다.

bower.json

{
    "name" : "bookstore-web",
    "version" : "0.0.0.1",
    "dependencies" : {
        "jquery" : "1.11.0",
        "bootstrap" : "3.1.1"
    },
    "install" : {
        "path" : {
            "css" : "src/main/webapp/lib/css",
            "js" : "src/main/webapp/lib/js",
            "eot" : "src/main/webapp/lib/fonts",
            "svg" : "src/main/webapp/lib/fonts",
            "ttf" : "src/main/webapp/lib/fonts",
            "woff" : "src/main/webapp/lib/fonts",
            "map" : "src/main/webapp/lib/js"
        },
        "sources" : {
            "jquery" : [
                    "bower_components/jquery/dist/jquery.min.js",
                    "bower_components/jquery/dist/jquery.min.map"
                ],
            "bootstrap" : [
                    "bower_components/bootstrap/dist/css/bootstrap.min.css",
                    "bower_components/bootstrap/dist/css/bootstrap-theme.min.css",
                    "bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.eot",
                    "bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.svg",
                    "bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf",
                    "bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff",
                    "bower_components/bootstrap/dist/js/bootstrap.min.js"
                ]
        }
    }
}

위의 install property에 지정된 js와 css들을 옮기는 task는 다음과 같이 설정할 수 있습니다. war task에 dependency를 주입해서 위의 tomcatRun이나 cargoRedeployRemote 등에서도 사용할 수 있습니다.

import org.apache.tools.ant.taskdefs.condition.Os
import groovy.json.JsonSlurper
task bowerInstall(type:Exec, description : 'copy js files dependencies that is defined in Bower.js') {

    if(Os.isFamily(Os.FAMILY_WINDOWS)) {
        commandLine 'cmd', 'bower', 'install'
    } else {
        commandLine 'bower', 'install'
    }

    def jsonHandler = new JsonSlurper()
    def jsonFile = file("bower.json")
    def conf = jsonHandler.parseText(jsonFile.getText("UTF-8"))
    def pathMap = [:]

    conf.install.path.each {
        pathMap.put(it.key, it.value)
    }

    conf.install.sources.each {
        it.value.each { f ->
            def sourceFile = file(f)
            String sourceName = sourceFile.name
            int dotPos = sourceName.lastIndexOf(".")
            String ext = sourceName.substring(dotPos + 1, sourceName.length())
            if(pathMap.containsKey(ext)) {
                copy {
                    from f
                    into pathMap.get(ext)
                }
            }
        }
    }
}

war {
    dependsOn bowerInstall
}


위 정리된 내용을 github에 공유합니다. 아래 주소에서 git clone 하시면 됩니다. ^^

 

 https://github.com/xyzlast/study-spring-bookstore.git



Posted by Y2K
,

java8이 나오고 빠르게 한번 환경을 변경시켜보고 난 후, 문제점 상황을 정리해봤습니다.

jacoco 버젼 문제

jacoco를 최신 버젼으로 해줄 필요가 있습니다. jacoco는 최신 버젼이 0.7.0.201403182114 입니다. 버젼뒤의 날짜를 보시면 아시겠지만, java8 발표 일자와 완전히 동일합니다. 기존 jacoco에서는 byte code exception이 발생하기 때문에 반드시 업그레이드 할 필요가 있습니다.

tomcat 8

gradle tomcat plugin이 아직 tomcat8을 지원하지 못합니다. gradle을 이용한 build의 경우에는 java 8을 사용하시는 것을 고민해주시는 것이 좋습니다. 물론 java 8에서 tomcat7을 사용하는 것은 아무런 문제가 발생되지 않습니다.

SonarQube

sonarQube에서 아직 java 8을 지원하지 못하고 있습니다. 3월말에 최신 업데이트가 될 것이라는 이야기가 아래 링크에 있는데…… java8은 작년 여름에 release 될 예정이였지 않나요. 이걸 믿어야지 되는지 고민하고 있습니다.

http://sonarqube.15.x6.nabble.com/Sonar-Support-for-JDK-8-td5019488.html

무엇보다 위 링크에 이야기되고 있는 JIRA Issue는 이미 resolved된 상태입니다. 일단 stackoverflower에 질문을 올려두긴 했는데.. 메일링 리스트에서 물어보세요. 라는 답변을 받아서 상처받고 메일링 리스트에 가입해서 다시 물어봤습니다. ㅠ-ㅠ 답변을 기다려야지요.

FindBugs

sonarQube에서 지금 지원을 못하는 가장 결정적 이유가 바로 FindBug가 Java8을 지원하지 못하는 이슈가 있기 때문입니다. StringSequence class를 비롯해서 몇몇 Class에 대한 오류를 발생시키고 있습니다. Java8에서 가장 큰 이슈중 하나네요.

Java8

개인적으로는 이번 java8을 매우 기다리고 있습니다. PermGemSize에 대한 GC의 개선과 더불어 lamda expression으로 대표되는 java 언어의 확장성. 그리고 지겹고 지겨운 Date 객체 추가.

개인적으로는 이 3가지때문에 java8을 기다리고 있는데. 다른 분들은 어떠실지 모르겠네요.

Posted by Y2K
,

Gradle 2.0으로 업그레이드 된 버젼에 대해서 업데이트 된 글이 있습니다.


기존 gradle을 이용한 개발 환경 설정 업데이트입니다.


변경사항은 다음과 같습니다. 


1. jetty plugin의 제거 : 개발환경에서는 jetty / tomcat의 차이가 없을 것 같아, jetty를 없앴습니다.

2. spork를 이용한 테스트 환경 추가. : groovy base인 spork를 이용한 test 환경을 추가하였습니다.

3. tomcat plugin 사용시, tomcatRun 에러 수정 : tomcat plugin 사용시 tomcatRun 을 하면 servlet jar class not found 에러가 나오는 것을 수정했습니다. 

4. gradle 1.9 에서 동작하지 않던 버그를 수정했습니다.

5. build.gradle을 4개로 분리했습니다.

1) base.gradle : 기본 설정에 관련된 내용으로 구성됩니다.

2) domain.gradle : domain module에 관련된 내용으로 구성됩니다. (queryDsl QFile generate)

3) web.gradle : war에 관련된 내용으로 구성됩니다. (tomcatRun, tomcatRunWar)


build.gradle

apply plugin: 'base'
apply plugin: 'sonar-runner'
apply plugin: 'maven'
version = '1.0.0'

ext {
    javaVersion = 1.7
    springVersion = '4.0.0.RELEASE'
}


buildscript {
    repositories {
        mavenCentral()
        jcenter()
        maven { url 'http://repo.springsource.org/plugins-release' }
    }
    dependencies {
        classpath 'org.gradle.api.plugins:gradle-tomcat-plugin:1.0'
        classpath (group: 'org.gradle.api.plugins', name: 'gradle-cargo-plugin', version: '0.6.1')
        classpath 'org.springframework.build.gradle:propdeps-plugin:0.0.1'
    }
}

allprojects {
    apply plugin: 'propdeps'
    apply plugin: 'propdeps-maven'
    apply plugin: 'propdeps-idea'
    apply plugin: 'propdeps-eclipse'

    repositories {
        mavenLocal()
        mavenCentral()
        jcenter()
        maven { url 'http://download.java.net/maven/2' }
        maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }
        maven { url 'https://maven.java.net/content/repositories/releases'}
        maven { url 'http://repo.springsource.org/plugins-release' }
    }

    dependencies {
        provided 'org.projectlombok:lombok:0.12.0'
    }
}


apply from: 'base.gradle'
apply from: 'domain.gradle'
apply from: 'web.gradle'


base.gradle

subprojects {
    apply plugin: 'java'
    apply plugin: 'eclipse'
    apply plugin: 'eclipse-wtp'
    apply plugin: 'idea'
    apply plugin: 'jacoco'
    apply plugin: 'groovy'

    sonarRunner {
        sonarProperties {
            property "sonar.sourceEncoding", "UTF-8"
        }
    }

    jacoco {
        toolVersion = '0.6.3.201306030806'
    }

    test {
        jacoco {
            append = false
            destinationFile = file("target/jacoco.exec")
            classDumpFile = file("target/classpathdumps")
        }
    }

    if(project.hasProperty('target')) {
        sourceSets {
            main.resources.srcDirs = ['src/main/resources', "src/main/resources-${project.target}"]
        }
    } else {
        String hostname = InetAddress.getLocalHost().getHostName();
        if(hostname.endsWith('.local')) {   //맥의 경우, .local 이 모든 hostname에 추가됩니다.
            hostname.replace(".local", '')
        }

        sourceSets {
            main.resources.srcDirs = ['src/main/resources', "src/main/resources-" + hostname]
        }
    }

    dependencies {
        def slf4jVersion = "1.7.2"
        compile "org.slf4j:jcl-over-slf4j:$slf4jVersion"
        compile "org.slf4j:jul-to-slf4j:$slf4jVersion"
        compile "org.slf4j:slf4j-api:$slf4jVersion"
        compile 'ch.qos.logback:logback-classic:1.0.13'

        testCompile "junit:junit:4.11"

        groovy "org.codehaus.groovy:groovy-all:2.1.5"
        testCompile "org.spockframework:spock-core:1.0-groovy-2.0-SNAPSHOT"
        testCompile "org.spockframework:spock-spring:1.0-groovy-2.0-SNAPSHOT"
    }

    sourceCompatibility = rootProject.ext.javaVersion
    targetCompatibility = rootProject.ext.javaVersion

    tasks.withType(Compile) {
        options.encoding = 'UTF-8'
    }
}

sonarRunner {
    sonarProperties {
        property "sonar.host.url", "http://192.168.13.209:9000"
        property "sonar.jdbc.url", "jdbc:mysql://192.168.13.209:3306/sonar"
        property "sonar.jdbc.driverClassName", "com.mysql.jdbc.Driver"
        property "sonar.jdbc.username", "root"
        property "sonar.jdbc.password", 'qwer12#$'
    }
}


domain.gradle

configure(subprojects.findAll { it.name.endsWith('domain') }) {
    dependencies {
        compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.22'
        compile("com.jolbox:bonecp:0.8.0.RELEASE") {
            exclude group: 'com.google.guava'
        }
        compile group: 'com.google.guava', name: 'guava', version: '15.0'
        compile group: 'org.springframework', name: 'spring-core', version: "${rootProject.ext.springVersion}"
        compile group: 'org.springframework', name: 'spring-orm', version: "${rootProject.ext.springVersion}"
        compile group: 'org.springframework', name: 'spring-tx', version: "${rootProject.ext.springVersion}"
        compile group: 'org.springframework', name: 'spring-context', version: "${rootProject.ext.springVersion}"
        compile group: 'org.springframework', name: 'spring-context-support', version: "${rootProject.ext.springVersion}"

        compile group: 'org.hibernate', name: 'hibernate-core', version: '4.1.10.Final'
        compile group: 'org.hibernate.javax.persistence', name: 'hibernate-jpa-2.0-api', version: '1.0.1. Final'
        compile group: 'org.hibernate', name: 'hibernate-entitymanager', version: '4.1.10.Final'
        compile group: 'org.hibernate', name: 'hibernate-validator', version: '4.3.1.Final'

        compile group: 'org.springframework.data', name: 'spring-data-jpa', version: '1.4.2.RELEASE'

        def queryDSL = '3.2.4'
        compile("com.mysema.querydsl:querydsl-core:$queryDSL")
        compile("com.mysema.querydsl:querydsl-jpa:$queryDSL")
        compile("com.mysema.querydsl:querydsl-sql:$queryDSL")
        provided("com.mysema.querydsl:querydsl-apt:$queryDSL") {
            exclude group: 'com.google.guava'
        }

    }

    sourceSets {
        generated {
            java {
                srcDirs = ['src/main/generated']
            }
        }
    }

    task generateQueryDSL(type: JavaCompile, group: 'build', description: 'Generates the QueryDSL query types') {
        source = sourceSets.main.java
        classpath = configurations.compile + configurations.provided
        options.compilerArgs = [
                "-proc:only",
                "-processor", "com.mysema.query.apt.jpa.JPAAnnotationProcessor"
        ]
        destinationDir = sourceSets.generated.java.srcDirs.iterator().next()
    }

    compileJava {
        dependsOn generateQueryDSL
        source generateQueryDSL.destinationDir
    }

    compileGeneratedJava {
        dependsOn generateQueryDSL
        options.warnings = false
        classpath += sourceSets.main.runtimeClasspath
    }

    clean {
        delete sourceSets.generated.java.srcDirs
    }

    idea {
        module {
            sourceDirs += file('src/main/generated')
        }
    }
}



web.gradle

configure(subprojects.findAll { it.name.endsWith('Web') }) {
    apply plugin: 'war'
    apply plugin: 'tomcat'
    apply plugin: 'cargo'

    dependencies {
        String tomcatVersion = '7.0.47'
        tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}"
        tomcat "org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}"
        tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}") {
            exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj'
        }

        def cargoVersion = '1.3.3'
        cargo "org.codehaus.cargo:cargo-core-uberjar:$cargoVersion",
                "org.codehaus.cargo:cargo-ant:$cargoVersion"

        providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
        providedCompile 'javax.websocket:javax.websocket-api:1.0'
        providedCompile 'javax.servlet:jsp-api:2.0'
        providedCompile "org.apache.tomcat:tomcat-servlet-api:${tomcatVersion}"
    }

    tomcatRun {
        dependsOn war
    }

    tomcatRunWar {
        dependsOn war
    }

    cargoRedeployRemote {
        dependsOn war
    }

    cargoDeployRemote {
        dependsOn war
    }

    cargo {
        containerId = 'tomcat7x'
        port = 8080

        deployable {
            context = "${project.name}"
        }

        remote {
            hostname = '192.168.13.209'
            username = 'ykyoon'
            password = 'qwer12#$'
        }
    }
}





Posted by Y2K
,

gradle은 maven과 다르게 provided가 지원되지 않는다. 

지원되지 않기 때문에 다양한 방법으로 provided를 사용하고 있는데, spring.org에서 해결책을 내놓은 plugin으로 해결 가능하다. 


github 주소는 이곳에, 

https://github.com/spring-projects/gradle-plugins/tree/master/propdeps-plugin


사용법은 아래와 같다. 

buildscript {
    repositories {
        maven { url 'http://repo.springsource.org/plugins-release' }
    }
    dependencies {
        classpath 'org.springframework.build.gradle:propdeps-plugin:0.0.1'
    }
}

// ...

configure(allprojects) {
    apply plugin: 'propdeps'
    apply plugin: 'propdeps-maven'
    apply plugin: 'propdeps-idea'
    apply plugin: 'propdeps-eclipse'
}


dependencies {
    compile("commons-logging:commons-logging:1.1.1")
    optional("log4j:log4j:1.2.17")
    provided("javax.servlet:javax.servlet-api:3.0.1")
    testCompile("junit:junit:4.11")
}
기존의 idea, eclipse task역시 반영을 받기 때문에 매우 유용하게 사용 가능하다.


Posted by Y2K
,