Maven ตอน profile

marvel
ยัง ยัง ยังเล่นอยู่อีก

รอบที่แล้วได้คายเรื่อง resources ไป เรื่องต่อมาที่เกี่ยวเนื่องกันก็คือเรื่อง profiles 

ยกตัวอย่างง่ายๆเช่นตอนเราทำ Web app ขึ้นมา..

  • ตอนเรา dev เราต่อ DB เครื่องเราเอง
  • ทำเสร็จก็ต้องเอาไปลงเครื่อง Test server ให้ Tester ทดสอบซึ่งก็ต้องต่อ DB สำหรับเทส
  • ตอนส่งลูกค้าต่อ DB เครื่อง UAT หรือ Production

ไอ้ครั้นจะค่อยมาแก้ก่อน package มันก็มีโอกาสผิดพลาดได้ง่ายมาก โชคดีที่ Maven มี <profiles> ไว้สำหรับเรื่องนี้ โดยเราสามารถใส่ได้ดังนี้

2016-02-05 14_07_51-Java - struts2demo_pom.xml - Eclipse Platform
profile id=tumit ลงไปเปลี่ยน username/password เป็น tumit/789456

ตัวอย่างนี้เราเพิ่ม profile ที่ id=tumit ลงไป จากนั้นก็ mvn clean package กันเลย แต่…

2016-02-05 14_12_39-MINGW32__D_github_struts2demo
อ้าว! ทำไมยังเป็น admin/123456 ล่ะ?

ที่มันยังไม่เป็น tumit/789456 เพราะเรายังไม่ได้ระบุให้ Maven รู้ว่าเราใช้ profile tumit ในการ package ตัว Maven เลยไปเอาค่า default ใน properties มาใช้เหมือนเดิม

ดังนั้นเอาใหม่ใส่ mvn clean package -P tumit  เพื่อเป็นการบอกให้ Maven รู้ว่าเราจะใช้ profile อะไร

2016-02-05 14_17_00-MINGW32__D_github_struts2demo.png
เย้! เป็นแล้วๆ

ส่วนตัวใน properties ที่เป็นค่า default มักกำหนดไว้เป็นสำหรับเครื่องทดสอบ แล้วแยก profile ตอน dev เป็นชื่อตัวเอง, uat และ production ตอนจบมันก็เลยเป็นประมาณนี้2016-02-05 14_45_32-Java - struts2demo_pom.xml - Eclipse Platform.pngจบปิ๊ง สวัสดีcode:

    git clone git@github.com:tumit/struts2demo.gitgit pull origin mastergit checkout 6a1c629

Maven ตอน resources

marvel.jpg
เดี๋ยวๆ นี้มัน Marvel เฟ้ย!

ต่อจาก Eclipse หัวข้อถัดมาที่อยากจะ #คายของเก่า คือ Maven นี้นี่เอง

Maven คืออะไร

สำหรับคนที่ไม่รู้ว่า Maven คืออะไรหรือสร้างยังไง… ก็ลองไปกูเกิ้ลเอานะ(ฮา) แต่ถ้าจะให้อธิบายคร่าวๆ Maven ก็คือเครื่องมือช่วย build โปรเจคจาก source code + config ต่างๆให้กลายเป็นไฟล์พร้อมเอาไป execute (jar) หรือ deploy (war)ได้นั้นเอง

Maven กับ resources

ส่วนของเก่าที่อยากคายรอบนี้คือ resources (พวกไฟล์นามสกุล properties, xml หรืออื่นๆที่ไม่ใช่ source code) เช่นถ้าเรามี jdbc.properties ไว้เก็บ username / password ไว้สำหรับต่อ database ปกติเราก็จะสร้างไฟล์ properties แล้วก็ใส่ properties ประมาณนี้

2016-02-03 17_31_55-Java - struts2demo_src_main_resources_jdbc.properties - Eclipse

คือถ้ามันมีไฟล์เดียวคงไม่มีปัญหาอะไร สมมุติถ้าเรามีไฟล์ properties หรือ xml ที่ต้องมา config เยอะๆมานั่งไรแก้ทีละไฟล์คงไม่ดีแน่ Maven จะมาช่วยเราตรงนี้ได้ ด้วยการย้ายมากำหนดที่ Maven ที่เดียวเลย แล้วค่อยเอาค่าที่เรากำหนดใน Maven ไปไล่แปะตามไฟล์พวกนี้ให้

โอเคสมมุติว่าเราสร้างโปรเจคจาก maven-archetype-webapp มาแล้ว แล้วเราต้องการย้ายการกำหนดค่า jdbc.username, jdbc.password มาไว้บน Maven ก็ทำง่ายๆประมาณนี้

แก้ jdbc.properties ไฟล์ซะ

2016-02-03 17_50_49-Java - struts2demo_src_main_resources_jdbc.properties - Eclipse.png
เปลี่ยนค่าเป็นตัวแปรซะโดยใช้ ${xxx} เป็นตัวบอก

เพิ่ม <properties> และสร้าง tag ชื่อเดียวกับที่เรากำหนดไว้ในนั้น

2016-02-03 17_53_13-Java - struts2demo_pom.xml - Eclipse.png
ชื่อ tag ใต้ properties tag ต้องชื่อเดียวกับที่ตั้งไว้

เสร็จแล้วก็เปิด cmd โฟลเดอร์ของโปรเจคจัดเลย mvn clean package

2016-02-03 18_00_57-C__WINDOWS_system32_cmd.exe.png

ถ้ามันมัน “BUILD SUCCESS” ก็ถือว่าโอเค ลองไปเช็คดูในโฟลเดอร์ target/classes จะมี jdbc.properties เด่นเป็นสง่าขึ้นมาแต่..

2016-02-03 18_03_55-jdbc.properties - Notepad2
ค่ายังไม่มา -*-

ค่าไม่มาเพราะเรายังไม่ได้บอก Maven ตอน build ว่าให้ไปกำหนดค่าใน resources ไฟล์ทั้งหลายด้วย ดังนั้นเราก็บอกด้วยการใส่ <resources> ใต้ <build> โดยเราต้องกำหนดโฟลเดอร์ที่เก็บ resources และ flag ที่บอกให้ทำการ filter ลงไปแบบนี้

2016-02-03 18_08_24-Java - struts2demo_pom.xml - Eclipse.png
เอาค่าไป set ให้ทีนะ

mvn clean package อีกที ค่าของ jdbc.username / jdbc.password ก็จะถูกแปะเรียบร้อย เย้ สวัสดี

Eclipse พระจันทร์ที่บังแสงแห่งดวงอาทิตย์

จริงๆจาวามันใช้อะไรช่วยโค้ดได้ทั้งนั้น ~ โปรแกรมเมพท่านนึงกล่าว

จำได้ว่าสมัยทำโปรเจคจบ พระจอมฯปราจีน นั่งเสิร์ซนั่งเลือกอยู่ว่าเราจะใช้อะไรไหนช่วยโค้ด สุดท้ายก็เลือก Eclipse ไม่แน่ว่าเพราะอะไร อาจจะเป็นเพราะหน้าตามันถูกจริตมากกว่า Netbeans (ไม่เกี่ยวกับฟีเจอร์หรืออะไรเลยซักนิด #ฮา)

จบมาทำงานที่แรกเขาก็ใช้ Eclipse ไปกี่ที่ก็ Eclipse สรุปเลยไม่ได้มีโอกาสใช้เจ้าอื่นเลย ..แต่ที่น่าสนใจก็คือ ด้วยความที่ว่า Eclipse มันแสนกลมาก สร้างโปรเจคได้ คอมไพล์ให้ เช็ค syntax ได้ตอนจบยัง export เป็น war ไว้ไปวางบน Tomcat อีกต่างหาก สรุปคือทำให้ทุกอย่าง.. ทุกอย่างจนไม่รู้ด้วยซ้ำว่า file/folder structure จริงๆที่อยู่ใน war ไฟล์มันเป็นยังไง เหลือเชื่อจริงๆว่าทำงานเป็นปีๆได้ไงไม่รู้ กากแท้ข้าพเจ้า ==’

web-demo.jpg
เอ.. แล้วโฟลเดอร์ WebContent มันอยู่ตรงไหนของ war ฟ่ะ ==?

 

มาตอนหลังนี้ดีขึ้นหน่อย ได้มารู้จัก Maven ทำให้ตอนนี้บทบาทของ Eclipse ใช้แค่ import โปรเจคเข้า แก้โค้ด และก็ config ไว้ให้ run Maven เท่านั้นเอง

ข้อดีของการ run Maven บน console ของ Eclipse ก็คือ เวลาจาวามันพ่น exception แบบเวิ้นเว้อ ถ้า run บนจอดำท่อนบนๆมันจะหายไปเลย ถ้า run ใน console ของ Eclipse เรายัง scroll หรือกด Link ไปหาจุดตายได้

eclipse-console.png
run มันตรงนี้แหละ

ส่วนเวอร์ชั่นของ Eclipse แต่ก่อนใช้ JEE developr แต่ตอนหลังใช้แค่ Java ธรรมดาเพราะเชื่อว่ามันจะเบากว่า JEE (น่าจะจริงมั้ง) ส่วน plugin จะมี

  • Eclipse Web Developer Tools – ไว้แก้ HTML JavaScript CSS
  • Maven – อันนี้ติดมากับ Eclipse อยู่แล้ว เอาไว้ช่วย import โปรเจคเข้ามาใน Eclipse และก็ run บน eclipse ได้
  • Properties Editor – ไว้แก้ Message resource
  • Lombok – ใช้ @Getter @Setter (ใช้บ่อยมากกับ Struts2)

ส่วน shortkey ที่ใช้บ่อยก็มี

  • Alt+Shift+O – ให้มัน auto ลบ import ที่ไม่ได้ใช้
  • Alt+Shift+R (ที่ชื่อตัวแปร) – ใช้แก้ชื่อตัวแปรที่อยู่ในคลาสให้เปลี่ยนทั้งคลาส
  • Ctrl+Shift+F -จัด format ของ code
  • F11 – ไว้ run maven แบบ debug ใช้ร่วมกับ dcevm จะได้ไม่ต้อง restart บ่อยๆ
  • Contrl+H -> File Search – ไว้หา ข้อความ ในไฟล์
  • Control+Alt+H – หาว่า method นี้ใครเรียกมันบ้าง

เท่าที่นึกออกมีประมาณนี้ ถ้าคิดเพิ่มได้จะมาเติมทีหลังแล้วกันเนาะ สวัสดี

ปล. อ้อตอนนี้ใช้ an Ergonomic Eclipse Theme for Windows 10 ด้วยแต๊ดแต๋สุดๆ 55

คายของเก่า

เริ่มด้วยความรู้สึกที่ว่าช่วงนี้ชีวิตมันชักจะเฉื่อยๆ รู้สึกตัวเองไม่กระตือรือล้นเท่าที่ควรทั้งที่โลกของโปรแกรมเมอร์วิ่งไวขึ้นเรื่อยๆ ตอนเลยอยากจะเริ่มเรียนรู้อะไรใหม่ๆ แต่ก่อนที่จะไปต่อก็อยากจะ #คายของเก่า ออกมาก่อนเพื่อจะได้เคลียร์หัวให้มันโล่งๆและทบทวนตัวเองไปในตัว

จากที่ทำงานมาหลายปี ที่พอทำได้จำได้ก็ประมาณนี้

  • Eclipse
  • Maven
  • Struts2
  • Jquery Validation
  • Spring
  • Hibernate

หลักๆประมาณนี้ เดี๋ยวจะกลับมาขยี้ที่ละหัวข้อ สวัสดี

Jasypt: มาเข้ารหัส property value ใน properties file กัน

ปกติแล้วเวลาทำ app ขึ้นมาเราก็ต้องมีการเก็บข้อมูลจำพวก username, password ไว้ในไฟล์ properties ประมาณนี้

jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:tcp://localhost/~/appdb
jdbc.username=admin
jdbc.password=password

ซึ่งมันดูโหดมากเพราะเรากำลังเอา password ไปโชว์หร่าบน text ไฟล์กันโต้งๆ ดังนั้นถ้าข้อมูลประเภทที่ควรเป็นความลับก็ควรจะเข้ารหัสซักนิดเพื่อความปลอดภัยในชีวิตและทรัพย์สิน

สมมุติโจทย์

สมมุติโจทย์ให้ app เรา(ของเดิมนั้นแหละ) ต้องอ่านข้อมูล JDBC จาก [root]/cfg/jdbc.properties โดยให้ข้อมูลมี่เป็น password ต้องเข้ารหัสให้ไม่สามารถรู้ได้ว่า password คืออะไร

Code เดิมๆ

ถ้าเป็นแบบเดิมๆเราก็จะมี jdbc.properties เหมือนด้านบนและก็เขียน JdbcProperties ขึ้นมาเพื่ออ่าน properties ขึ้นมาประมาณนี้

package ninja.tumit.launch4jdemo.properties;

import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class JdbcProperties {
 
    final static Logger log = LoggerFactory.getLogger(JdbcProperties.class);
    
    private static final String PROPERTIES_PATH = "../cfg";
    private static String PROPERTIES_FILE = "jdbc.properties";    
    private static JdbcProperties instance = null;
    private Properties prop = null;
    
    private JdbcProperties(){
        
        prop = new Properties();
        try {   
            File executorPath = new File(getExecutorPath());            
            String propertiesPath = executorPath.getParentFile().getAbsolutePath();              
            prop.load(new FileInputStream(propertiesPath+"/"+ PROPERTIES_PATH +"/" + PROPERTIES_FILE));          
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String getExecutorPath() {
        return JdbcProperties.class.getProtectionDomain().getCodeSource().getLocation().getPath();
    }      
    
    public static synchronized JdbcProperties getInstance(){
        if (instance == null)
            instance = new JdbcProperties();
        return instance;
    }

    public String getValue(String key){
        return this.prop.getProperty(key);
    }
}

ในส่วนของ app ก็เรียกมาดูว่าอ่าน properties จาก [root]/cfg/jdbc.properties

package ninja.tumit.launch4jdemo;

import java.io.IOException;

import ninja.tumit.launch4jdemo.properties.JdbcProperties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Hello Launch4j!
 *
 */
public class App 
{
    
    Logger logger = LoggerFactory.getLogger(App.class);

    public void run() {
        try {
            
            String username = JdbcProperties.getInstance().getValue("jdbc.username");
            String password = JdbcProperties.getInstance().getValue("jdbc.password");
            
            logger.info( "JDBC: username={}, password={}",  username, password);
            
            System.in.read();            
        }catch(IOException e) {
            e.printStackTrace();
        }        
    }
        
    public static void main( String[] args ) {
        new App().run();
    }
}
แน่นอนมันควรจะอ่่านได้เรียบร้อยดี

Code แบบเข้ารห้ส

เอาละมาเข้าประเด็นของหัวข้อกัน 😛

ใส่ Jasypt เข้าไปในโปรเจค

แน่นอนอย่างแรกก็ต้องเป็น lib สำหรับช่วยเข้ารหัสก่อนเราใช้ lib ที่ชื่อ Jasypt (อ่านว่าอะไรฟ่ะ) ก่อนอื่ก็เอาเข้าไปใส่ใน pom.xml ก่อน

<dependency>
	<groupId>org.jasypt</groupId>
	<artifactId>jasypt</artifactId>
	<version>1.9.2</version>
</dependency>

เข้ารหัสในไฟล์ properties

ก่อนอื่นเราต้องมีตัวช่วยในการเข้ารหัสเพื่อเอา text ที่เข้ารหัสแล้วไปแปะที่ไฟล์ properties ซึ่งเรา download ได้จากนั้น unzip ซะแล้ว cmd เข้าไปเลย
cmd-jasypt-enc.exe

  • encrypt.bat – executor ตัวช่วย generate password ที่เข้ารหัส
  • input – password เดิมๆที่เราอยากให้เป็นความลับ
  • password – ตัวปั่นให้ password เดิมๆให้อ่านยากๆ
  • —- OUTPUT —-  – นี้แหละ text ที่เราจะเอาไปแปะแทน password เดิม

ว่าแล้วก็เอาไปแปะแทน password เดิมเลย แต่เพิ่ท ENC(xxxx) หน่อยเพื่อเป็นการบอกว่า text ชุดนี้เข้ารหัสนะ

jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:tcp://localhost/~/appdb
jdbc.username=admin
jdbc.password=ENC(4pCWOkm2Kwwa1tfM0x9maZMgfMqsdw0v)

เปลี่ยนตัวอ่าน properties

จาก code เดิมเราใช้คลาส Properties ในการโหลดแต่พอเราใช้ Jasypt ก็ต้องใช้ EncryptableProperties + StandardPBEStringEncryptor แทน

package ninja.tumit.launch4jdemo.properties;

import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;

import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.properties.EncryptableProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class JdbcProperties {
 
    final static Logger log = LoggerFactory.getLogger(JdbcProperties.class);
    
    private static final String PROPERTIES_PATH = "../cfg";
    private static String PROPERTIES_FILE = "jdbc.properties";    
    private static JdbcProperties instance = null;
    private Properties prop = null;
    
    private JdbcProperties(){
        
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        encryptor.setPassword("MAKE_PASS");

        // prop = new Properties();
        prop = new EncryptableProperties(encryptor);        

        try {   
            File executorPath = new File(getExecutorPath());            
            String propertiesPath = executorPath.getParentFile().getAbsolutePath();              
            prop.load(new FileInputStream(propertiesPath+"/"+ PROPERTIES_PATH +"/" + PROPERTIES_FILE));          
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String getExecutorPath() {
        return JdbcProperties.class.getProtectionDomain().getCodeSource().getLocation().getPath();
    }      
    
    public static synchronized JdbcProperties getInstance(){
        if (instance == null)
            instance = new JdbcProperties();
        return instance;
    }

    public String getValue(String key){
        return this.prop.getProperty(key);
    }
}
  • StandardPBEStringEncryptor – เราจะเอา MAKE_PASS(ตัวช่วยปั่น) ใส่ไปในออปเจคของคลาสนี้
  • EncryptableProperties – เปลี่ยนจาก Properties ธรรมดาเป็นตัวนี้แล้วเอา encryptor หยอดไป

กลับไป run App อีกทีนี้เราก็จะสามารถใช้ properties ที่เข้ารหัสได้เป็นปกติเหมือนไม่เคยเข้ารหัสมาก่อน

buid ซิ buid

อ้าว! ลืมก็อปไฟล์ properties
อ้าว! ลืมก็อปไฟล์ properties
launch4j-enc
ก็อปปี้แล้ว run โอเช

Launch4j: วิธีกำหนด parameter ไปให้ app

จากโปรเจค Launch4j ในบทความก่อนหน้านั้น เราได้สร้าง app เล็กๆขึ้นมาแล้ว ต่อมาสมมุติว่าเราต้องการส่งพวก JVM parameter ไปให้ app ละทำไงดี

สมมุติโจทย์

สมมุติว่าโจทย์คือ “ให้ app เราพ่น log ออกมาได้และสามารถกำหนดได้ว่าจะให้พ่นแบบไหนยังไง” ในส่วนของ log ผมจะใช้ sfl4j+log4j ช่วยในการพ่นและก็จะกำหนดให้ app ไปอ่าน log4j.properties ตาม path ที่กำหนดเพื่อจะได้กำหนด format ของข้อความและ path ของ ไฟล์ log ได้

Code & Config

เริ่มจากเพิ่ม log library เข้าไปให้โปรเจคก่อน

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.6.1</version>
		</dependency>

จากนั้นก็ลองเขียน log ออกมาแล้ว run ซิ

package ninja.tumit.launch4jdemo;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Hello Launch4j!
 *
 */
public class App 
{
    
    Logger logger = LoggerFactory.getLogger(App.class);

    public void run() {
        try {
            logger.info( "Hello {}! on {}", "Launch4j",  System.getenv("computername"));
            System.in.read();            
        }catch(IOException e) {
            e.printStackTrace();
        }        
    }
    
    
    public static void main( String[] args ) {
        new App().run();
    }
}

log message แดงเถือก

message แดงเถือกกันเลย เพราะเรายังไม่ได้กำหนด properties อะไรให้ log library เลยดังนั้นก็ search หาตัวอย่าง log4j.properties มา ลองใส่เข้าไปใน resources เลย พลั๊กเข้าให้

log.file

run อีกทีควรจะโอเค จากนั้นทดลองเสร็จก็ pack เป็น exe โล้ด การกำหนด log4j.properties ให้ app นั้นต้องใช้ผ่าน JVM parameter ตอน run ด้วย parameter แบบนี้

-Dlog4j.configuration=file:e:\launch4jdemo\cfg\log4j.properties

และส่วนของ launch4j ก็สามารถกำหนด parameter ได้หลายวิธี แต่วิธีที่ผมเลือกใช้คือสร้างไฟ xxx.l4j.ini ในโฟลเดอร์ที่เราแปะ exe แล้วเราก็สร้างไฟล์แล้วใส่ไปเลย
log-cfg
แค่นี้เราก็สามารถส่ง JVM parameter ไปให้ app ของเราได้แล้ว

Launch4j: run Java แบบ exe + พก JRE ไปด้วย

โจทย์ที่ได้มา

หมายเหต: รอบนี้เฉพาะ Windows เท่านั้นนนนน !!!

รอบนี้โจทย์ที่ได้มาคือ “อยากใด้โปรแกรมจาวาเล็กๆไว้ run ได้แบบไม่ต้อง install Java” ก็ search ไปมาไปเจอตัวนึงน่าสนใจคือเจ้า Launch4j

จริงๆหลักๆแล้วเจ้า Launchb4j มันเอาไว้แปลงร่างจาก .jar ไปเป็น .exe ที่เหล่าวินโดเลี่ยนคุ้นเคยและยังมีความสามารถในการแอบแปะ jre ไปกับ exe ได้ด้วย โอเช เรามาลองกัน

เขียน App ง่ายๆขึ้นมาตัวนึง

ขั้นแรกเขียน App ง่ายๆขึ้นมาตัวนึงก่อน เพื่อดูว่ามันจะ run ได้ไหม

package ninja.tumit.launch4jdemo;

import java.io.IOException;

/**
 * Hello Launch4j!
 *
 */
public class App 
{
    public static void main( String[] args ) 
    {
        try {
            System.out.println( "Hello Launch4j!" );
            System.in.read();            
        }catch(IOException e) {
            e.printStackTrace();
        }
    }
}

App นี้ไม่ทำอะไรเลยนอกจากพ่นคำว่า “Hello Launch4j!” แล้วก็รอ enter แล้วจึงจะออกจากโปรแกรม

ใช้ Launch4j Maven Plugin

จากนั้นก็ไป search หาวิธีใช้เจ้า launch4j ก็ไปเจอโพสนึงใน Stackoverflow (แต่หา post นั้นไม่เจอซะละ) บอกวิธีใช้ Launch4j Maven Plugin ดีเลยเราใช้ Windows เราใช้ Maven ว่าแล้วก็ copy โครมมมใน pom.xml แก้นิดหน่อยให้เป็นของเราเองซะก็ได้หน้าตาประมาณนี้ประมาณนี้

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

	<groupId>ninja.tumit</groupId>
	<artifactId>launch4jdemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

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

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-shade-plugin</artifactId>
				<version>1.4</version>
				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>shade</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<shadedArtifactAttached>true</shadedArtifactAttached> <!-- Make the shaded artifact not the main one -->
					<shadedClassifierName>shaded</shadedClassifierName> <!-- set the suffix to the shaded jar -->
				</configuration>
			</plugin>
			<plugin>
				<groupId>com.akathist.maven.plugins.launch4j</groupId>
				<artifactId>launch4j-maven-plugin</artifactId>
				<executions>
					<!-- Command-line exe -->
					<execution>
						<id>l4j-cli</id>
						<phase>package</phase>
						<goals>
							<goal>launch4j</goal>
						</goals>
						<configuration>
							<headerType>console</headerType>
							<outfile>target/launch4jdemo.exe</outfile>
							<jar>target/${artifactId}-${version}-shaded.jar</jar> <!-- 'shaded' is the value set on shadedClassifierName above -->
							<classPath>
								<mainClass>ninja.tumit.launch4jdemo.App</mainClass>
							</classPath>
							<jre>
								<path>../jre</path>
								<minVersion>1.6.0</minVersion>
								<maxVersion>1.8.0</maxVersion>
							</jre>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

</project>

อธิบาย pom กันซะหน่อย จุดที่น่าสนใจก็มี

  • maven-shade-plugin เป็นตัวช่วย pack พวก libs ที่เราต้องใช้ให้เป็น jar ตัวเดียวเก๋ๆ
  • launch4j-maven-plugin ตัวนี้แหละพระเอกของงานมันจะเอา jar ที่ pack เป็นหนึ่งเดียวแล้วไปทำเป็น exe โดย
    • outfile บอกว่าให้เป็น exe ชื่ออะไร เก็บที่ไหน
    • jar บอกว่าจะเอา jar ที่ไหนมาทำ (ก็เอา jar จาก shade ไง)
    • classPath.mainClass ก็ระบุไปว่าจะ execute main ตัวไหน
    • และ jre บอก path ของ jre โปรเจคนี้เราจะเอา jre ไปด้วยจะได้ไม่ต้องเสียเวลา install

ว่าแล้วก็ลอง pack กันเลย

mvn clean package

ถ้าไม่มีอะไรผิดพลาดเราก็จะเห็น launch4jdemo.exe ตัวเป็นๆที่ targat
launch4j-target

เอาไป run กัน

โดยเรากำหนด folder structure ง่ายๆดังนี้

launch4j-folders

  • bin เอาไว้เก็บ exe ก็ไป copy launch4jdemo.exe มาแปะ
  • jre เอาไว้เก็บ jre (จะอธิบายทำไม == ‘)
    ปล. สังเกตุ plugin config ใน <jre> เราถึงระบุว่าให้เป็น ../jre นั้นเอง แล้วก็ไป copy jre มาแปะซะ (folder bin กับ lib)

เราก็จะได้ exe ไว้ run Java app ของเราได้เลย

launch4jdemo-exe

======== จบ(ก่อนตอนนึง)จ้า ========

อ้างอิง: http://launch4j.sourceforge.net/ , https://github.com/lukaszlenart/launch4j-maven-plugin