728x90
반응형

Pseudo 컬럼을 이용한 변경 체크

기존의 테이블에 컬럼 추가 없이 Pseudo 컬럼을 이용한 방법을 사용할 수 있다.

이는 10g에서의 SCN 번호를 가져올 수 있는 ORA_ROWSCN 함수를 사용해서 체크하는 방법을 이용하면 간단히 구현될 수 있다. 10g에서는 SCN 값의 변경사항 중 하나가 종래의 block level SCN에서 Row level SCN을 지원하게 되었다는 것이다(SCN은 System Change Number 또는 System Commit Number라고 병행해서 사용된다. 이 값은 커밋시마다 부여되는 오라클의 내부시계와 같은 역할을 수행한다).


기존에 BLOCK 레벨로 부여하던 SCN 값이 로우 레벨에 따라 다른 번호를 가질 수 있게 된 것이다. 따라서 이러한 ora_ rowscn 값을 이용해 테이블의 로우가 언제 변경되었는지에 대한 정보도 뽑아볼 수 있다. 그러나 모든 테이블에 다 적용되는 것이 아닌 ROW LEVEL SCN을 적용하게끔 테이블 생성시 ROW DEPENDENCIES 옵션으로 생성해야 한다는 것이다.

728x90
반응형
728x90
반응형

오라클 DB에 접속하는 Java 프로그램을 컴파일 하거나, 실행할 때 Unsupported majar.minor version 에러가 발생할 수 있습니다.

 

$ javac jdbcTest_CNT.java
warning: oracle/jdbc/driver/OracleDriver.class(oracle/jdbc/driver:OracleDriver.class): major version 51 is newer than 50, the highest major version supported by this compiler.
It is recommended that the compiler be upgraded.
1 warning
$ java jdbcTest_CNT
Exception in thread "main" java.lang.UnsupportedClassVersionError: oracle/jdbc/driver/OracleDriver : Unsupported major.minor version 51.0
       at java.lang.ClassLoader.defineClass1(Native Method)
       at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
       at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
       at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
       at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
       at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
       at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
       at java.security.AccessController.doPrivileged(Native Method)
       at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
       at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
       at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
       at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
       at jdbcTest_CNT.main(jdbcTest_CNT.java:56)

 

원인은 Oracle JDBC 버전과 JDK 버전, Java 버전이 서로 다르기 때문입니다.

 

예를 들어,

아래와 같이 ClassPath 에는 ojdbc7.jar 파일을 사용하도록 설정해놓고,

컴파일 or 실행할 때는 Java 6 버전(1.6 버전)을 쓰게 되면 이런 에러가 발생합니다.

 

export CLASSPATH=.:$ORACLE_HOME/jdbc/lib/ojdbc7.jar
$ javac -version
javac 1.6.0_37

$ java -version
java version "1.6.0_37" Java(TM) SE Runtime Environment (build 1.6.0_37-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.12-b01, mixed mode)

 

이런 경우, JDK, Java 버전을 1.7 로 똑같이 맞춰주던가,

아니면, $ORACLE_HOME/jdbc/lib/ 디렉토리에 가보면, ojdbc6.jar 파일이 있습니다.

이걸 ClassPath 에 잡아주면 에러없이 잘 동작합니다.

 

오라클 DB버전별로 지원되는 JDBC 버전과 JDK 버전이 있습니다. 버전에 맞는 걸 정확히 확인하고 사용하는 것이 바람직합니다.

 

 

728x90
반응형
728x90
반응형

JDK 8u161 이전 버전을 사용중이라면 AES-256 암호화 작업중에 다음 예외가 발생할 것이다.

java.security.InvalidKeyException: Illegal key size or default parameters

이전 버전의 JDK를 사용시,

1) https://www.oracle.com/index.html

메뉴에 검색 JCE라고 검색을 하면 JDK 버전별로 다운로드를 할 수 있다.

 

또는

 

2) https://www.oracle.com/java/technologies/downloads/archive/

Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 링크 클릭!!

 

자신의 JDK 버전에 맞게 다운로드 받은 후, 압축을 풀면 local_policy.jar US_export_policy.jar 2개의 jar 파일이 있다.

 

이 2개의 파일을 $JAVA_HOME/jre/lib/security 아래에 local_policy.jar, US_export_policy.jar 파일을 바꾸어 주면 된다.

ex) 

C:\Program Files\Java\jdk1.7.0_80\jre\lib\security

728x90
반응형
728x90
반응형

java에서 sftp를 사용하기 위해서는 apache common vfs를 사용하거나, jsch 사용하는 방법이 있다.

jsch 라이브러리를 사용하면 쉽게 개인키나 패스워드를 사용하여 sftp에 접속할 수 있고, 업로드나 다운로드를 할 수 있다.

 

maven repository

https://mvnrepository.com/artifact/com.jcraft/jsch

 

개인키, 공개키 생성

sftp 접속 시 아이디와 패스워드를 사용하지 않고, 아이디와 개인키, 공개키를 사용하면 보안에 좀 더 좋을 수 있다.

(aws의 s3 ftp 사용시에도 키 파일을 이용하여 접속하라고 권고하고 있다.)

 

1. 접속하려는 클라이언트가 되는 서버에서 아래 명령으로 개인키, 공개키를 생성한다.

ssh-keygen -P "" -m PEM -f  원하는키명

(윈도우, 리눅스 동일)

위 명령 실행 시 파일이 2개가 생성되는데, 생성된 키는 아래와 같다.

원하는키명 <- 개인키

원하는키명.pub <- 공개키

 

끝에 .pub가 붙은 파일이 공개키 파일이다.

 

접속하려는 서버 담당자에게 공개키 파일을 전달하고, 개인키는 sftp 접속 시 사용해야 하므로 잘 보관해둔다.

 

JSch 접속 예제 소스

package com.tistory.hitomis.util;

import com.jcraft.jsch.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;

public class SFTPUtil {

    private Session session = null;
    private Channel channel = null;
    private ChannelSftp channelSftp = null;

    /**
     * 서버와 연결에 필요한 값들을 가져와 초기화 시킴
     *
     * @param host 서버 주소
     * @param userName 아이디
     * @param password 패스워드
     * @param port 포트번호
     * @param privateKey 개인키
     */
    public void init(String host, String userName, String password, int port, String privateKey) {

        JSch jSch = new JSch();

        try {
            if(privateKey != null) {//개인키가 존재한다면
                jSch.addIdentity(privateKey);
            }
            session = jSch.getSession(userName, host, port);

            if(privateKey == null && password != null) {//개인키가 없다면 패스워드로 접속
                session.setPassword(password);
            }

            // 프로퍼티 설정
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no"); // 접속 시 hostkeychecking 여부
            session.setConfig(config);
            session.connect();
            //sftp로 접속
            channel = session.openChannel("sftp");
            channel.connect();
        } catch (JSchException e) {
            e.printStackTrace();
        }

        channelSftp = (ChannelSftp) channel;
    }

    /**
     * 디렉토리 생성
     *
     * @param dir 이동할 주소
     * @param mkdirName 생성할 디렉토리명
     */
    public void mkdir(String dir, String mkdirName) {
        if (!this.exists(dir + "/" + mkdirName)) {
            try {
                channelSftp.cd(dir);
                channelSftp.mkdir(mkdirName);
            } catch (SftpException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 디렉토리( or 파일) 존재 여부
     * @param path 디렉토리 (or 파일)
     * @return
     */
    public boolean exists(String path) {
        Vector res = null;
        try {
            res = channelSftp.ls(path);
        } catch (SftpException e) {
            if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                return false;
            }
        }
        return res != null && !res.isEmpty();
    }

    /**
     * 파일 업로드
     *
     * @param dir 저장할 디렉토리
     * @param file 저장할 파일
     * @return 업로드 여부
     */
    public boolean upload(String dir, File file) {
        boolean isUpload = false;
        SftpATTRS attrs;
        FileInputStream in = null;
        try {
            in = new FileInputStream(file);
            channelSftp.cd(dir);
            channelSftp.put(in, file.getName());

            // 업로드했는지 확인
            if (this.exists(dir +"/"+file.getName())) {
                isUpload = true;
            }
        } catch (SftpException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return isUpload;
    }

    /**
     * 파일 다운로드
     *
     * @param dir 다운로드 할 디렉토리
     * @param downloadFileName 다운로드 할 파일
     * @param path 다운로드 후 로컬에 저장될 경로(파일명)
     */
    public void download(String dir, String downloadFileName, String path) {
        InputStream in = null;
        FileOutputStream out = null;
        try {
            channelSftp.cd(dir);
            in = channelSftp.get(downloadFileName);
        } catch (SftpException e) {
            e.printStackTrace();
        }

        try {
            out = new FileOutputStream(new File(path));
            int i;

            while ((i = in.read()) != -1) {
                out.write(i);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 연결 종료
     */
    public void disconnection() {
        channelSftp.quit();
        session.disconnect();
    }
}

 

사용방법 예제 소스

package com.tistory.hitomis;

import com.tistory.hitomis.util.SFTPUtil;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 테스트를 위한 클래스
 */
public class JavaTest {

    public static void main(String[] args) {

        final SFTPUtil sftpUtil = new SFTPUtil();

        final String host = "접속할 서버 아이피";
        final String userName = "접속할 아이디";
        final int port = 22;
        final String uploadPath = "업로드경로";
        final String downloadPath = "다운로드경로";
        final String privateKey = "개인키경로/파일명";
        
        // 업로드 시 업로드 폴더 아래에
        // 현재 날짜 년월일을 생성하고 그 아래 올리기 위한 날짜 변수
        final Date today = new Date();
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");

		// 접속
        sftpUtil.init(host, userName, null, port, privateKey);

        // 업로드 테스트
        File uploadfile = new File("업로드할 경로+파일명"); // 파일 객체 생성
       
        String mkdirPath = sdf.format(today); //현재날짜 년월일
        sftpUtil.mkdir(uploadPath, mkdirPath); // 업로드경로에 현재날짜 년월일 폴더 생성
        boolean isUpload = sftpUtil.upload(uploadPath+mkdirPath, uploadfile); //업로드
        System.out.println("isUpload -" + isUpload); // 업로드 여부 확인

        /* 다운로드 테스트 */
        sftpUtil.download(downloadPath, "다운로드파일명", "로컬에저장할경로+파일명");
        File downloadFile = new File("로컬에저장할경로+파일명");
        if (downloadFile.exists()) {
            System.out.println("다운로드 완료");
            System.out.println(downloadFile.getPath());
            System.out.println(downloadFile.getName());
        }

        // 업로드 다운로드 수행 후 꼭 연결을 끊어줘야 한다!!
        sftpUtil.disconnection();
    }
}

 

728x90
반응형
728x90
반응형

sftp를 활용하기 위해서는 JSCH 라이브러리가 필요하다.

라이브러리 버전은 자신에게 알맞는 버전을 받도록 하자.

 

* maven 형태

<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>

* gradle 형태

// https://mvnrepository.com/artifact/com.jcraft/jsch
compile group: 'com.jcraft', name: 'jsch', version: '0.1.55'

 

사용법은 어렵지 않다. 오히려 파일을 다운받는 기능이나 아니면 명령을 전달해서 결과를 받는 것이 더 복잡하다.

세션을 열어주고 세션 객체를 가져온다.

import java.time.LocalDateTime;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpProgressMonitor;

public class SFTPSender {
    private String ADDRESS = "접속할주소";
    private int PORT = 22;  //포트번호
    private String USERNAME = "아이디";
    private String PASSWORD = "비밀번호";
    private static Session session = null;
    private static Channel channel = null;
    private static ChannelSftp channelSftp = null;

    public void sshAccess() throws Exception {
        JSch jsch = new JSch();
        session = jsch.getSession(USERNAME, ADDRESS, PORT);  //세션 오픈!
        session.setPassword(PASSWORD);
        java.util.Properties config = new java.util.Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        session.connect();
    }
}

 

위 sshAccess 메소드가 세션객체를 가져오는 부분이다.

해당 세션이 열리게 되면 세션에서 어떠한 체널을 사용 할 지 결정을 하고 파일을 전송하는 메소드만 호출하면 된다.

아래 메소드에서 간단하게 put 메소드만 호출하면 파일을 원하는 경로까지 잘 배달하여준다.

물론 아래 코드는 Exception을 던지고 있기 때문에 try - catch - finally 로 바꾸어 커넥션을 사용하지 않는다면 닫아주는 코드로 바꾸어야한다.

    //..생략
    public void sendFileToOtherServer(String sourcePath, String destinationPath) throws Exception {
        channel = session.openChannel("sftp");
        channel.connect();
        channelSftp = (ChannelSftp) channel;
        channelSftp.put(sourcePath, destinationPath);  //파일을 전송하는 메소드
        channelSftp.disconnect();
        channel.disconnect();
    }	 

 

재미있는 기능 중 1가지는 SftpProgressMonitor 라는 인터페이스를 통해서 파일전송 진행현황(프로그래스, Progress)을 볼 수 있다.

//SftpProgressMonitor 인터페이스 정의 모습 ----
package com.jcraft.jsch;

public interface SftpProgressMonitor{
  public static final int PUT=0;
  public static final int GET=1;
  public static final long UNKNOWN_SIZE = -1L;
  void init(int op, String src, String dest, long max);
  boolean count(long count);
  void end();
}

 

위 인터페이스를 상속받아서 파일을 전송하는 put 메소드의 3번째 인자값으로 전달해주면 동작한다.

    //..생략
    public void sendFileToOtherServer(String sourcePath, String destinationPath) throws Exception {
        channel = session.openChannel("sftp");
        channel.connect();
        channelSftp = (ChannelSftp) channel;
        channelSftp.put(sourcePath, destinationPath, new SftpProgressMonitor() {
            private long max = 0;  //최대
            private long count = 0;  //계산을 위해 담아두는 변수
            private long percent = 0;  //퍼센트
            @Override
            public void init(int op, String src, String dest, long max) {  //설정
                this.max = max;
            }
            @Override
            public void end() {
                //종료시 할 행동
            }
            @Override
            public boolean count(long bytes) {
                this.count += bytes;  //전송한 바이트를 더한다.
                long percentNow = this.count*100/max;  //현재값에서 최대값을 뺀후
                if(percentNow>this.percent){  //퍼센트보다 크면
                    this.percent = percentNow;
                    System.out.println("progress : " + this.percent); //Progress
                }
                return true;//기본값은 false이며 false인 경우 count메소드를 호출하지 않는다.
            }
        });
        channelSftp.disconnect();
        channel.disconnect();
    }	 

 

최종 클래스 모습이다.

import java.time.LocalDateTime;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpProgressMonitor;

public class SFTPSender {
    private String ADDRESS = "접속할주소";
    private int PORT = 22;  //포트번호
    private String USERNAME = "아이디";
    private String PASSWORD = "비밀번호";
    private static Session session = null;
    private static Channel channel = null;
    private static ChannelSftp channelSftp = null;


    public static void main(String[] args) {
        System.out.println("start : " + LocalDateTime.now());
        SFTPSender sender = new SFTPSender();

        try {
            sender.sshAccess();
        } catch (Exception e) {
            e.printStackTrace();
        }
		
        if(session != null){
            try {
                sender.sendFileToOtherServer("D:/보낼파일.txt", "/home/user/test/받을파일.txt");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("end : "  + LocalDateTime.now());
        System.exit(0);
    }



    public void sshAccess() throws Exception {
        JSch jsch = new JSch();
        session = jsch.getSession(USERNAME, ADDRESS, PORT);  //세션 오픈!
        session.setPassword(PASSWORD);
        java.util.Properties config = new java.util.Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        session.connect();
    }
    public void sendFileToOtherServer(String sourcePath, String destinationPath) throws Exception {
        channel = session.openChannel("sftp");
        channel.connect();
        channelSftp = (ChannelSftp) channel;
        channelSftp.put(sourcePath, destinationPath);  //파일을 전송하는 메소드
        channelSftp.disconnect();
        channel.disconnect();
    }	 

    public void sendFileToOtherServer(String sourcePath, String destinationPath) throws Exception {
        channel = session.openChannel("sftp");
        channel.connect();
        channelSftp = (ChannelSftp) channel;
        channelSftp.put(sourcePath, destinationPath, new SftpProgressMonitor() {
            private long max = 0;  //최대
            private long count = 0;  //계산을 위해 담아두는 변수
            private long percent = 0;  //퍼센트
            @Override
            public void init(int op, String src, String dest, long max) {  //설정
                this.max = max;
            }
            @Override
            public void end() {
                //종료시 할 행동
            }
            @Override
            public boolean count(long bytes) {
                this.count += bytes;  //전송한 바이트를 더한다.
                long percentNow = this.count*100/max;  //현재값에서 최대값을 뺀후
                if(percentNow>this.percent){  //퍼센트보다 크면
                    this.percent = percentNow;
                    System.out.println("progress : " + this.percent); //Progress
                }
                return true;//기본값은 false이며 false인 경우 count메소드를 호출하지 않는다.
            }
        });
        channelSftp.disconnect();
        channel.disconnect();
    }	 
}

 

sftp로 파일을 전송하기 때문에 용량이 큰 파일(2GB 이상) 같은 파일도 전송이 가능하다.

실제 동작모습

 

참조 : https://lts0606.tistory.com/256

728x90
반응형

+ Recent posts