반응형

출처

https://dzone.com/articles/java-8-how-to-create-executable-fatjar-without-ide

명령줄로 실행 가능한 Fat JAR 생성하기

명령줄 만으로 실행 가능한 fat JAR 파일을 만들고 실행하고 싶으십니까? 필요한 기초 작업과 수행 방법을 확인하십시오.

이 글은 추가 플러그인, IDE 또는 다른 도구를 사용하지 않고 순수한 명령 줄과 Java에서 Fat JAR (Java 아카이브 파일)을 만들 수 있는 가능성을 검토하는 내 블로그 게시물을 통합한 내용입니다.

빌드 도구 (Ant, Maven 또는 Gradle)의 세계에서는 명령줄에 대해 생각하는 것이 유용하지 않을 수도 있습니다. 가장 유명한 IDE (IntelliJ, Eclipse 또는 NetBeans)는 빌드 도구와 구현을 즉시 제공합니다. 그러나 명령줄 만 있고 인터넷에 액세스 할 수 없다고 가정해 보겠습니다.

그러면 어떻게 하시겠습니까?

파트 1: ExecutableOne.jar 컴파일 (GitHub)

이 첫 번째 부분의 목표는 실행 가능한 JAR 파일을 만드는 것입니다. ExecutableOne.jar 이라고 부르겠습니다. 명령줄을 열어서 간단한 프로젝트 폴더를 생성해 보겠습니다. 예제 프로젝트 구조는 Maven 표준 디렉토리 레이아웃 구조를 따릅니다.

./libs
./out
./README.md
./src
./src/main
./src/main/java
./src/main/java/com
./src/main/java/com/exec
./src/main/java/com/exec/one
./src/main/resources

우리의 의도는 실행 가능한 JAR 파일을 만드는 것이므로 기본 클래스를 만들어야 합니다. com.exec.one 패키지에서 해봅시다. 패키지는 샘플 프로젝트 구조의 SRC/MAIN/JAVA 폴더에서 찾을 수 있습니다.

package com.exec.one;

public class Main {
    public static void main(String[] args){                                                                                 
        System.out.println("Main Class Start");                     
    }                                                      
}

SRC/MAIN/RESOURCES 폴더 안에 META-INF 폴더를 만든 다음 그 안에 MANIFEST.MF 파일을 배치합니다. 새로 생성된 MANIFEST.MF 파일을 열고 기본 설명을 입력해 보겠습니다.

Manifest-Version: 1.0   
Class-Path: .                                                                                                                                                                          
Main-Class: com.exec.one.Main

참고 : JAR 파일 당 하나의 MANIFEST.MF 파일만 있습니다.

MANIFEST.FM 파일에는 JAR 파일이 사용되는 방법에 대한 세부 사항이 포함되어 있습니다. 자세한 내용은 다루지 않습니다. 정의한 옵션에 집중하겠습니다.

  1. Manifest-Version : manifest 파일 버전입니다.
  2. 클래스 경로 : 애플리케이션 또는 확장 클래스 로더는 이 속성 값을 사용하여 내부 검색 경로를 구성합니다. 원래 클래스 로더는 검색 경로에서 각 요소를 다운로드하고 엽니다. 이러한 목적을 위해 간단한 선형 검색 알고리즘이 사용되었습니다.
  3. Main-Class : 시작시에 런처가 로드할 클래스의 이름이 있습니다.

이제 *.jar 라이브러리없이 JAR 파일을 생성합니다. 프로젝트 구조의 LIBS 폴더는 여전히 비어 있습니다. 이렇게 하려면 먼저 javac를 사용하여 프로젝트를 컴파일해야합니다. 한편 출력은 OUT 폴더에 저장합니다. 명령 줄로 돌아가서 프로젝트 루트 안에 다음을 입력해 보겠습니다.

$javac -cp ./src/main/java ./src/main/java/com/exec/one/*.java -d ./out/

프로젝트가 OUT 디렉터리로 컴파일되었습니다. ls 명령을 사용하여 확인할 수 있습니다.

두 번째 단계는 OUT 디렉터리에 있는 리소스에서 실행 가능한 JAR 파일을 만드는 것입니다. 명령줄로 돌아가서 다음 명령을 실행합니다.

$jar cvfm ExecutableOne.jar ./src/main/resources/META-INF/MANIFEST.MF -C ./out/ .

우리가 사용한 JAR 도구 옵션을 간단히 검토 및 설명해 보겠습니다.

  • c : 새 JAR 파일을 만들려고 함을 나타냅니다.
  • v : 표준 출력에 대한 자세한 출력을 생성합니다.
  • f : 생성 할 jar 파일을 지정합니다.
  • m : 우리가 사용하는 매니페스트 파일을 나타냅니다. 매니페스트 파일에는 이름-값 쌍이 포함됩니다.
  • -C : 디렉토리에 대한 임시 변경을 나타냅니다. 이 디렉토리에서 JAR 파일로 클래스가 추가됩니다. 점은 모든 클래스 (파일)를 나타냅니다.

최종 출력을 위해 명령줄을 열고 다음을 입력합니다.

$java -jar ./ExecutableOne.jar

standard output: 
Main Class Start

잘 했습니다! 파트 2로 이동하겠습니다.

파트 2: 추가적인 패키지와 함께 ExecutableOne.jar 컴파일

이 섹션의 주요 목표는 추가적인 패키지를 포함하여 실행 가능한 JAR 파일을 컴파일 아는 방법을 보여드리겠습니다. 이러한 목적을 위해, 우리는 MagicService를 만들 것입니다. 이 서비스는 우리에게 getMessage() 메소드를 우리에게 제공하며 표준 출력으로 메세지를 출력합니다.

명령줄을 열어 새로운 폴더 _SERVICE_와 파일 MagicService.java를 만듭니다.

$mkdir src/main/java/com/exec/one/service
$vi src/main/java/com/exec/one/service/MagicService.java

새롭게 만들어진 MagicService는 다음 예제에서 사용될 수 있습니다.

package com.exec.one.service;                                                                                                                                                          
public class MagicService {                                                                                                                                                            
  private final String message;                                       
    public MagicService(){ 
        this.message = "Magic Message";
    }                    

    public String getMessage(){                                                      
         return message;                              
    }
}

MagicService는 Main 클래스보다 패키싲 구조에서 다른 위치에 있습니다. 이제 우리는 Main 클래스로 돌아가서 새롭게 만든 MagicService를 import 합니다. import하고 서비스 인스턴스를 만든 뒤에 Main 클래스는 getMessage() 메소드로 접근을 할 것입니다. Main 클래스는 다음 방법으로 변경될 것입니다.

package com.exec.one;                                                                                                                                                                  
import com.exec.one.service.MagicService;                                                                                                                                              
public class Main {                                                                                                         
    public static void main(String[] args){
        System.out.println("Main Class Start");            
        MagicService service = new MagicService();          
        System.out.println("MESSAGE : " + service.getMessage());
     }
} 

이제 코드를 컴파일 할 준비가 된 지점에 도달했습니다. 명령줄로 돌아가 Executable-One 프로젝트의 루트 폴더로 이동하겠습니다. 첫 번째 단계는 Executable-One 프로젝트를 OUT 폴더로 컴파일 / 재컴파일하는 것입니다. 이를 위해 새로 생성된 MagicService.java 클래스의 위치를 추가해야 합니다.

javac -cp ./src/main/java ./src/main/java/com/exec/one/*.java ./src/main/java/com/exec/one/**/*.java -d ./out/

두 번째 단계는 컴파일된 클래스에서 실행 가능한 JAR 파일을 만드는 것입니다. JAR 파일 논리를 변경하지 않았으므로 명령을 변경할 필요가 없습니다. 이는 MANIFEST.FM 파일이 변경없이 그대로 유지됨을 의미합니다.

Manifest-Version: 1.0
Class-Path: .                                                           
Main-Class: com.exec.one.Main

이제 샘플 프로젝트의 루트 디렉토리에서 Part 1과 유사한 명령을 다시 실행할 수 있습니다.

jar cvfm ExecutableOne.jar ./src/main/resources/META-INF/MANIFEST.MF -C ./out/ .

생성된 JAR 파일을 실행하여 표준 출력에 인쇄된 메시지를 얻습니다.

$java -jar ExecutableOne.jar 
output: 
Main Class Start
MESSAGE : Magic Message

축하합니다. 다시 잘 하셨습니다!

파트 3: 실행 가능한 Fat JAR 생성하기 (GitHub)

이 파트의 목표는 개발된 프로그램에서 모든 필요한 의존성을 포함하는 fat JAR(Java Archive)를 생성하는 것입니다. 외부 라이브러리로 우리는 파트 2에서 생성된 JAR 파일을 사용할 필요가 있습니다. 파트 3에서 우리는 (위의 링크에서 다운로드 받을 수 있는) "Executable-Two"라고 불리는 샘플 프로젝트를 생성합니다.

executable-two 프로젝트는 다음과 같은 폴더 구조를 가집니다.

./libs
./libs/ExecutableOne.jar
./out
./README.md
./src
./src/main
./src/main/java
./src/main/java/com
./src/main/java/com/exec
./src/main/java/com/exec/two
./src/main/java/com/exec/two/Main.java
./src/main/resources
./src/main/resources/META-INF
./src/main/resources/META-INF/MANIFEST.MF

LIBS 폴더는 전에 생성된 "ExecutableOne.jar"를 포함합니다. "ExecutableOne.jar"는 우리가 ExecutableTwo 안에서 사용될 MagicService 클래스를 포함합니다. 우리는 클래스 MagicService를 인스턴스화 하여 public 메소드 getMessage()를 실행할 것입니다. 프로젝트 "ExecutableTwo"의 메인 클래스 안에서 이 모든 것이 발생할 것입니다.

프로젝트 패키지 com.exec.two 에서 다음 메인 클래스를 생성합시다.

package com.exec.two;                                                                                                                                                                  
import com.exec.one.service.MagicService;                                                                                                                                              
public class Main {
    public static void main(String[] args){
        System.out.println("Executable-Two Main");
        MagicService service = new MagicService();                                                                                 System.out.println("MagicService from Executable-ONE");                                                                     System.out.println("MESSAGE: " + service.getMessage());
     }
}

이제 우리는 JAR 파일 생성의 모든 것이 준비 되었습니다. 이전에 생성한 getMessage() 메소드를 실행한 JAR 라이브러리에서 MagicService를 가져 왔습니다. 다음 몇 단계에서는 Java JDK에서 제공하는 javac 및 JAR 도구를 사용합니다. 명령 줄로 돌아가서 프로젝트를 컴파일해 보겠습니다. 명령에서 클래스 경로를 사용된 라이브러리로 확장해야 함을 컴파일러에 알려야합니다.

$javac -cp ./src/main/java 
./src/main/java/com/exec/two/*.java -d ./out/ 
-classpath ./libs/ExecutableOne.jar

"Executable-Two" 프로젝트가 OUT 디렉토리로 성공적으로 컴파일 되었습니다.

이제 fat JAR 생성을 위해 OUT 디렉토리를 적절하게 준비할 때입니다. OUT 디렉토리 안에는 "Executable-Two"를 위해 만든 컴파일 된 클래스가 있습니다. 한편, JAR 도구는 파일 시스템에 물리적으로 위치한 파일만 읽습니다. 압축된 JAR 파일은 읽지 않습니다. 물론 이는 JAR 도구가 OUT 디렉토리에있는 * .jar 파일의 압축을 풀거나 읽지 않음을 의미합니다.

그 결과 ExecutableOne.jar을 OUT 디렉토리에 복사하더라도 JAR 도구는 ExecutableOne.jar 파일의 압축을 풀지 않고 라이브러리가 결과에 추가됩니다 (압축된 형태로). 물론 압축 되었기 때문에 무시됩니다.

문제는 $java -jar 도구가 내부 패키지 *.jar 아카이브 파일을 읽지 않는다는 것입니다!

이는 이전에 생성된 Java 아카이브 (JAR) "Executable-One.jar"을 "Executable-Two" 프로젝트의 OUT 디렉토리에 압축 해제해야 함을 의미합니다. 명령 줄을 열고 다음을 입력합니다.

$cp libs/ExecutableOne.jar ./out/
$cd ./out
$jar xf ExecutableOne.jar
$rm ExecutableOne.jar

이제 "Executable-Two" 프로젝트 출력 디렉토리를 새 JAR 파일의 소스 폴더로 사용할 준비가 되었습니다.

참고: 모든 실행 가능한 JAR 파일에는 하나의 MANIFEST.FM 파일만 사용할 수 있습니다.

"Executable-Two"프로젝트를 JAR 아카이브 파일에 묶기 위해 ./src/main/resources/META-INF/ 폴더에 새로 생성된 매니페스트 파일을 사용합니다.

Manifest-Version: 1.0                              
Class-Path: .                                      
Main-Class: com.exec.two.Main

다음처럼 타이핑하여 이 모두를 묶을 수 있습니다..

$jar cvfm ExecutableTwo.jar ./src/main/resources/META-INF/MANIFEST.FM -C./out/ .

새로 생성된 fat JAR 파일인 "ExecutableTwo.jar"을 실행하면 다음과 같은 출력이 나타납니다.

$java -jar ./ExecutableTwo.jar
output:
Executable-Two Main
MagicService from Executable-ONE
MESSAGE: Magic Message

축하합니다! 당신은 fat JAR 파일을 실행했습니다!

반응형
반응형

출처

https://stackoverflow.com/questions/18431857/compile-source-file-to-a-different-directory

소스 파일을 다른 디렉터리로 컴파일?

다른 디렉터리로 자바 소스 파일 (*.java)를 컴파일 하는 방법이 있을까요?

만약 제 패키지 파일 구조가 다음과 같습니다.

Mathematics ->
  Formulas ->
    src ->
      // 수학 공식을 포함하는 소스 파일...
    bin ->
      // 수학 공식을 포함하는 class 파일...
  Problems ->
    src ->
      // 수학 문제를 포함하는 소스 파일...
    bin ->
      // 수학 문제를 포함하는 class 파일...

저는 소스와 class 파일을 폴더 구조를 유지한 채로 분리하고 싶고, 컴파일 할 때마다 src 폴더를 bin 폴더로 모든 class 파일을 복사해야 합니다.

javac 명령어로 다른 폴더로 클래스 파일을 컴파일함으로써 이 과정을 간단하게 하는 방법이 있을까요?


1개의 답변

옙, 절대적으로 출력 디렉터리를 지정하기 위해 -d 옵션을 사용합니다.

javac -d bin src/foo/bar/*.java

당신이 지정한 디렉터리는 출력 구조의 루트 입니다. 관련 하위 디렉토리는 코드의 패키지 구조에 따라 자동으로 생성됩니다.

자세한 내용은 javac 문서를 참조하십시오.

이 경우 하나의 javac 명령을 실행하여 공식 디렉터리를 컴파일하고 다른 하나는 문제 디렉터리를 컴파일 해야 합니다. 잠재적으로 문제 디렉터리를 컴파일 할 때 클래스 경로의 일부로 공식 bin 디렉토리를 사용합니다.

(단일 소스 구조를 사용하지만 다른 패키지를 사용하는 것을 고려할 수도 있습니다. IDE를 사용하여 이러한 복잡성 중 일부를 숨기는 것도 고려해야 합니다. 실제로는 어렵지 않더라도 이 모든 작업을 수작업으로 수행하는 데 지치게 됩니다.)

반응형
반응형

참고주소 : https://stackoverflow.com/questions/85190/how-does-the-java-for-each-loop-work

어떻게 자바의 'for each' 루프가 작동하나요?

다음을 고려할 때

List<String> someList = new ArrayList<String>();
// "monkey", "donkey", "skeleton key"를 someList에 추가
for (String item : someList) {
    System.out.println(item);
}

for each 문법을 사용하지 않고 for 루프처럼 똑같이 하려면 어떻게 해야할까요?


28개 답변 중 1개

for (Iterator<String> i = someIterable.iterator(); i.hasNext();) {
    String item = i.next();
    System.out.println(item);
}

루프에서 i.remove();를 사용할 필요가 있거나, 다른 방법으로 실제 iterator를 접근(수정)한다면 구문을 실제 iterator만 추론(읽기) 때문에 for ( : )를 사용할 수 없습니다.

Denis Bueno가 언급했듯이 이 코드는 Iterable 인터페이스를 구현하는 모든 개체에서 작동합니다.

또한 for (:) 관용구의 오른쪽이 Iterable 객체가 아닌 array(배열)인 경우 내부 코드는 int 인덱스 카운터를 사용하고 대신 array.length를 확인합니다. Java 언어 스펙을 참조하십시오.

반응형
반응형

참고주소 : https://stackoverflow.com/questions/2235276/how-to-run-junit-test-cases-from-the-command-line

명령어 라인에서 JUnit test case를 실행하는 방법

저는 명령어 라인에서 JUnit test case를 실행하고 싶습니다. 어떻게 이를 할 수 있을까요?


10개 답변 중 1개만 추려냄

JUnit 5.X에서는

java -jar junit-platform-console-standalone-<version>.jar <Options>

https://stackoverflow.com/a/52373592/1431016 에서 간단한 요약을 찾을 수 있고 https://junit.org/junit5/docs/current/user-guide/#running-tests-console-launcher 에서 전체 세부사항을 볼 수 있습니다.

JUnit 4.X에서는

java -cp .:/usr/share/java/junit.jar org.junit.runner.JUnitCore [test class name]

JUnit 3.X를 사용하신다면 클래스 이름이 다릅니다.

java -cp .:/usr/share/java/junit.jar junit.textui.TestRunner [test class name]

윈도우즈에서는 세미콜론으로 유닉스/리눅스에서는 콜론으로 classpath로 클래스 파일을 디렉터리나 JAR를 더 추가하고 싶을 수 있습니다. 환경에 따라 다릅니다.

편집: 저는 예제로 현재 디렉터리를 추가하였습니다. 환경과 당신이 응용프로그램을 어떻게 빌드하느냐(bin/ 또는 build/ 또는 my_application.jar 등)에 의존적입니다. Java 6+는 classpath에 glob을 지원합니다. 다음처럼 할 수 있습니다.

java -cp lib/*.jar:/usr/share/java/junit.jar ...

도음이 되길 바랍니다. 테스트를 작성하세요! :-)

반응형
반응형

출처: https://en.wikipedia.org/wiki/Multiton_pattern

Multiton 패턴

소프트웨어 엔지니어링에서 multiton 패턴은 singleton 패턴을 일반화한 디자인 패턴입니다. singleton은 생성될 클래스의 인스턴스를 하나만 허용하지만, multiton 패턴은 map의 사용을 통해 관리되는 여러개의 인스턴스 생성을 허용합니다.

응용프로그램(예시 java.lang.Runtime) 당 하나의 인스턴스를 가지는 것에 비해 multiton 패턴은 대신에 key 당 하나의 인스턴스를 보장합니다.

대부분의 사람과 교과서는 singleton 패턴을 고려합니다. 예를 들어 multiton은 객체지향 프로그래밍 교과서인 디자인 패턴에 자주 나타나지는 않습니다. (이는 더 유연한 접근인 singleton들의 레지스트리라는 이름으로 나타납니다.)

설명

multiton이 액세스가 동기화된 해시 테이블인 것처럼 보일 수 있지만 두 가지 중요한 차이점이 있습니다. 첫째, multiton은 클라이언트가 매핑을 추가하는 것을 허용하지 않습니다. 둘째, multiton은 null 또는 빈 참조를 반환하지 않습니다. 대신 연결된 키를 사용하여 첫 번째 요청에서 multiton 인스턴스를 만들고 저장합니다. 동일한 키를 가진 후속 요청은 원래 인스턴스를 반환합니다. 해시 테이블은 구현 세부 사항 일뿐 가능한 유일한 접근 방식은 아닙니다. 이 패턴은 애플리케이션에서 공유 객체 검색을 단순화합니다.

객체 풀은 인스턴스가 아닌 클래스와 연결된 멤버이므로 한 번만 생성되기 때문에 multiton은 트리 구조로 진화하는 대신 플랫 동작을 유지합니다.

multiton은 풀의 각 멀티 톤 인스턴스가 자체 상태를 가질 수 있는 multiton의 단일 디렉토리 (즉, 모든 키가 그 자체로 동일한 네임 스페이스에 있음)에 대한 중앙 집중식 액세스를 제공한다는 점에서 고유합니다. 이러한 방식으로 패턴은 시스템에 대한 필수 개체의 색인화된 저장소 (예 : LDAP 시스템에서 제공)를 옹호합니다. 그러나 multiton은 분산 시스템이 아닌 단일 시스템에서 광범위하게 사용되도록 제한됩니다.

단점

Singleton 패턴과 같은 이 패턴은 애플리케이션에 전역 상태를 도입하므로 단위 테스트를 훨씬 더 어렵게 만듭니다.

가비지 수집 언어를 사용하면 객체에 대한 전역적인 강력한 참조를 도입하므로 메모리 누수의 원인이 될 수 있습니다.

구현

Java에서는 인스턴스에 해당하는 유형의 값과 함께 열거 유형을 사용하여 multiton 패턴을 구현할 수 있습니다. 단일 값이 있는 열거 형의 경우 singleton 패턴을 제공합니다.

C#에서는 다음 예제와 같이 열거 형을 사용할 수도 있습니다.

using System.Collections.Generic;

public enum MultitonType {
    Zero,
    One,
    Two
};

public class Multiton {
    private static readonly IDictionary<MultitonType, Multiton> instances =
        new Dictionary<MultitonType, Multiton>();
    private int number;

    private Multiton(int number) {
        this.number = number;
    }

    public static Multiton getInstance(MultitonType type) {
        // Lazy init (not thread safe as written)
        // Recommend using Double Check Locking if needing thread safety
        if (!instances.ContainsKey(type)) {
            instances.Add(type, new Multiton((int)type));
        }
        return instances[type];
    }

    public override string toString() {
        return "My number is " + number.ToString();
    }

    // Sample usage
    public static void main(string[] args) {
        Multiton m0 = Multiton.GetInstance(MultitonType.Zero);
        Multiton m1 = Multiton.GetInstance(MultitonType.One);
        Multiton m2 = Multiton.GetInstance(MultitonType.Two);
        System.Console.WriteLine(m0);
        System.Console.WriteLine(m1);
        System.Console.WriteLine(m2);
    }
}
반응형
반응형

출처 : https://stackoverflow.com/questions/43995886/gradle-could-not-target-platform-java-se-8-using-tool-chain-jdk-7-1-7/44000209

Gradle - Could not target platform: 'Java SE 8' using tool chain: 'JDK 7 (1.7)'

저는 Intellij Idea에서 Gragle 프로젝트를 불러오려고(import) 시도하였고 다음과 같은 메세지가 나왔습니다. Gradle - Could not target platform: 'Java SE 8' using tool chain: 'JDK 7 (1.7)' 누구든지 이렇게 나온 이유가 무엇인지 설명해 주시겠습니까?


7 개의 답변 중 1 개의 답변만 추려냄.

저는 이렇게 해결 하였습니다. (Intellij Idea 2018.1.2)

1) 파일-> 설정-> 빌드, 실행, 배포-> 빌드 도구-> Gradle로 이동합니다.
File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle

2) Gradle JVM : 버전 1.8로 변경

3) Gradle 작업 다시 실행

반응형
반응형

출처 및 응용

데모 소스

JNA로 Java에서 C++ 클래스 호출하기

저는 Java에서 C++ 클래스를 호출하고 싶어서 최근에 스스로 방법을 찾았습니다. 저는 C++ 라이브러리를 직접 호출하고 싶었습니다.

저는 진행하기 전에 이를 Java로 하기 위한 다양한 방법이 있다는 것을 말하고 싶습니다. 그리고 저는 작동한 것 중 하나를 선택하였습니다. 다른 기술도 사용 가능하며 어떤 기술이 '최상'인지에 대한 의견은 상당히 분분해 보입니다.

C++ 클래스로 시작하기 위해 평범하게 작성하였습니다.

#include <iostream>

// A simple class with a constuctor and some methods...
// 생성자와 몇 개의 메소드를 가지는 간단한 클래스...

class Foo
{
    public:
        Foo(int);
        void bar();
        int foobar(int);
    private:
        int val;
};

Foo::Foo(int n)
{
    val = n;
}

void Foo::bar()
{
    std::cout << "Value is " << val << std::endl;
}

int Foo::foobar(int n)
{
    return val + n;
}

JNA에서는 C++을 사용할 수 없기 때문에 C++ 코드 주변에 C wrapper를 놓을 것입니다. 이를 하기 위해 파일 제일 밑에 다음 부분에 코드를 추가합니다.

// ctypes는 C와만 대화할 수 있기 때문에 C++ 클래스를 위한 C 함수를 정의합니다.
extern "C"
{
    Foo* Foo_new(int n) {return new Foo(n);}
    void Foo_bar(Foo* foo) {foo->bar();}
    int Foo_foobar(Foo* foo, int n) {return foo->foobar(n);}
}

호출하기 원하는 각 메소드를 클래스 기반이 아닌 이름으로 제공해야 함을 알아두세요.
우리는 우리 코드에서 libfoo.so 파일을 빌드해야 합니다.
다음을 쉘에서 입력하세요.

$ g++ -c -fPIC foo.cpp -o foo.o
$ g++ -shared -W1,-soname,libfoo.so -o libfoo.so foo.o 

gradle에서 JNA를 fat-jar로 컴파일하기 위해 빌드 스크립트를 만들었습니다.
gradle 3.4.1에서 잘 작동함을 확인하였습니다.

apply plugin: 'java'
apply plugin: 'com.github.johnrengelman.shadow'

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
    }
}

repositories {
 mavenCentral()
}

dependencies {
  compile 'net.java.dev.jna:jna:4.1.0'
  compile 'net.java.dev.jna:jna-platform:4.1.0'
}

jar {
   finalizedBy shadowJar
   manifest {
      attributes 'Main-Class': 'hello.Foo',
                 'Implementation-Title': 'Foo jna Project',
                 'Implementation-Version': '1.0'
   }
}

자바 소스는 src/main/java/hello/Foo.java 로 작성하였습니다.

jna_demo$ vi src/main/java/hello/Foo.java
package hello;

import com.sun.jna.Native;
import com.sun.jna.Platform;
import com.sun.jna.Library;
import com.sun.jna.Pointer;

public class Foo {
    public interface FooLib extends Library {
        Pointer Foo_new(int n);
        void Foo_bar(Pointer foo);
        int Foo_foobar(Pointer foo, int n);
    }

    private String sopath;
    private FooLib INSTANCE;
    private Pointer self;

    private void loadLibrary(int n) {
        INSTANCE = (FooLib)Native.loadLibrary(
            sopath, FooLib.class
        );
        self = INSTANCE.Foo_new(n);
    }

    public Foo(int n) {
        sopath = "libfoo.so";
        loadLibrary(n);
    }
    public Foo(String sopath, int n) {
        this.sopath = sopath;
        loadLibrary(n);
    }

    public void bar() {
        INSTANCE.Foo_bar(self);
    }

    public int foobar(int n) {
        return INSTANCE.Foo_foobar(self, n);
    }

    public static void main(String[] args) {
        String path = System.getProperty("user.dir") + "/libfoo.so";
        System.out.println(path);
        Foo f = new Foo(path,5);

        f.bar();

        System.out.println("f.foobar(7) = " + f.foobar(7));

        int x = f.foobar(2);
        System.out.println("x = " + x);
    }
}

gradle로 Java 소스를 build 합니다.

jna_demo$ gradle build

빌드를 하면 build/libs 경로에 2개 파일이 생성됩니다.

  • content.jar: 원래 내 프로젝트
  • content-all.jar: JNA 라이브러리가 포함된 Fat-JAR 프로젝트
jna_demo$ ls -lrt build/libs/*.jar
-rw-r--r-- 1 root root    1878 May 19 08:33 build/libs/content.jar
-rw-r--r-- 1 root root 1606286 May 19 08:33 build/libs/content-all.jar

다음은 실행 결과입니다.

jna_demo$ java -jar build/libs/jna_demo-all.jar
/home/sdrlurker/jna_demo/libfoo.so
Value is 5
f.foobar(7) = 12
x = 7
반응형
반응형

출처 : https://stackoverflow.com/questions/21524642/splitting-string-with-pipe-character

파이프 글자로 문자열 분리하기("|")

저는 이 문자열을 값으로 분리할 수 없습니다.

"Food 1 | Service 3 | Atmosphere 3 | Value for money 1 "

저의 현재 코드입니다.

String rat_values = "Food 1 | Service 3 | Atmosphere 3 | Value for money 1 ";
String[] value_split = rat_values.split("|");

출력

[, F, o, o, d, , 1, , |, , S, e, r, v, i, c, e, , 3, , |, , A, t, m, o, s, p, h, e, r, e, , 3, , |, , V, a, l, u, e, , f, o, r, , m, o, n, e, y, , 1, ]

기대하는 출력

Food 1
Service 3
Atmosphere 3
Value for money 1

5개의 답변 중 1개의 답변만 추려냄

|는 정규식에서 메타글자입니다. 당신은 파이프를 escape 할 필요가 있습니다.

String[] value_split = rat_values.split("\\|");
반응형
반응형

출처

https://stackoverflow.com/questions/13297207/is-it-valid-to-compare-a-double-with-an-int-in-java

Java에서 double과 int를 비교하는 것이 유효합니까?

Utilities.getDistance(uni, enemyuni) <= uni.getAttackRange()

Utilities.getDistance는 double을 리턴하고 getAttackRange는 int를 리턴합니다. 위의 코드는 if 구문의 한 부분이고 이는 true가 되어야 합니다. 이 비교가 유효합니까?

감사합니다.

--

5개 답변중 1개의 답변만 추려냄.

예 유효합니다. 비교하기 전에 int를 double로 변환(promote)합니다.

JLS 섹션 5.6.2 (이진 숫자 변환) 의 링크에 있는 JLS 섹션 15.20.1 (숫자 비교 연산) 를 확인해 보세요.

뒷부분을 발췌하면

primitive의 넓은 타입의 변환은 다음 규칙에 의해 정의된 대로 두 피연산자에 변환이 적용됩니다.
* 만약 한 피연산자 type이 double이면 다른 하나는 double로 변환됩니다.
* ...

--

원본

https://github.com/SDRLurker/TIL/blob/master/scala/01/compare.md

반응형
반응형

출처 

http://stackoverflow.com/questions/7872846/how-to-read-from-standard-input-non-blocking

자바에서 표준 입력을 non-blocking으로 읽는 방법?

    long end=System.currentTimeMillis()+60*10;
    InputStreamReader fileInputStream=new InputStreamReader(System.in);
    BufferedReader bufferedReader=new BufferedReader(fileInputStream);
    try
    {
        while((System.currentTimeMillis()<end) && (bufferedReader.readLine()!=null))
        {

        }
        bufferedReader.close();
    }
    catch(java.io.IOException e)
    {
        e.printStackTrace();
    }

저는 600밀리초 안에 읽기 위해 위에처럼 시도하였습니다만 bufferedReader가 blocking되어 readline에서 읽기가 되지 않습니다. 제발 도와주세요.

----

5 개의 답변 중 1개의 답변만 추려냄.

역자주 : available() 메소드가 Java 1.5에는 없어서 ready() 메소드를 사용하여 문제를 해결하였습니다.

long end=System.currentTimeMillis()+60*10;
InputStreamReader fileInputStream = new InputStreamReader(System.in);
BufferedReader bufferedReader = new BufferedReader(fileInputStream);
try {
    while ((System.currentTimeMillis() < end)) {
        if (bufferedReader.ready()) {
            System.out.println(bufferedReader.readLine());
        }
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
        if (bufferedReader != null) {
            bufferedReader.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}


반응형

+ Recent posts