질문/답변 페이지 많이 이용해 주세요.

Archive for the ‘Development’ Category.

조직도 JavaScript 실험. recursive function. 연습.

커뮤니티에 javascript를 사용하여, 조직도를 구현하는 것에 대한 질문이 올라왔다.
그래서, 나는 프로그래밍 연습을 하기 위해서, 한 번 만들어보기로 결심하고, 답변을 올렸다.

https://okky.kr/article/389606

올리자마자 다시 봐도, 코드 상에 잘못된 부분은 보이지만, 그런 것들은 나중에 시간날지 고칠지 하고, 우선 코드를 내 블로그에 복사해 놓는다.

var depth1 = [];
var depth2 = [];
var depth3 = [];
var depth4 = [];

depth1.push(
    {
        GROUP_ID: 1,
        GROUPNAME: '본사',
	children: []
    },
    {
        GROUP_ID: 2,
        GROUPNAME: '지사',
	children: []
    }
);

depth2.push(
    {
        GROUP_ID: 10,
        GROUP_ID_P: 1,
        GROUPNAME: '기술지원본부',
	children: []
    },
    {
        GROUP_ID: 20,
        GROUP_ID_P: 1,
        GROUPNAME: '연구소',
	children: []
    },
    {
        GROUP_ID: 30,
        GROUP_ID_P: 2,
        GROUPNAME: '영업부',
	children: []
    }
);

console.log(depth1);
console.log("========================================");
console.log(depth2);
console.log("========================================");

/*
var datasource = { children: [] };

for(var i=0; i < depth1.length; i++){
	datasource['children'].push({'name': depth1[i]['GROUPNAME'], 'gid': depth1[i]['GROUP_ID'],'children': []});
	for(var j=0; j < depth2.length; j++){
		if(depth1[i]['GROUP_ID'] == depth2[j]['GROUP_ID_P']){
			datasource['children'][i]['children'].push({'name': depth2[j]['GROUPNAME'], 'gid': depth2[j]['GROUP_ID'], 'children':[]});
			for(var k=0; k < depth3.length; k++){
				if(depth2[j]['GROUP_ID'] == depth3[k]['GROUP_ID_P']){
					for(var l=0; l < datasource['children'][i]['children'].length; l++){
						if(depth2[j]['GROUP_ID'] == datasource['children'][i]['children'][l]['gid']){
							datasource['children'][i]['children'][l]['children'].push({'name': depth3[k]['GROUPNAME'], 'gid': depth3[k]['GROUP_ID'], 'children':[]});
							for(var m=0; m < depth4.length; m++){
								if(depth3[k]['GROUP_ID'] == depth4[m]['GROUP_ID_P']){
									for(var n=0; n < datasource['children'][i]['children'][l]['children'].length; n++){
										if(depth3[k]['GROUP_ID'] == datasource['children'][i]['children'][l]['children'][n]['gid']){
											datasource['children'][i]['children'][l]['children'][n]['children'].push({'name': depth4[m]['GROUPNAME'], 'gid': depth4[m]['GROUP_ID'], 'children':[]});
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

console.log("===== FINAL =====");
console.log(JSON.stringify(datasource));
*/

/**
 * tree에서 gid를 가지는 객체를 반환한다.
 * 못 찾으면 undefined를 반환한다.
 */
function findById(tree, gid) {

	//console.log("========================================");
	//console.log(new Error().stack);
	//console.log("========================================");

	if ( tree['GROUP_ID'] == gid ) {
		return tree;
	} else if ( tree.children.length == 0 ) {
		return undefined;
	} else {
		for ( var i = 0 ; i < tree.children.length ; i ++ ) {
			var found = findById(tree.children[i], gid);
			if ( found !== undefined ) {
				return found;
			}
		}
		return undefined;
	}
}

var ROOT = {
	'GROUP_ID': 0,
	'GROUPNAME': 'ROOT',
	'children': []
}

depth1 = depth1.concat(depth2);
for ( var i = 0 ; i < depth1.length ; i ++ ) {
	if ( depth1[i]['GROUP_ID_P'] === undefined ) {
		ROOT.children.push(depth1[i]);
	} else {
		var parent = findById(ROOT, depth1[i]['GROUP_ID_P']);
		if ( parent !== undefined ) {
			parent.children.push(depth1[i]);
		}
	}
}

console.log("FINAL");

console.log(JSON.stringify(ROOT));

tomcat parallel deployment

톰캣이 이미 8.0까지 나온 시점에 인터넷에서 흥미를 끄는 내용을 하나 접했습니다.

parallel deployment라는 제목에서는

전략을 얘기하는 것인 줄 알았는데, 기능이었습니다.

tomcat 7.0 부터 지원하는 기능으로 무중단 배포를 할 수 있는 기능입니다.

즉, app v1 운용중에 app v2를 배포할 수 있고,
기존 세션 사용자는 v1을 사용하고 신규 접속자는 v2로 접근하게 됩니다.

저는 weblogic에서 production redeployment로 접했던 기능입니다.

상세내용은 위 링크에서 참조하십시오.

‘Objective-C for absolute Beginners’를 봅니다.

2013-002 – 2013/04/30 – Objective-C for absolute Beginners – Gary Bennett 외 2 – 공학 – 전자책 – 링크 – 읽는중

gcc로 몇가지 연습 -c -S -E

gcc로 c언어 컴파일을 몇 가지 실습해 보았습니다.

-c 옵션

gcc 명령에 -c 옵션을 주면, .c파일을 컴파일만하고 링크를 하지 않습니다. 결과물로는 .o 파일이 생성됩니다.

test.c 파일

int test(int a, int b) {
    return a + b;
}

int main(int argc, char* argv[]) {
    test(3,4);
    return 0;
}
[dgkim@dgkim asm]$ ls -al
합계 12
drwxr-xr-x   2 dgkim users 4096 2013-01-03 19:37 .
drwxrwxrwt. 18 root  root  4096 2013-01-03 19:34 ..
-rw-r--r--   1 dgkim users  112 2013-01-03 18:12 test.c
[dgkim@dgkim asm]$ gcc -c test.c
[dgkim@dgkim asm]$ ls -l
합계 8
-rw-r--r-- 1 dgkim users  112 2013-01-03 18:12 test.c
-rw-r--r-- 1 dgkim users 1464 2013-01-03 19:37 test.o
[dgkim@dgkim asm]$

-c 옵션으로 생성된 .o 파일을 gcc를 통해 링크하려면 아래와 같이 할 수 있습니다.
여러 개의 .c 파일을 가지는 프로그램 및 링크 과정 및 순서에 따라, -c 옵션으로 컴파일만하고, 마지막에 .o 파일들을 묶는 일은 자주 있을 것입니다.

[dgkim@dgkim asm]$ gcc -o test test.o
[dgkim@dgkim asm]$ ls -l
합계 16
-rwxr-xr-x 1 dgkim users 6379 2013-01-03 19:39 test
-rw-r--r-- 1 dgkim users  112 2013-01-03 18:12 test.c
-rw-r--r-- 1 dgkim users 1464 2013-01-03 19:37 test.o
[dgkim@dgkim asm]$

-S 옵션

gcc에서 -S 옵션을 주고 실행하면, .c파일에서 .s파일, 즉 어셈블러 소스가 생성됩니다.

[dgkim@dgkim asm]$ ls -al
합계 28
drwxr-xr-x   2 dgkim users 4096 2013-01-03 19:41 .
drwxrwxrwt. 18 root  root  4096 2013-01-03 19:39 ..
-rwxr-xr-x   1 dgkim users 6379 2013-01-03 19:39 test
-rw-r--r--   1 dgkim users  112 2013-01-03 18:12 test.c
-rw-r--r--   1 dgkim users 1464 2013-01-03 19:37 test.o
-rw-r--r--   1 dgkim users  807 2013-01-03 19:41 test.s
[dgkim@dgkim asm]$

위의 간단하고 짧던 .c 파일은 아래와 같은 복잡한 .s 파일을 만들었습니다.

        .file   "test.c"
        .text
.globl test
        .type   test, @function
test:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)
        movl    -8(%rbp), %eax
        movl    -4(%rbp), %edx
        leal    (%rdx,%rax), %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   test, .-test
.globl main
        .type   main, @function
main:
.LFB1:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    %edi, -4(%rbp)
        movq    %rsi, -16(%rbp)
        movl    $4, %esi
        movl    $3, %edi
        call    test
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE1:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.6 20120305 (Red Hat 4.4.6-4)"
        .section        .note.GNU-stack,"",@progbits

-E 옵션

gcc에서 -E 옵션을 주면, preprocessor 처리가 된 c파일의 내용을 볼 수 있습니다.

preprocessor 처리를 위해서 test.c 파일에서 #define 사용하여 작은 수정을 한 결과입니다.

[dgkim@dgkim asm]$ cat test.c
#define a alpha
#define b bravo

int test(int a, int b) {
    return a + b;
}

int main(int argc, char* argv[]) {
    test(3,4);
    return 0;
}
[dgkim@dgkim asm]$ gcc -E test.c
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.c"



int test(int alpha, int bravo) {
    return alpha + bravo;
}

int main(int argc, char* argv[]) {
    test(3,4);
    return 0;
}
[dgkim@dgkim asm]$

ps. 다음에는 -m32 옵션을 통해서 x86_64 머신에서 x86 i686용 빌드를 테스트한 글을 올려볼까요? glibc-devel.i686
ps2. 그리고, gdb를 통해서 위 파일을 디버깅하였는데, 그 글도 올려볼까요?

파이어폭스에서 드래그앤드롭으로 파일업로드 구현하기

파이어폭스에서는 파일업로드를 드래그앤드롭으로 할 수 있습니다.

3.6 버전에서부터 지원했던 것 같습니다.

자바스크립트와 Spring 프레임워크를 사용해서 간단하게 파일 업로드 프로그램을 작성해 보았습니다.

HTML + JavaScript 업로드 구현부분 UI

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>파일업로드</title>
</head>
<body>
    <form name="form0" action="file/upload" method="POST" enctype="multipart/form-data">
        <ul>
            <li>파일 하나 업로드 : <input id="file1" type="file" name="file" /></li>
            <li>여러 파일 업로드 : <input id="file2" type="file" name="multifile" multiple /></li>
            <li>파일을 선택하고 '전송'을 눌러 업로드하시거나, 탐색기에서 파일을 드래그앤드롭하여 업로드할 수 있습니다. 드래그앤드롭은 '전송'을 누르는 것이 아니라 드롭시점에 바로 업로드됩니다.</li>
        </ul>
        <input type="submit" value="전송"/>
    </form>
    <script type="text/javascript">
    var dropzone1 = document.getElementById('file1');
    var dropzone2 = document.getElementById('file2');
    setDnDhandler(dropzone1);
    setDnDhandler(dropzone2);
    
    function setDnDhandler(obj) {
        obj.addEventListener("dragover", function(event) {
            event.preventDefault();
        }, true);
        obj.addEventListener("drop", function(event) {
            event.preventDefault();
            var allTheFiles = event.dataTransfer.files;
            for (var i=0; i<allTheFiles.length; i++) {
                var element = document.createElement('div');
                element.id = 'f' + i;
                document.body.appendChild(element);
                sendFile(allTheFiles[i], element.id);
            }
        }, true);
    }
    function sendFile(file, fileId) {
        var xhr = new XMLHttpRequest();
        
        xhr.upload.addEventListener("progress", function(e) {
                if (e.lengthComputable) {
                    var percentage = Math.round((e.loaded * 100) / e.total);
                    document.getElementById(fileId).innerHTML = file.name + ' - ' + percentage + '%';
                }
            }, false);
        xhr.onreadystatechange = function() {  
            if (xhr.readyState == 4 && xhr.status == 200) {
                alert(xhr.responseText);
            }
        };
        xhr.upload.addEventListener("load", function(e){
                document.getElementById(fileId).innerHTML = file.name + ' - uploaded';
            }, false);
        xhr.open("POST", "file/upload");
        var fd = new FormData();
        fd.append("file", file);
        xhr.send(fd);
    }
    </script>
</body>
</html>

드래그앤드롭을 사용할 때, 드롭될 영역의 경우는 input형이 아니라도 가능하지만, 일반 HTML전송도 가능하게 예제를 작성하고자 input을 사용하였습니다.

드래그앤드롭을 지원하는 부분은 addEventListener 부분입니다. “dragover”, “drop” 이벤트에 대하여 기본동작을 막고, 자바스크립트처리를 할 수 있도록 하였습니다.

파이어폭스에서 input에 type이 file이면, 기본동작이 파일이 업로드를 위해 준비됩니다. IE의 경우는 그 동작조차 없으며, 파일 열기가 수행되지요.

drop이벤트에서 XMLHttpRequest를 통해 AJAX업로드를 합니다.

“xhr.upload.addEventListener(“progress”, …);” 부분은 업로드 처리 경과를 확인할 수 있는 기능입니다. 업로드된 양을 퍼센트로 출력해 줍니다.

서버에서 파일 받기

package test.control;

import java.io.File;
import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

@Controller
@RequestMapping("/file")
public class FileController {
    
    @RequestMapping("upload")
    public @ResponseBody String upload(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        if (! (request instanceof MultipartHttpServletRequest)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expected multipart request");
            return null;
        }
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        for ( String fileName : multipartRequest.getFileMap().keySet() ) {
            for ( MultipartFile file : multipartRequest.getFiles(fileName) ) {
                String originalFileName = file.getOriginalFilename();
                File destination = File.createTempFile("file", originalFileName, new File("/var/dgkim"));
            }
        }
        return "success";
    }
}

스프링 MVC 프레임워크를 통해, 저수준의 파일 처리부분은 빠지고 실제 파일명과 File객체 처리를 바로 할 수 있습니다.

위 예에서는 의미없이 임시 파일만 생성했으나, 필요에 따라 처리를 작성할 수 있겠습니다.

서버측 라이브러리는 Maven으로 관리하였으며, Spring 3.0외에 추가로 아래 의존성이 있습니다.

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3</version>
        </dependency>

소스 다운로드 : http://www.dgkim.net/dav/users/dgkim/fileuptest.zip

참조

웹로직의 보편적인 웹개발상의 디버그

웹로직을 사용하면서, 에러의 원인을 쉽게 찾을 수 없을 때, 원인을 추적하는 방법을 나열해 봅니다.

dispatcher forward 사용시 해당 리소스가 없을 경우 추적하기.( 알 수 없는 404 에러 )

웹개발 프레임워크를 사용할 때 주로 개발하는 형태가, controller를 작성하여 비즈니스 로직을 수행한 후, 사용자에게 보여질 페이지(JSP)로 RequestDispatcher.forward 하는 형태이리라 생각됩니다. ( 물론 다른 형태도 많겠지만, RequestDispatcher.forward 하는 형태를 가정한 상황으로 글을 씁니다. )

아래는 그렇게 사용한 java servlet 예제입니다.

RequestDispatcher rd = null;
rd = request.getRequestDispatcher("/main.jsp");
rd.forward(request, response);

이 경우 만약 main.jsp 파일이 존재하지 않는다면, 웹로직은 404 에러 페이지만 출력할 뿐입니다. 그러면 사용자는 호출한 URL이 존재하지 않는 것인지 forward 대상 페이지가 없는 것인지 알 수 없습니다.

tomcat 이나 다른 WAS의 경우는 주로 위 처리에 대한 에러가 로그파일 혹은 웹브라우저에 추적이 가능한 정보를 제공하는데, 웹로직은 404 에러코드만 줄 뿐 추가정보를 제공하지 않았습니다. ( 제 경험 )

이에 추적정보를 찾는 방법을 알았기에 공유합니다.

  1. 해당 서버를 선택합니다.
  2. 디버그 탭을 선택합니다.
  3. weblogic 항목을 펼칩니다.
  4. servlet 항목을 펼칩니다.
  5. DebugURLResolution 항목에 체크표합니다.
  6. 사용 버튼을 눌러 활성화 합니다.

위 과정을 거치면 아래와 같은 로그를 발견할 수 있습니다. 서버의 logs/서버명.log 파일에 기록됩니다.

####<2011. 6. 17 오후 4시 41분 10초 KST> <Debug> <URLResolution> <whity> <AdminServer> <[ACTIVE] ExecuteThread: '1' for queue: 'weblogic.kernel.Default (self-tuning)'> <<anonymous>> <> <> <1308296470912> <BEA-000000> <ServletContext@465470[app:test module:test path:/test spec-version:null]: resolving request with relUri: /main.jsp>
####<2011. 6. 17 오후 4시 41분 10초 KST> <Debug> <URLResolution> <whity> <AdminServer> <[ACTIVE] ExecuteThread: '1' for queue: 'weblogic.kernel.Default (self-tuning)'> <<anonymous>> <> <> <1308296470912> <BEA-000000> <ServletContext@465470[app:test module:test path:/test spec-version:null]: getResourceAsSource() couldn't find source for : /main.jsp>

위 로그 마지막 줄을 통해서 main.jsp 파일을 찾을 수 없다는 내용을 확인할 수 있습니다.

JDeveloper + WebLogic 환경에서 Remote Debugging 사용하기.

JDeveloper와 WebLogic을 사용하는 개발환경에서 Integrated Server를 쓰지 않고, 테스트 혹은 운용서버에 배포후 Remote Debugging을 하는 방법을 간략히 소개합니다.

1. 대상 서버의 Remote Debugging JVM 옵션 세팅.

먼저 JVM에서 Remote Debugging을 받아들이기 위한 설정을 추가합니다.
아래 세팅을 서버의 구동 스크립트에 포함되도록 합니다.
( 저는 편의상 개발서버의 .profile을 사용하고 있습니다. nodemanager 등을 사용중인 경우에는 콘솔에서 세팅 가능하겠습니다. )

# JDK 1.4 이하 기본 형태
# -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=[port]
# JDK 1.5 이상 기본 형태
# -agentlib:jdwp=transport=dt_socket,server=y,address=[port]
JAVA_OPTIONS=$JAVA_OPTIONS\
"-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"

2. 대상 서버의 Tunneling 설정.

weblogic 콘솔에서 대상 서버의 Protocols탭에 Enable Tunneling.을 체크합니다.

3. JDeveloper에서 대상 프로젝트에 Remote Debugging 설정.

개발중인 프로젝트에 Remote Debugging을 세팅하는 과정입니다.

  1. ‘Project Properties’에서 ‘Run/Debug/Profile’ 선택.
  2. ‘Run Configurations’에서 복제 혹은 기존 것을 편집.
  3. ‘Launch Settings’에서 ‘Remote Debugging’ 체크.
  4. ‘Tool Settings’ – ‘Debugger’ – ‘Remote’에 아래 설정 추가.
    • Protocol : Attach to JPDA
    • Host : 해당서버 주소
    • Port : 4000 1항에서 설정.

4. Remote Debugging 시작.

프로젝트에서 ‘Start Remote Debugger’를 선택하면, 브레이크포인트 등 원하는 디버깅을 수행할 수 있습니다.

WLST로 DataSource 생성하기.

이번에 웹로직으로 웹프로젝트를 하면서, JDBC 정보를 프로그램에 녹여볼 생각을 했습니다.

예전에 Oracle WAS의 경우 Oracle WAS Deployment Descriptor중 data-sources.xml을 활용하여 애플리케이션 배포에 JDBC정보를 넣어 배포를 했었는데, 웹로직은 약간 달랐습니다.( 그리고 웹로직의 버저닝을 사용하니 뭔가 좀 더 달라지는 듯 했습니다. )

그래서, 애플리케이션 소스에는 넣지 못하더라도, 배포 가능한 소스로 해보고자 방법을 찾던 중 WLST를 사용하는 방법의 예제를 찾았습니다.

그래서 그 방법으로 하기로 하고 아래와 같은 스크립트로 한번에 DataSource를 생성하였습니다.

import sys
from java.lang import System

# function definition BEGIN

# defining addJDBC function
def addJDBC(obj, servers):
    print("")
    print("*** Creating JDBC  ")
    
    # Create the Connection Pool.  The system resource will have
    # generated name of 'PoolName'+"-jdbc"
    
    myResourceName = obj["PoolName"]
    print("Here is the Resource Name: " + myResourceName)
    
    jdbcSystemResource = create(myResourceName,"JDBCSystemResource")
    myFile = jdbcSystemResource.getDescriptorFileName()
    print ("HERE IS THE JDBC FILE NAME: " + myFile)
    
    jdbcResource = jdbcSystemResource.getJDBCResource()
    jdbcResource.setName(obj["PoolName"])
    
    # Create the DataSource Params
    dpBean = jdbcResource.getJDBCDataSourceParams()
    myName=obj["JNDIName"]
    dpBean.setJNDINames([myName])
    
    # Create the Driver Params
    drBean = jdbcResource.getJDBCDriverParams()
    drBean.setPassword(obj["Password"])
    drBean.setUrl(obj["URLName"])
    drBean.setDriverName(obj["DriverName"])
    
    propBean = drBean.getProperties()
    driverProps = Properties()
    driverProps.setProperty("user",obj["UserName"])
    
    e = driverProps.propertyNames()
    while e.hasMoreElements() :
        propName = e.nextElement()
        myBean = propBean.createProperty(propName)
        myBean.setValue(driverProps.getProperty(propName))
        print myBean
        
        # Create the ConnectionPool Params
        ppBean = jdbcResource.getJDBCConnectionPoolParams()
        
        ppBean.setInitialCapacity(int(obj["InitialCapacity"]))
        ppBean.setMaxCapacity(int(obj["MaxCapacity"]))
        ppBean.setCapacityIncrement(int(obj["CapacityIncrement"]))
        
        if not obj["ShrinkPeriodMinutes"] == None:
            ppBean.setShrinkFrequencySeconds(int(obj["ShrinkPeriodMinutes"]))
        if not obj["TestTableName"] == None:
            ppBean.setTestConnectionsOnReserve(1)
            ppBean.setTestTableName(obj["TestTableName"])
        if not obj["LoginDelaySeconds"] == None:
            ppBean.setLoginDelaySeconds(int(obj["LoginDelaySeconds"]))

        # Adding KeepXaConnTillTxComplete to help with in-doubt transactions.
        xaParams = jdbcResource.getJDBCXAParams()
        xaParams.setKeepXaConnTillTxComplete(1)

        # Add Target
        
        for servername in servers:
            jdbcSystemResource.addTarget(getMBean("/Servers/" + servername))

# function definition END


print "@@@ Starting the script ..."


url = 't3://localhost:7001'
usr = 'weblogic'
password = 'welcome'
servernames = ['AdminServer', 'SSO1', 'SSO2']

connect(usr,password, url)

for servername in servernames:
    servermb=getMBean("Servers/" + servername)
    if servermb is None:
       print '@@@ No server MBean found'
       exit()

edit()
startEdit()

DB1 = {
    'PoolName' : 'DataSource for DGKIM',
    'JNDIName' : 'jdbc/dgkim',
    'Password' : 'password',
    'URLName' : 'jdbc:oracle:thin:@db.dgkim.net:1521:DGKIM',
    'DriverName' : 'oracle.jdbc.OracleDriver',
    'UserName' : 'dgkim',
    'InitialCapacity' : '20',
    'MaxCapacity' : '100',
    'CapacityIncrement' : '10',
    'ShrinkPeriodMinutes' : None,
    'TestTableName' : 'DUAL',
    'LoginDelaySeconds' : None
}

addJDBC(DB1, servernames)

save()
activate(block="true")

exit()

저는 프로젝트 특성상 하나의 DB가 아닌 여러개의 DB를 사용하게 됩니다. 위 예제에서는 DB1이란 것을 사용했지만, 실제로는 8개의 DB에 접속하는 프로그램을 작성했습니다.

그래서 위 코드를 만들고, 개발 서버냐 운용 서버냐에 따라 url과 servernames를 수정하여 한번에 데이터 소스를 작성해서 편리했습니다.

Java properties 에서 문자 치환하기.

저는 아래와 같은 properties 파일을 만들고,

UNKNOWNERROR=알 수 없는 오류입니다.(ERRORCODE)

아래와 같은 코드로 ERRORCODE를 대체해 왔습니다.

package com.idatabank.sso.exception;

import java.util.ResourceBundle;

public class SSOException extends Exception {
    public SSOException() {
        super();
    }
    
    public SSOException(String errorType) {
        super(errorType);
        
        userMessage = rb.getString(errorType);
        
        if ( userMessage == null ) {
            userMessage = rb.getString("UNKNOWNERROR").replaceAll("ERRORCODE", errorType);
        }
    }
    
    /**
     * 사용자 에러 메시지를 세팅한다.
     * @param userMessage
     */
    public void setUserMessage(String userMessage) {
        this.userMessage = userMessage;
    }
    
    /**
     * 사용자 에러 메시지를 반환한다.
     * @return
     */
    public String getUserMessage() {
        return userMessage;
    }
    
    private String userMessage = null;
    private static final ResourceBundle rb = ResourceBundle.getBundle("com.idatabank.sso.exception.Messages");
}

그런데, 오늘 문자열 치환의 새로운 방법을 찾았습니다.

http.method_not_implemented=Method {0} is not defined in RFC 2068 and is not supported by the Servlet API 
      String errMsg = rb.getString("http.method_not_implemented");
      Object[] errArgs = new Object[1];
      errArgs[0] = method;
      errMsg = java.text.MessageFormat.format(errMsg, errArgs);

이 방법이 좀더 우아해 보입니다.

Trac + SVN 을 사용하여 협업하기

개발업무를 하면, 당연히 소스코드를 관리할 필요가 있습니다.
그래서 일반적으로 CVS SVN GIT … 등의 소스 버전 관리툴을 사용합니다.
하지만 잘못 사용하고 계신 사용자 분도 계십니다. ( ex. 커밋시점 등. 테스트가 완료되지 않은 코드를 커밋하여 테스트. )

저는 trac 이란 툴과 SVN을 사용하여 협업을 하고 있습니다.

SVN에서 소스코드를 관리해 주고, Trac을 통해 타임라인 확인(히스토리), wiki 기능을 통한 문서 및 지침사항 정리, ticket을 통한 이슈 사항 관리를 하고 있습니다.

저는 현재 trac의 레포지토리 관리를 제 업무특성상, 고객사별로 레포지토리를 만들어 사용중입니다. 차후 버전에서는 멀티레포지토리를 지원한다고 하는데, 정확하게 어떻게 되는지는 모릅니다.

리눅스 서버를 하나가지고 계시면, 이번에 Trac + SVN 의 협업 개발환경을 사용해 보시는 것은 어떨까 권해드립니다.

trac 홈페이지 : http://trac.edgewall.org/

저는 현재, 소스코드 뿐 아닌 노출하지 말아야할 정보도 trac으로 관리하다 보니 인터넷에는 공개를 못하고, 사내에서만 사용합니다.