오른쪽 마우스 해제하기 URL


javascript:function r(d){d.oncontextmenu=null;d.onselectstart=null;d.ondragstart=null;d.onkeydown=null;d.onmousedown=null; d.body.oncontextmenu=null;d.body.onselectstart=null;d.body.ondragstart=null;d.body.onkeydown=null; d.body.onmousedown=null;};function unify(w){r(w.document);if(w.frames.length>0){for(var i=0;i<w.frames.length;i++){try{unify(w.frames[i].window);}catch(e){}};};}; unify(self);


by holyjohn | 2012/02/23 10:05 | 개발 | 트랙백 | 덧글(0)

JCF 3.0 사이트


사이트 주소


mantis 사이트 네....


by holyjohn | 2012/01/17 15:26 | 트랙백 | 덧글(0)

Eclipse - redmine - myln 연동

왜 Mylyn 은 RedMine 이랑 친하지 않아서 안찾아 지는건지 ㅡㅡ;;
설정해서 install software 여기 들어간다.

1. Myln 설치
http://download.eclipse.org/mylyn/releases/indigo

여기중 task ~ , task repositories ~ 머 이런애들 두개 설치.
나머지는 필요하다면 설치..
2.  Mylyn Connector (Redmine) 설치
Redmine과 연동하기 위해 Mylyn Connector (Redmine)을 찾아야 된다.
http://redmin-mylyncon.sourceforge.net
요놈 사이트에 가면 redmin 과 mylyn을 연동하는 프로젝트가 진행중인것을 알수있다.
여기저기 돌아다니다보면 이클립스에 connector 를 올리려면

http://redmin-mylyncon.sourceforge.net/update-site/N/
이놈으로 하면 된다고 한다...

완성~!~!~! 

///
사용방법
window - show view 에서 task List 와 Task Repositories 를 끄집어 낸다.
레드마인 주소를 입력 아이디 비밀번호 설정하면
뉴 쿼리를 통해 원하는 데이터를 읽어올수 있다~!

by holyjohn | 2012/01/17 13:57 | 개발 | 트랙백 | 덧글(0)

Dynamic MBean 예제

흔히 사용하는 SomethingMBean과는 달리

동적으로 MBean을 생성하는 방식이 Dynamic MBean이다.

Dynamic MBean은 애플리케이션 설정을 포함한 속성 파일을 사용하고,

이 내용를 관리를 위해 노출하고자 할 때 사용할 수 있다.

Dynamic MBean을 사용함으로써 원격에서 속성 파일의 정보를 확인하고,

애플리케이션이 실행 중에 속성 파일이 변경되더라도 이를 반영할 수 있다.

가장 큰 차이점은 동적으로 없던 속성을 추가하거나 있던 속성을 제거할 수 있다는 것이다.

다음은 Dynamic MBean을 사용하는 예제이다.

다음은 Dynamic MBean의 구현체이다.


package propertymanager;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.util.Iterator;

import java.util.Properties;

import java.util.SortedSet;

import java.util.TreeSet;

import javax.management.Attribute;

import javax.management.AttributeList;

import javax.management.AttributeNotFoundException;

import javax.management.DynamicMBean;

import javax.management.InvalidAttributeValueException;

import javax.management.MBeanAttributeInfo;

import javax.management.MBeanException;

import javax.management.MBeanInfo;

import javax.management.MBeanOperationInfo;

import javax.management.ReflectionException;

public class PropertyManager implements DynamicMBean {

 private final String propertyFileName;

 private final Properties properties;

 public PropertyManager(String propertyFileName) throws IOException {

  this.propertyFileName = propertyFileName;

  properties = new Properties();

  load();

 }

 public synchronized String getAttribute(String name)

   throws AttributeNotFoundException {

  String value = properties.getProperty(name);

  if (value != null)

   return value;

  else

   throw new AttributeNotFoundException("No such property: " + name);

 }

 public synchronized void setAttribute(Attribute attribute)

   throws InvalidAttributeValueException, MBeanException,

   AttributeNotFoundException {

  String name = attribute.getName();

  if (properties.getProperty(name) == null)

   throw new AttributeNotFoundException(name);

  Object value = attribute.getValue();

  if (!(value instanceof String)) {

   throw new InvalidAttributeValueException(

     "Attribute value not a string: " + value);

  }

  properties.setProperty(name, (String) value);

  try {

   save();

  } catch (IOException e) {

   throw new MBeanException(e);

  }

 }

 public synchronized AttributeList getAttributes(String[] names) {

  AttributeList list = new AttributeList();

  for (String name : names) {

   String value = properties.getProperty(name);

   if (value != null)

    list.add(new Attribute(name, value));

  }

  return list;

 }

 public synchronized AttributeList setAttributes(AttributeList list) {

  Attribute[] attrs = (Attribute[]) list.toArray(new Attribute[0]);

  AttributeList retlist = new AttributeList();

  for (Attribute attr : attrs) {

   String name = attr.getName();

   Object value = attr.getValue();

   if (properties.getProperty(name) != null && value instanceof String) {

    properties.setProperty(name, (String) value);

    retlist.add(new Attribute(name, value));

   }

  }

  try {

   save();

  } catch (IOException e) {

   return new AttributeList();

  }

  return retlist;

 }

 public Object invoke(String name, Object[] args, String[] sig)

   throws MBeanException, ReflectionException {

  if (name.equals("reload") && (args == null || args.length == 0)

    && (sig == null || sig.length == 0)) {

   try {

    load();

    return null;

   } catch (IOException e) {

    throw new MBeanException(e);

   }

  }

  throw new ReflectionException(new NoSuchMethodException(name));

 }

 public synchronized MBeanInfo getMBeanInfo() {

  SortedSet<String> names = new TreeSet<String>();

  for (Object name : properties.keySet())

   names.add((String) name);

  MBeanAttributeInfo[] attrs = new MBeanAttributeInfo[names.size()];

  Iterator<String> it = names.iterator();

  for (int i = 0; i < attrs.length; i++) {

   String name = it.next();

   attrs[i] = new MBeanAttributeInfo(name, "java.lang.String",

     "Property " + name, true, // isReadable

     true, // isWritable

     false); // isIs

  }

  MBeanOperationInfo[] opers = { new MBeanOperationInfo("reload",

    "Reload properties from file", null, // no parameters

    "void", MBeanOperationInfo.ACTION) };

  return new MBeanInfo(this.getClass().getName(),

    "Property Manager MBean", attrs, null, // constructors

    opers, null); // notifications

 }

 private void load() throws IOException {

  InputStream input = new FileInputStream(propertyFileName);

  properties.load(input);

  input.close();

 }

 private void save() throws IOException {

  String newPropertyFileName = propertyFileName + "$$new";

  File file = new File(newPropertyFileName);

  OutputStream output = new FileOutputStream(file);

  String comment = "Written by " + this.getClass().getName();

  properties.store(output, comment);

  output.close();

  if (!file.renameTo(new File(propertyFileName))) {

   throw new IOException("Rename " + newPropertyFileName + " to "

     + propertyFileName + " failed");

  }

 }

}

다음은 Dynamic MBean 테스트 클래스이다.

package propertymanager;

import java.io.IOException;

import java.lang.management.ManagementFactory;

import javax.management.InstanceAlreadyExistsException;

import javax.management.MBeanRegistrationException;

import javax.management.MBeanServer;

import javax.management.MalformedObjectNameException;

import javax.management.NotCompliantMBeanException;

import javax.management.ObjectName;

public class PropertyManagerTest {

 

 public static void main(String[] args) {

  MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

  ObjectName name;

  try {

   name = new ObjectName("propertymanager:type=PropertyManager");

   PropertyManager mbean = new PropertyManager("test.properties");

   mbs.registerMBean(mbean, name);

   

   System.out.println("Waiting forever...");

   Thread.sleep(Long.MAX_VALUE);

  } catch (MalformedObjectNameException e) {

   // TODO Auto-generated catch block

   e.printStackTrace();

  } catch (NullPointerException e) {

   // TODO Auto-generated catch block

   e.printStackTrace();

  } catch (IOException e) {

   // TODO Auto-generated catch block

   e.printStackTrace();

  } catch (InstanceAlreadyExistsException e) {

   // TODO Auto-generated catch block

   e.printStackTrace();

  } catch (MBeanRegistrationException e) {

   // TODO Auto-generated catch block

   e.printStackTrace();

  } catch (NotCompliantMBeanException e) {

   // TODO Auto-generated catch block

   e.printStackTrace();

  } catch (InterruptedException e) {

   // TODO Auto-generated catch block

   e.printStackTrace();

  }

 }

 

}

JConsole로 연결하여 MBeans 탭을 활용하면 간단히 테스트할 수 있다.

Reference:

http://weblogs.java.net/blog/emcmanus/archive/2006/11/a_real_example.html

by holyjohn | 2011/10/28 21:51 | 트랙백 | 덧글(0)

java 실행 옵션에 대하여

java - the Java application launcher

SYNOPSIS

    java [ options ] class [ argument ... ]    java [ options ] -jar file.jar [ argument ... ]    javaw [ options ] class [ argument ... ]    javaw [ options ] -jar file.jar [ argument ... ]
options
Command-line options.
class
Name of the class to be invoked.
file.jar
Name of the jar file to be invoked. Used only with -jar.
argument
Argument passed to the main function.

DESCRIPTION

The java tool launches a Java application. It does this by starting a Java runtime environment, loading a specified class, and invoking that class's main method.

The method must be declared public and static, it must not return any value, and it must accept a String array as a para meter. The method declaration must look like the following:

    public static void main(String args[])
By default, the first non-option argument is the name of the class to be invoked. A fully-qualified class name should be used. If the -jar option is specified, the first non-option argume nt is the name of aJAR archive containing class and resource f iles for the application, with the startup class indicated by the Main-Class manifest header.

The Java runtime searches for the startup class, and other classes used, in three sets of locations: the bootstrap class path, the installed extensions, and the user class path.

Non-option arguments after the class name or JAR file name are passed to the main function.

The javaw command is identical to java, except that with javaw there is no associated console window. Use javaw when you don't want a command prompt window to appear. The javaw launcher will, however, display a dialog box with error information if a launch fails for some reason.

OPTIONS

The launcher has a set of standard options that are supported on the current runtime environment and will be supported in future releases. In addition, the default Java HotSpot VMs provide a set of non-standard options that are subject to change in future releases.

Standard Options

-client
Select the Java HotSpot Client VM.

For more information, see Server-Class Machine Detection

-server
Select the Java HotSpot Server VM.

For more information, see Server-Class Machine Detection

-agentlib:libname[=options]
Load native agent library libname, e.g.

-agentlib:hprof

-agentlib:jdwp=help

-agentlib:hprof=help

For more information, see JVMTI Agent Command Line Options.

-agentpath:pathname[=options]
Load a native agent library by full pathname. For more information, see JVMTI Agent Command Line Options.

-classpath classpath
-cp classpath
Specify a list of directories, JAR archives, and ZIP archives to search for class files. Class path entries are separated by semicolons (;). Specifying -classpath or -cp overrides any setting of theCLASSPATH environment variable.

If -classpath and -cp are not used and CLASSPATH is not set, the user class path consists of the current directory (.).

For more information on class paths, see Setting the Class Path.

-Dproperty=value
Set a system property value. If value is a string that contains spaces, you must enclose the string in double quotes:
        java -Dfoo="some string" SomeClass        
-enableassertions[:<package name>"..." | :<class name> ]
-ea[:<package name>"..." | :<class name> ]
Enable assertions. Assertions are disabled by default.

With no arguments, enableassertions or -ea enables assertions. With one argument ending in "...", the switch enables assertions in the specified package and any subpackages. If the argument is simply "...", the switch enables assertions in the unnamed package in the current working directory. With one argument not ending in "...", the switch enables assertions in the specified class.

If a single command line contains multiple instances of these switches, they are processed in order before loading any classes. So, for example, to run a program with assertions enabled only in package com.wombat.fruitbat (and any subpackages), the following command could be used:

java -ea:com.wombat.fruitbat... <Main Class>

The -enableassertions and -ea switches apply to all s loaders and to system classes (which do not have a class loader). There is one exception to this rule: in their no-argument form, the switches do not apply to system. This makes it easy to turn on asserts in all classes except for system classes. A separate switch is provided to enable asserts in all system classes; see -enablesystemassertions below.

-disableassertions[:<package name>"..." | :<class ; ]
-da[:<package name>"..." | :<class name> ]
Disable assertions. This is the default.

With no arguments, disableassertions or -da disables assertions. With one argument ending in "...", the switch disables assertions in the specified package and any subpackages. If the argument is simply "...", the switch disables assertions in the unnamed package in the rent working directory. With one argument not ending in "...", the switch disables assertions in the specified class.

To run a program with assertions enabled in package com.wombat.fruitbat but disabled in class com.wombat.fruitbat.Brickbat, the following command could be used:

java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat lt;Main Class>

The -disableassertions and -da switches apply to all ss loaders and to system classes (which do not have a class loader). There is one exception to this rule: in their no-argument form, the switches do not apply to system. This makes it easy to turn on asserts in all classes except for system classes. A separate switch is provided to enable asserts in all system classes; see -disablesystemassertions below.

-enablesystemassertions
-esa
Enable asserts in all system classes (sets the default assertion status for system classes to true).

-disablesystemassertions
-dsa
Disables asserts in all system classes.

-jar
Execute a program encapsulated in a JAR file. The first argument is the name of a JAR file instead of a startup class name. In order for this option to work, the manifest of the JAR file must contain a line of the form Main-Class: classname. Here, classname identifies the class having the public static void main(String[] args) method that serves as your application's starting point. See the Jar tool reference page and the Jar trail of the Java Tutorial for information about working with Jar files and Jar-file manifests.

When you use this option, the JAR file is the source of all user classes, and other user class path settings are ignored.

-javaagent:jarpath[=options]
Load a Java programming language agent, see java.lang.instrument.

-verbose
-verbose:class
Display information about each class loaded.

-verbose:gc
Report on each garbage collection event.

-verbose:jni
Report information about use of native methods and other Java Native Interface activity.

-version
Display version information and exit.

-showversion
Display version information and continue.

-?
-help
Display usage information and exit.

-X
Display information about non-standard options and exit.

Non-Standard Options

-Xint
Operate in interpreted-only mode. Compilation to native code is disabled, and all bytecodes are executed by the interpreter. The performance benefits offered by the Java HotSpot Client VM's adaptive compiler will not be present in this mode.

-Xbatch
Disable background compilation. Normally the VM will compile the method as a background task, running the method in interpreter mode until the background compilation is finished. The -Xbatch flag disables background compilation so that compilation of all methods proceeds as a foreground task until completed.

-Xdebug
Start with support for JVMDI enabled. JVMDI has been deprecated and is not used for debugging in J2SE 5.0, so this option isn't needed for debugging in J2SE 5.0.

-Xbootclasspath:bootclasspath
Specify a semicolon-separated list of directories, JAR archives, and ZIP archives to search for boot class files. These are used in place of the boot class files included in the Java 2 SDK. Note: Applications that use this option for the purpose of overriding a class in rt.jar should not be deployed as doing so would contravene the Java 2 Runtime Environment binary code license.

-Xbootclasspath/a:path
Specify a semicolon-separated path of directires, JAR archives, and ZIP archives to append to the default bootstrap class path.

-Xbootclasspath/p:path
Specify a semicolon-separated path of directires, JAR archives, and ZIP archives to prepend in front of the default bootstrap class path. Note: Applications that use this option for the purpose of overriding a class in rt.jar should not be deployed as doing so would contravene the Java 2 Runtime Environment binary code license.

-Xcheck:jni
Perform additional checks for Java Native Interface (JNI) functions. Specifically, the Java Virtual Machine validates the parameters passed to the JNI function as well as the runtime environment data before processing the JNI request. Any invalid data encountered indicates a problem in the native code, and the Java Virtual Machine will terminate with a fatal error in such cases. Expect a performance degradation when this option is used.

-Xfuture
Perform strict class-file format checks. For purposes of backwards compatibility, the default format checks performed by the Java 2 SDK's virtual machine are no stricter than the checks performed by 1.1.x versions of the JDK software. The -Xfuture flag turns on stricter class-file format checks that enforce closer conformance to the class-file format specification. Developers are encouraged to use this flag when developing new code because the stricter checks will become the default in future releases of the Java application launcher.

-Xnoclassgc
Disable class garbage collection.

-Xincgc
Enable the incremental garbage collector. The incremental garbage collector, which is off by default, will reduce the occasional long garbage-collection pauses during program execution. The incremental garbage collector will at times execute concurrently with the program and during such times will reduce the processor capacity available to the program.

-Xloggc:file
Report on each garbage collection event, as with -verbose:gc, but log this data to file. In addition to the information -verbose:gc gives, each reported event will be preceeded by the time (in seconds) since the first garbage-collection event.

Always use a local file system for storage of this file to avoid stalling the JVM due to network latency. The file may be truncated in the case of a full file system and logging will continue on the truncated file. This option overrides -verbose:gc if both are given on the command line.

-Xmsn
Specify the initial size, in bytes, of the memory allocation pool. This value must be a multiple of 1024 greater than 1MB. Append the letter k or K to indicate kilobytes, or m or M to indicate megabytes. The default value is 2MB. Examples:
       -Xms6291456       -Xms6144k       -Xms6m       

-Xmxn
Specify the maximum size, in bytes, of the memory allocation pool. This value must a multiple of 1024 greater than 2MB. Append the letter k or K to indicate kilobytes, or m or M to indicate megabytes. The default value is 64MB. Examples:
       -Xmx83886080       -Xmx81920k       -Xmx80m       

-Xprof
Profiles the running program, and sends profiling data to standard output. This option is provided as a utility that is useful in program development and is not intended to be be used in production systems.

-Xrunhprof[:help][:<suboption>=<value>,...]
Enables cpu, heap, or monitor profiling. This option is typically followed by a list of comma-separated "<suboption>=<value>" pairs . Run the command java -Xrunhprof:help to obtain a list of suboptions and their default values.

-Xrs
Reduces usage of operating-system signals by the Java virtual machine (JVM). This option is available beginning with J2SE 1.3.1.

In J2SE 1.3.0, the Shutdown Hooks facility was added to allow orderly shutdown of a Java application. The intent was to allow user cleanup code (such as closing database connections) to run at shutdown, even if the JVM terminates abruptly.

The JVM watches for console control events to implement shutdown hooks for abnormal JVM termination. Specifically, the JVM registers a console control handler which begins shutdown-hook processing and returns TRUE for CTRL_C_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT.

The JVM uses a similar mechanism to implement the pre-1.2 feature of dumping thread stacks for debugging purposes. Sun's JVM uses CTRL_BREAK_EVENT to perform thread dumps.

If the JVM is run as a service (for example, the servlet engine for a web server), it can receive CTRL_LOGOFF_EVENT but should not initiate shutdown since the operating system will not actually terminate the process. To avoid possible interference such as this, the -Xrs command-line option has been added beginning with J2SE 1.3.1. When the -Xrs option is used on Sun's JVM, the JVM does not install a console control handler, implying that it does not watch for or process CTRL_C_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, or CTRL_SHUTDOWN_EVENT.

There are two consequences of specifying -Xrs:

  • Ctrl-Break thread dumps are not available.
  • User code is responsible for causing shutdown hooks to run, for example by calling System.exit() when the JVM is to be terminated.

-Xssn
Set thread stack size.

SEE ALSO


Copyright © 2004, 2010 Oracle and/or its affiliates. All rights reserved.

Sun 

by holyjohn | 2011/10/26 14:51 | 트랙백 | 덧글(0)

자바 툴을 사용해 보자

자바툴을 사용하자

자바를 처음 시작할 때 반드시 배우는 명령어가 있다. javac, java, javaw (이건 옵션... -_-;;;) 이다.
자바 컴파일러와 인터프리터... javac는 자바 소스코드를 바이트코드로 컴파일해주는 툴이고, java는 컴파일된 자바 바이트코드를 실행하는 툴이다. 모두 알다시피 java 툴은 인터프리터이다.

그런데 JDK 또는 JRE가 설치된 위치에 bin 디렉토리를 보면 javac, java, javaw 들 외에도 상당히 많은 바이너리들이 존재한다. 보통은 JVM에서 그냥 쓰는 녀석들이려니 하고 넘어가지만 만물은 모두 존재이유가 있는 법... -_-;; 이들은 자바 어플리케이션 개발 및 운영시에 상당히 유용하게 사용할 수 있는 각종 툴들이다.

물론 메모리 덤프를 통해 힙 영역에 있는 각종 객체들을 볼 수 있는 툴도 존재한다. 올레~~ /-_-/
이들 중 유용하다고 생각되는 몇개만 뽑아서 정리해보도록 하자. 


1. jps
http://java.sun.com/javase/6/docs/technotes/tools/share/jps.html

jps는 unix 명령어인 ps와 같이 실행중인 자바 프로세스를 보여주는 툴이다. 일반적으로 ps 명령어와 grep 명령어의 조합으로 실행중인 자바 프로세스를 찾을 수도 있지만, jps를 사용하면 좀 더 편하다. (사실.. 말이 편하다는거지 ps 와 grep 으로 찾는게 더 편하다는 사람이 있다면 그것도 좋다.)

옵션없이 실행하면 프로세스 아이디와 클래스명만 보이므로 정보가 매우 부족하다. 
보통 -v 옵션(JVM 파라미터 보임)만 줘도 상당히 좋은 결과를 보여주며, 취향에 따라 -m (메인 메소드의 args 보임)과 -l (전체 패키지명 보임)옵션을 곁들이면 좋다.

예) jps -v -m -l


2. jmap
http://java.sun.com/javase/6/docs/technotes/tools/share/jmap.html

jmap은 현재 수행중인 자바 어플리케이션의 메모리맵을 보여주는 툴이다. 이 툴을 사용하여 heap 메모리를 덤프떠서 볼 수 있다. 일반적으로 OOME는 heap 영역에서 나는 것이 대부분이므로 이 툴을 사용하는 경우도 아마 heap 메모리 영역을 덤프를 뜨기 위한 것이 대부분일 것이다. 

jmap을 사용하여 heap 메모리 덤프를 뜨는 방법은 다음과 같다.

jmap -dump:<dump-options> pid
또는 
jmap -histo[:live] pid
-dump 옵션을 사용할 때는 보통 -dump:format=b,file=heap.hprof 정도의 옵션만으로 충분하다.

JDK 버전에 따라 제공되는 옵션이 다르므로 jmap -help 명령을 실행한 후에 사용법을 읽어보고, 두개 중 지원되는 옵션을 선택하여 실행하면 된다. 

pid는 ps 명령어를 사용하거나 위에서 본 jps를 사용하여 알아내도록 하자.
 
만약 톰캣과 같이 WAS에서 동작하는 서버 어플리케이션에 jmap을 사용하여 heap 덤프 생성하고자 한다면 -Xmx 옵션값에 주의해야 한다.
만약 -Xmx가 1024m 으로 잡혀있는 WAS에서 OOME 발생 후 jmap으로 heap 덤프를 만들게 되면??
1G가 넘는 덤프파일과 만나게 된다.. -_-;;;;;

보너스~~ heap 덤프를 생성하는 또 다른 방법)
jmap은 runtime에 직접 툴을 실행하여 덤프를 생성해야 한다. 하지만 서비스를 운영하고 있는 경우 OOME가 언제 발생할 줄 알겠는가... OOME가 발생하기까지 눈뜨고 기다렸다가 jmap을 실행하여 덤프를 생성하는건 너무 터무니없다. 이럴 땐 -XX:+HeapDumpOnOutOfMemoryError VM 옵션을 이용하자. 이 옵션을 사용하면 OOME가 발생했을 때 자동으로 java_pid<pid>.hprof 파일명으로 heap 덤프를 생성한다. 또한 -XX:OnOutOfMemoryError=<Command> 옵션을 추가하여 OOME 발생 후 특정 명령어를 수행하도록 할 수도 있다. 주의할 점은 OnOutOfMemoryError 옵션은 JDK 버전 1.4.2 update 12 이후 버전과 6 에서만 동작한다는 점이다. 왜 그런지는 모르겠지만 JDK 5 에서는 해당 옵션을 지원하지 않는다.

이와 같은 VM 추가 옵션에 대해서는 다음의 링크를 참조하면 된다.
http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
http://wiki.ex-em.com/index.php/JVM_Options

주의할 점은 -XX 옵션은 Hotspot 옵션으로 각 VM마다 제공할수도 안할수도 있다.


3. jhat
http://java.sun.com/javase/6/docs/technotes/tools/share/jhat.html

jmap으로 heap 메모리 덤프를 생성했으면 이제 메모리를 분석해야 한다. 메모리 덤프 파일은 눈으로 보기 힘드므로 (그래도 굳이 눈으로 보고 싶으면 말리진 않겠다...) 이를 분석하여 쉽게 보여주는 툴이 필요하다.

대표적으로 IBM에서 만든 HeapAnalyzer 가 있지만 라이센스 문제도 있고, 직접 사용해본 결과 덤프 파일의 크기가 매우 큰 경우 HeapAnalyzer에서 OOME가 발생하는 아주 짜증나는 상황에 직면하게 된다. -_-;;;;;;; (물론 jhat도 마찬가지다.)

jhat은 jmap으로 생성된 바이너리 heap 덤프 파일을 분석하여 보여주는 툴이다. jhat을 실행하는 방법은 매우 간단하며 heap 덤프 파일 경로를 주어 jhat을 실행시키면 7000 포트로 분석 결과를 볼 수 있도록 자체 웹서버가 구동된다. 7000 포트가 이미 사용중이면 -port 옵션을 사용하여 포트를 변경할 수 있다.

IBM의 HeapAnalyzer 와 같은 예쁜 GUI를 제공하는 것은 아니지만, 나름 쓸만한 정보를 보여준다.

<jhat 실행 후...>

<각 인스턴스(객체) 별 갯수를 볼 수 있다.. 메모리 누수의 범인은 뉴규~??>


여기서는 Memory 상태를 추적할 수 있는 공짜 Tool 을 하나 소개하겠다.
Sun JDK, HP JDK 에 포함되어있는 것으로 jstat 라는 것이다.
현재 JVM 의 Heap Memory 상태를 실시간으로 추적하는 매우 효율적인 Tool 이다.

다음은 jstat 에 사용할 수 있는 파라미터들이다.

[ngwas1:/NGI/agreeDomain]jstat
invalid argument count
Usage: jstat -help|-options
       jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

Definitions:
  <option>      An option reported by the -options option
  <vmid>        Virtual Machine Identifier. A vmid takes the following form:
                     <lvmid>[@<hostname>[:<port>]]
                Where <lvmid> is the local vm identifier for the target
                Java virtual machine, typically a process id; <hostname> is
                the name of the host running the target Java virtual machine;
                and <port> is the port number for the rmiregistry on the
                target host. See the jvmstat documentation for a more complete
                description of the Virtual Machine Identifier.
  <lines>       Number of samples between header lines.
  <interval>    Sampling interval. The following forms are allowed:
                    <n>["ms"|"s"]
                Where <n> is an integer and the suffix specifies the units as 
                milliseconds("ms") or seconds("s"). The default units are "ms".
  <count>       Number of samples to take before terminating.
  -J<flag>      Pass <flag> directly to the runtime system.


위에 있는 <option> 을 보겠다.

[ngwas1:/NGI/wily601]jstat -options
-class
-compiler
-gc
-gccapacity
-gccause
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcpermcapacity
-gcutil
-printcompilation


위와같이 여러개의 옵션이 있는데 여기서 우리는 GC 관련 옵션을 선택하여 Heap Memory Area 를 추적해보겠다.

[ngwas1:/NGI/wily601]jstat -gc -h20 6637 3000
위의 명령을 해석해보면...

-gc : GC 되는 것을 보겠다는 의미
-h20 : 아래 데이터를 보면 알겠지만 숫자에 대한 헤더가 있는데 그 헤더를 20 라인마다 찍겠다는 의미이다.
6637 : Process PID 이다. 이건 항상 변하겠죠?
3000 : 시간이다. ms 단위라서 3000 이면 3초마다 한번씩 데이터를 추출하겠다는 얘기다.

위와같이 명령을 실행하면 아래와 같은 내용이 추출되며 각 줄마다 3초에 한번씩 추출된다.
그럼 아래의 데이터를 보는 법을 알려주겠다.
첫번째 파마리터 부터 설명을 붙여주겠다.

SOC : Servivor 0 Area Capacity
S1C : Servivor 1 Area Capacity
SOU : Servior 0 Area Used
S1U : Servivor 1 Area Used
EC : Eden Capacity
EU : Eden Used
OC : Old Capacity (Tenured Capacity)
OU : Old Used (Tenured Used)
PC : Permanent Capacity
PU : Permanent Used
YGC : Young GC Count
YGCT : Young GC Count Time (Young GC 시간이다. 누적시간이므로 아래서 위의 시간을 빼면 Young GC Time 을 알수있다)
FGC : Full GC Count
FGCT : Full GC Count Time
GCT : GC Time (YGCT + FGCT)

어려운가 ?

http://blog.naver.com/salsu0/30000025219  <== 요기를 읽고나서 보면 안어렵다.

그럼 아래 데이터를 한번 분석해 봅시다.

S0 Area 는 34 메가 정도가 할당되었다.
S1 Area 역시 동일하게 할당되었다.
Eden Area 는 72 메가 정도가 할당되었고
Old Area (Tenured Area) 는 700 메가 정도가 할당되었다.
Permanent Area 는 130 메가 정도 할당되었다.

Young GC 가 마지막줄을 봤을때 40 번 실행되었고,
Full GC 는 실행된적이 없다.

Young GC 가 40 번 실행되는 동안 걸린 시간은 7초 정도이며
맨 밑에 세번째 줄과 다음 줄을 비교해봤을때 Young GC 가 일어난 시간은 0.17 초 정도이다.
알다시피 GC 할때는 전혀 일을 하지 않는다.
물론 Default GC Option 일 경우이다. (다음번에는 GC Option 의 종류를 설명하겠다.)

그럼 다들 한번 테스트를 해보기를 바란다.

잼있죠 ?

  S0C        S1C     S0U      S1U      EC           EU            OC           OU            PC         PU        YGC    YGCT FGC    FGCT     GCT   
34944.0 34944.0  0.0   17142.6 279616.0 72110.9   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 72110.9   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 72110.9   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 72110.9   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 72110.9   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 72110.9   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 72110.9   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 72110.9   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 72110.9   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 72110.9   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 72110.9   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 72110.9   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 73802.8   699072.0   86240.1   131072.0 69871.3     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 76141.1   699072.0   86240.1   131072.0 69871.4     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 102379.9  699072.0   86240.1   131072.0 69871.7     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 159719.5  699072.0   86240.1   131072.0 69875.0     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 207689.5  699072.0   86240.1   131072.0 69946.6     39    7.006   0      0.000    7.006
34944.0 34944.0  0.0   17142.6 279616.0 259934.1  699072.0   86240.1   131072.0 69948.0     39    7.006   0      0.000    7.006
34944.0 34944.0 17625.0  0.0   279616.0 50821.6   699072.0   86240.1   131072.0 69950.3     40    7.176   0      0.000    7.176
34944.0 34944.0 17625.0  0.0   279616.0 83850.1   699072.0   86240.1   131072.0 69950.9     40    7.176   0      0.000    7.176




by holyjohn | 2011/10/26 14:24 | Java - 자바 | 트랙백 | 덧글(0)

JMX 사용 관련 정보 URL


JMX를 사용해 관리되는 인스턴스 생성하기
http://chanwook.tistory.com/568
간단한 사용법

JMX를 이용한 daemon 모니터링
http://blog.kangwoo.kr/76
설정, 클라이언트, 서버 개발 코드 포함

4장. JMX 어플리케이션 개발
http://technet.tmax.co.kr/kr/edocs/jeus/60/jmx/chapter_JMX_applications.html
TMAX 의 제우스에 jmx 추가 설정하는 방법

Chapter 20. JMX 지원 - Spring
http://openframework.or.kr/framework_reference/spring/ver1.2.2/html/ch20.html
Spring내 JMX지원


by holyjohn | 2011/10/23 10:45 | 트랙백 | 덧글(0)

NOSQL 제품 특징 비교


FeaturesMongoDBRiakHyperTableHBase
Logical Data ModelRich Document with
support
for Nested Document
Rich DocumentColumn FamilyColumn Family
Support for CAPCAAPCACA
Dynamic Addition
/Removal of Node
SupportedSupportedSupportedSupported
Multi DC supportSupportedNot SupportedSupportedSupported
InterfaceVariety of APIs
(Java, Python, Perl, C#)
JSON over HTTPREST, Thrift, JavaC++,Thrift
Persistence ModelDiskDiskMemory + Desk
(Tunable)
Memory + Desk
(Tunable)
Comparative
Performance
Better
(C++)
Best
(Erlang)
Better
(C++)
Good
(Java)
Commercial
Support
10gen.comBasho TechnologiesHypertable IncCloudera

사례

해외사례

  • Google Bigtable
    • 모든 URL 기반의 문서 정보 수집 (Key/Value 기반의 Bigtable에 저장)
    • 수집한 정보를 Map/Reduce로 색인 (색인 데이터도 Bigtable에 저장)
    • Document에서 사용하는 정보가 계속 추가될 수 있다. (URL, title, 본문, meta tag 등)
  • Amazon’s Dynamo
  • Digg.com Cassandra
    • 내 친구가 추천한 정보를 보여주기 위해서 NoSQL이 필요해짐
  • Twitter Cassandra
    • 모든 사용자의 글을 하나의 DB에 저장할 수 없다면 내가 팔로우하는 timeline은 어떻게 보여줄 수 있을까?

NHN 인증 플랫폼

  • 요구사항
    • backend DB 점검 시에도 read가 가능한 시스템
    • 로그인 사용자 세션 정보를 저장 및 조회
  • 구현
    • MySQL 클러스터링으로 세션 정보 저장 – MySQL을 메모리 DB로 사용
    • in-house 메모리 DB로 사용자 정보 저장 – Oracle 로그 기반으로 메모리 리플리케이션

Daum view 추천 플랫폼 (예정)

  • 사용자의 추천을 모두 수집한다.
  • 소셜 네트워크를 저장한다.
  • 추천 정보를 추천 히스토리와 소셜 네트워크 기반으로 Map/Reduce 분석
  • 신뢰도 높은 추천 기반으로 문서 랭킹 부여

Daum Cafe

  • ‘최근 방문 카페’ 기능에 Cassandra 적용

Reference

This entry was posted in Software & Developer and tagged . Bookmark thepermalinkPost a comment or leave a trackback: Trackback URL.

by holyjohn | 2011/10/20 15:10 | 트랙백 | 덧글(0)

NoSQL 정의와 사례 정리

NoSQL 정의

No SQL? Not Only SQL?

NoSQL은 관계형 데이터베이스의 한계를 극복하기 위한 데이터 저장소의 새로운 형태로 수평적 확장성을 특징으로 한다.
관계형이 아니기 때문에 join이 없고 고정된 스키마를 갖지 않는다.

대표적인 NoSQL 제품은 Google의 BigTable, Amazon의 Dynamo이 있고, 이외에 비롯해 다양한 오픈소스 제품이 소개되어 있다.

Why NoSQL?

데이터 규모의 확대

  • 저장할 데이터가 많아지면서 읽고/쓰기에 있어서 RDB가 제약 요소가 되었다.
  • RDB의 수평적 확장성 한계로 새로운 해결책이 필요했다.

웹 서비스의 구조 변화

  • 저장할 데이터의 형태가 계속 변화한다.
  • 사용자의 데이터 요구가 일관적이지 않고 다양하다.

NoSQL 선택

BigTable, Dynamo, Cassandra, CouchDB, MongoDB, Hbase, Riak, Voldemort 등 20개 이상의 NoSQL 제품이 있고,
자신의 목적에 맞는 NoSQL 제품을 선택하기 위해서는 각각의 특징을 정확히 파악해야 한다.

CAP 이론

CAP이란?

  • Consistency: 각각의 사용자가 항상 동일한 데이터를 조회한다.
  • Availability: 모든 사용자가 항상 읽고 쓸 수 있다.
  • Partition tolerance: 물리적 네트워크 분산 환경에서 시스템이 잘 동작한다.

Availability와 Partition tolerance가 추상적 정의만으로 이해가 힘들 수 있어서 설명을 추가합니다. (용어 때문에 오히려 이해가 어려울 수 있는데 둘 다 파티션에 대한 특성으로 접근해야 합니다.) Availability and Partition Tolerance 글과 댓글을 참고하면 A, P에 대한 정의 및 기능이 좀 더 이해가 쉬울 수 있습니다.

  • 장애 때문에 특정 노드들이 기약 없이 대기해야 한다면 Availability가 희생된다.
  • 특정 요청이 장애로 기약 없이 대기해야 한다면 Partition tolerance이 희생된다.

CAP 이론과 NoSQL

데이터 저장소는 CAP 중에서 2가지만 선택할 수 있다. 예를 들어 RDB는 CA에 특화되어 있기 때문에 분산 환경에 적용이 어렵다.
NoSQL 제품은 CAP 중에서 C 또는 A를 일부 포기함으로써 분산 확장이 가능하다. 일반적으로 NoSQL 시스템은 관계형을 포기하거나 트랜잭션 구조를 느슨하게 함으로써 수평 확장이 가능하도록 한다.

CAP과 데이터 모델에 따른 구분 (각각의 구분은 설정에 따라 변경될 수 있다.)

RDB 대안

RDB 대안에 NoSQL만 있는 것은 아니다.

RDB CLUSTER

  • Oracle RAC, MySQL cluster
  • 분산 확장과 안정성을 강화한 RDB 솔루션
  • 단점)
    • 확장 비용이 비싸고 적용 및 관리에 고급 기술이 필요하다.
    • 읽기 성능만 향상된다.
  • 장점)
    • 최소한의 개발 비용으로 도입이 가능하다.
    • 비용 예측이 가능하다.
    • 상용화된 지원이 가능하다.

MEMORY CACHE

  • memcached, Coherence, Santa
  • RDB 앞단에 메모리 캐시를 두고 읽기 또는 읽기/쓰기를 캐싱한다.
  • 단점)
    • 메모리 비용이 비싸다.
    • 메모리 크기에 제약이 있다.
    • RDB를 저장소로 사용하지만 장애시 일부 데이터가 손실될 수 있다.
  • 장점)
    • 가격대비 효율성이 disk보다 훨씬 높다.
    • 읽기/쓰기 성능을 크게 향상시킬 수 있다.
    • 읽기/쓰기 수평 확장이 가능하다.

NOSQL

  • 디스크 기반의 수평 확장이 뛰어난 데이터 저장소
  • 단점)
    • Persistence Layer를 새로 개발해야 한다.
    • 사용화된 지원이 어려울 수 있다.
  • 장점)
    • 읽기/쓰기 성능 및 확장성이 뛰어나다.
    • 일반 저가 장비를 사용할 수 있다.

NOSQL 교체 비용, 가치, 특징 비교

API, DATA MODEL

  • Interfaces- REST (HBase, CouchDB, Riak, etc.), MapReduce (HBase, CouchDB, MongoDB, Hypertable, etc.), Get/Put (Voldemort, Scalaris, etc.), Thrift (HBase, Hypertable, Cassandra, etc.), Language Specific APIs (MongoDB).
  • Logical Data Models-Key-Value oriented (Voldemort, Dynomite etc.), Column Familiy oriented (BigTable, HBase, Hypertable etc.), Document oriented (Couch DB, MongoDB etc.), Graph oriented (Neo4j, Infogrid etc.)
  • Data Distribution Model- Consistency and Availability(HBase, Hypertable, MongoDB etc), Availability and Partitionality (Cassandra etc.).
  • DataPersistence-Memory Based (e.g. Redis, Scalaris, Terrastore), Disk Based (e.g., MongoDB, Riak etc.), Combination of both Memory and Disk(e.g., HBase, Hypertable, Cassandra).


by holyjohn | 2011/10/20 15:09 | 트랙백 | 덧글(0)

noSQL란 무엇인가?


noSQL기술이 BigData처리와 함께 갑자기 화두가 되고 있다. 5년전 memory cache기반의 noSQL기술을 접했을 때에는 '이걸로 과연 어디에 적용할 수 있을까?' 하며 부정적이었는데, 어느새 이런 기술이 이전의 많은 기술을 뒤엎기라도 할 기세다. 아무튼 noSQL에 대한 나의 부정적인 감정(?)에 대한 토로는 나중으로 미루고 이번에는 noSQL의 기본 개념에 대해 정리해보자.

구글의 BigTable paper에 보면 구글의 BigTable이 대체 무엇인지에 대해 간단하고 명료하게 기술하고 있다.

A Bigtable is a sparse, distributed, persistent multidimensional sorted map.

이 간단한 문장에 noSQL기반 기술의 거의 모든 기본이 들어있다고 보면 된다. 자 이제 하나 하나 파들어가볼까?

Map이다

이게 정말 핵심중의 핵심이다. 이것만 이해해도 상당히 많은 기본개념을 이해한거라 나는 생각한다. 바쁘면 이제 이 글의 나머지를 안 읽어도 된다(^^). 거의 모든 noSQL은 Map이다. 즉, 유일한 키와 그것에 관련된 하나의 값을 가진 자료형이 noSQL이 데이타를 저장하는 기본적인 방식이다. JSON방식으로 예를 들면 다음과 같은 형태로 noSQL은 자료를 저장한다.
{
 "zzzzz" : "woot",
 "xyz" : "hello",
 "aaaab" : "world",
 "1" : "x",
 "aaaaa" : "y"
}
영구적이다 (Persistence)
noSQL은 기본적으로 데이타베이스의 역할을 수행한다. 당연히 데이타는 어느 정도 이상은 영구적이어야한다. 다만 메모리기반의 noSQL의 경우 이 영구적인 특성이 조금 떨어진다고 볼수도 있겠다.

분산기반이다 (Distribute)

noSQL이 나온 배경중의 하나는 기존의 RDB가 데이터 확장에 따른 확장성에 제약이 심했기 때문이다. 따라서 대부분의 noSQL은 데이터 저장 및 복제에 대해 설계 초기부터 분산시스템을 기반에 두게 되었다. 구글의 BigTable의 경우 Google File System(GFS)을 기반으로 하고, HBase를 비롯한 많은 noSQL은 하둡(Hadoop)의 분산 파일 시스템(HDFS)을 사용한다.
데이타 복제와 분산처리를 어떻게 하는지 자체에 대한 것은 사실 noSQL 기본 개념과는 큰 상관이 없는 주제이므로, 그리고 나도 사실 그쪽에는 내공이 딸리므로 넘어가자.

정렬기능이 있다 (Sorted)

모든 noSQL이 이 기능을 가지고 있다는 게 아니라 HBase, BigTable같은 일부가 가지고 있는 기능이다. 키와 값이 알파벳순으로 정렬이 되는 기능을 가진다. 실로 어마어마한 데이타를 처리하기 위한 용도로 만들어진게 noSQL이다보니 이런 정렬기능은, 혹은 이런 정렬기능의 성능은 너무나도 중요한 요소이다.

다차원적이다 (Multidimentional)

noSQL의 개념이 기존 데이타베이스에 익숙한 사람들에게 전달되기 위해 많은 noSQL기술들이 기존의 개념을 많이 차용한다. 테이블이나 칼럼 혹은 Row같은 RDB의 개념이 그대로 noSQL에서도 쓰이는 경우가 있고 이로 인해 noSQL의 이해를 어렵게 하는 경우가 있다. 새술은 새푸대에 담았으면 싶다. 그래서 다차원적인 Map을 지원하는게 noSQL의 특징이라고 강조하고 싶다. 다차원적이라는 말이 어렵다면 Map의 Map을 지원한다라고 할까? 즉,
{
 "1" : {
 "A" : "x",
 "B" : "z" },
 "aaaaa" : {
  "A" : "y",
  "B" : "w"
},
 "aaaab" : {
  "A" : "world",
  "B" : "ocean"
},
 "xyz" : {
 "A" : "hello",
  "B" : "there"
},
 "zzzzz" : {
 "A" : "woot",
 "B" : "1337"
}
}
위의 코드와 같이 Map의 Map의 형태로 데이터를 저장하는 것이 가능하다. 이 특성을 이용해서 "1"이나 "aaaa"를 row에 대한 키로 정의하고 "A"나 "B"를 column의 키로 정의하는 것이 가능하고 전체를 하나의 테이블로 정의할수도 있겠다.

엉성하다 (Sparse)
noSQL의 데이타 모델의 특징은 엉성하다는데 있기도 하다. 기존 RDB가 명확한 데이타 스키마 기반으로 데이타를 처리하고 그 스키마를 변경하는데 많은 비용이 들었다면, noSQL의 경우는 그보다 훨씬 더 데이타 모델에 대해 유연성을 제공한다.
{  // ...
 "zzzzz" :
{ "A" :
{
"catch_phrase" : "woot",
}
  }
}
위의 예처럼, "zzzz" Row의 "A"칼럼은 이제까지 없었던 "catch_phrase"라는 키를 가지고 있다. 이처럼 noSQL은 데이타 모델에 대해 훨씬 더 유연한 특성을 가지고 있다.

그럼 어떤 종류의 noSQL이 있을까?
맵+엉성+다차원= memcache: 일단 가장 단순한 형태로 memcache를 들수 있다. 쉽게 말해 메모리상에 맵형태로 데이터를 처리하는 방식인데, 이 메모리가 해싱기법에 의해 다중 서버에 의해 관리되는 방식이다. 클라이언트는 자신만의 해싱키를 이용해서 자기가 원하는 서버에 데이터를 저장하고 처리할 수 있다. 하지만 Persistence를 지원하거나 확장성이 높은 형태를 지원하지는 않는다. 또한 메모리만을 사용하므로 가격이 저렴한 편도 아니다. 하지만 가장 단순하게 noSQL을 적용해서 처리할 수 있는 기술이고 특히 캐시용도로 많이 사용한다.

맵+엉성+다차원+분산= 오라클 coherence, IBM ObjectGrid: memcache에다가 그리드 기반의 기술을 접목한 것으로 메모리만 충분히 많다면 가장 빠른 처리시스템을 보유할 수 있고 데이타 복제나 분산기반 확장등에 아주 많은 잇점을 가져올 수 있다. 별도의 솔루션을 이용하면 일반 Disk기반의 영속성등을 처리할 수도 있다.

맵+엉성+다차원+분산+영속성+정렬=HBase, BigTable, Cassandra: 이것은 앞서 설명했던 기본적인 noSQL의 기능을 대부분 가지고 있는 기술들이다. 더 설명할 게 없다. MongoDB같은 문서기반의 noSQL도 설명할 수도 있겠는데, 일단 본 포스트는 기본 개념에 충실하자고 쓴 글이므로 여기서 마무리 한다.


이정도면 기본이겠지
noSQL기술이 얼마나 앞으로 사람들을 더 열광시킬지 잘 모르겠다. 분명 재미있는 기술이고 부분적으로나마 상당히 효과를 볼만한 곳이 많은 기술이다. 아무튼 기본적인 noSQL의 개념은 여기까지...

by holyjohn | 2011/10/20 15:04 | 트랙백(1) | 덧글(0)

Eclipse + Drools 설치 및 확인하기

[Drools 5.1 다운로드 + Eclipse 설정]

  1. http://www.jboss.org/drools/downloads.html 접속
  2. Drools Binaries 다운로드
    → Drools 5.1 Runtime 이므로 임의 위치에 압축을 해제한다.
  3. Drools Eclipse 3.5 Workbench 다운로드
    → Drools 5.1 Eclipse Plug-In 이므로 Eclipse 설치한 곳에 압축을 해제한다.
  4. Drools Runtime 설정
    → Eclipse 실행
    → 메뉴 “Windows – Preferences – Drools – Installed Drools Runtime”
    → Add 클릭
    → Create a new Drools 5 Runtime… 클릭
    → Drools 5.1 Runtime 경로 지정  (ex, D:\BOX-PROGRAMMiNG\drools-5.1.1-bin) (2번에 해당)
    → Installed Drools Runtimes 선택 후 OK 클릭
  5. Drools Project 생성 테스트
    → ”File – New – Project”
    → “Drools – Drools Project”
    → Project Name 지정 – Next 클릭
    → New Drools Project – 전부 체크 – Next 클릭
    → Generate code compatible with – Drools 5.1.x 선택 – Finish 클릭
    → Project Name\src\main\java\com\sample\DroolsTest.java 선택
    → 메뉴 “Run – Run”
    → Console 창에 아래와 같이 나오면 설정 완료


    Hello World
    Goodbye cruel world

by holyjohn | 2011/09/21 09:48 | 개발 | 트랙백 | 덧글(0)

스프링 배치(Spring Batch) 관련 국내 자료

정상혁

이미 35개 이상의 Accenture 고객사에 Spring batch가 적용되고 있다

스프링배치 연재(1) 배치처리의 특징

스프링배치 연재(2) 대용량 처리 배치 프로그램을 만들 때 유의할 점

스프링배치 연재(3) 스프링배치 프로젝트와 주요 기능들

스프링배치 연재(4) 스프링배치의 구조와 구성요소들

스프링배치 연재(5) ItemReader와 ItemWriter

스프링배치 연재(6) 플랫파일 읽기와 쓰기

스프링배치 연재(7) XML파일 읽기와 쓰기

스프링배치 연재(8) JDBC를 이용한 Cursor 기반의 DB 조회

스프링배치 연재(9) JobRepository

스프링배치 연재(10) JobLauncher와 Job, Step

스프링배치 연재(11) 재시작과 재시도

스프링배치 연재(12) 이벤트 처리, 유효성 검사, 변환, 기존 클래스 활용

스프링배치 연재(13) 스프링배치의 형제들

스프링배치 연재(14) 드라이빙 쿼리와 iBatis의 활용

스프링배치 연재(15) 하이버네이트 활용과 여러파일 읽기

스프링배치 연재(16) DB to XML 파일 만들기 예제

배치 어플리케이션 실행 스크립트와 빌드

 

박찬욱님

엘레강스한 배치 추상화 프레임웍 - 스프링 배치

[Beta 1.0]Spring Batch 프레임웍 레퍼런스 한글 편역 버전.

[Beta 2.0] Spring Batch 프레임웍 레퍼런스 한글 편역 버전.

[Beta 3.0] Spring Batch 프레임웍 레퍼런스 한글 편역 버전.

 

제 1부. 스프링 배치 기본 아키텍처와 잡(Job) 직접 실행해보기

제 2부. FlatFileItemReader와 그 친구들(파트1) 

제 2부. FlatFileItemReader와 그 친구들(파트2) (소스 및 PPT)

제 3부. FlatFileItemWriter와 아이템 변환하기 (소스 및 PPT)

제 4부. StAX 기반 아이템 처리 (소스 및 PPT)

제 5부. 데이터베이스에 아이템 쓰고, 읽고~ (소스 및 PPT)

제 6부. 배치 반복 처리하기

 

Spring Batch 쓰임새 분석 - 단순한 배치 반복하기

Spring Batch 쓰임새 분석 - 자동적인 재시작

Batch Processing Strategies at Spring Batch 

스프링 배치's 액터(Actor)

 

Spring Batch 1.0에서 2.0으로 진화하기- 1. ItemReader/ItemWriter(1)

Spring Batch 1.0에서 2.0으로 진화하기- 1. ItemReader/ItemWriter(2)

Spring Batch 1.0에서 2.0으로 진화하기- 3. JobExecutionLisneter & 4. ItemProcessor

Spring Batch 1.0에서 2.0으로 진화하기- 5. Configuration

 

백기선님

The Domain Language of Batch - Spring Batch Chapter 2

ItemReader - Spring Batch Chapter 3

경구사님

Spring batch 개발환경 설정

 

김승권님

차세대배치시스템구축성공전략

 - [Spring batch]차세대 배치시스템 구축 성공전략 - JCO컨퍼런스

박재성님

Spring Batch 시작하기

 

KSUG포럼

SpringBatch에 대한 경험담을 듣고 싶습니다.

Spring Batch ItemReader 구현체에 대한 궁금증 

by holyjohn | 2011/08/17 12:10 | Java - 자바 | 트랙백(1) | 덧글(0)

Java Object Serialization에 대해 모르고 있던 5가지 사항

출처 : http://www.ibm.com/developerworks/kr/library/j-5things1/index.html

직렬화된 데이터가 안전하다고 생각한다면 다시 한번 생각하라.
Ted Neward, Principal, Neward & Associates

요약: 
Java Object Serialization은 모든 사람이 당연하게 생각하는 Java 프로그래밍의 기본 기술입니다. 
하지만 Java 플랫폼의 많은 특성과 마찬가지로 Serialization도 자세히 알수록 더욱 유용하게 사용할 수 있습니다. 
기사에서 Ted Neward는 Java Object Serialization API를 다시 한번 봐야 하는 5가지 이유와 함께 
직렬화된 데이터의 리팩토링, 암호화 및 유효성 검증을 위한 트릭(및 코드)을 설명합니다.

Java 프로그래밍에 대해 알고 있다고 생각하는가? 하지만 실제로는 대부분의 개발자가 작업을 수행하기에 충분할 정도만 알고 있을 뿐 Java 플랫폼에 대해서는 자세히 알고 있지 않다. 이 시리즈에서 Ted Neward는 Java 플랫폼의 핵심 기능에 대한 자세한 설명을 통해 까다로운 프로그래밍 과제를 해결하는 데 도움이 되는 알려져 있지 않은 사실을 밝힌다.
1년여 전 애플리케이션의 사용자별 설정 관리를 담당한 개발자는 설정 내용을 Hashtable에 저장한 후 지속성을 위해 Hashtable을 디스크에 직렬화했다. 사용자가 해당 설정을 변경하면 Hashtable은 단순히 디스크에 다시 기록되었다.
이는 일반적인 개방형 설정 시스템이었지만 팀에서 Hashtable을 Java Collections 라이브러리의 HashMap으로 이주하기로 결정할 경우 문제가 발생한다.
Hashtable과 HashMap은 디스크 양식이 서로 다르기 때문에 호환되지 않는다. @@@영구적인 각 사용자 설정에 대해 데이터 변환 유틸리티를 실행하지 않으면(임시 태스크) Hashtable은 라이프사이클의 나머지 기간 동안 애플리케이션의 저장소 형식인 것처럼 보인다.@@@
팀은 한계를 절감하게 된다. 하지만 이는 Java Serialization와 관련된 중요한(그러면서도 약간 모호한) 무엇인가를 모르기 때문이다. 그것은 바로 Java Serialization이 시간이 지나면서 유형의 확장이 가능하도록 빌드되었다는 것이다. 필자가 자동 직렬화 대체를 수행하는 방법을 보여주자 HashMap으로의 변환이 계획대로 진행되었다.
@@@이 기사는 Java 플랫폼의 유용한 기능을 밝히기 위한 시리즈의 첫 번째 기사로서 Java 프로그래밍 과제를 해결하는 데 유용한 정보를 제공한다.@@@
Java Object Serialization은 JDK 1.1부터 제공되었기 때문에 시작하기에 매우 좋은 API이다. 이 기사에서 설명하는 Serialization에 대한 5가지 사항을 알고 나면 표준 Java API라고 하더라도 다시 한번 볼 필요가 있다는 생각이 들 것이다.

Java Serialization 101
JDK 1.1의 획기적인 기능 세트의 일부로 도입된 Java Object Serialization은 Java 오브젝트 그래프를 저장 또는 전송을 위해 바이트 배열로 변환하고 나중에 다시 바이트 배열을 Java 오브젝트 그래프로 변환할 수 있는 메커니즘을 제공한다.
기본적으로 직렬화라는 개념은 오브젝트 그래프를 "고정"하고 디스크 또는 네트워크로 이동한 다음 그래프를 사용 가능한 Java 오브젝트로 "되돌리는" 것이다. 이 모든 작업은 ObjectInputStream/ObjectOutputStream 클래스, 신뢰성 높은 메타데이터 및 클래스에 Serializable 표시자 인터페이스를 태그로 지정하여 이 프로세스를 "선택"한 프로그래머의 의지로 인해 마술처럼 발생한다.
Listing 1에서는 Serializable을 구현한 Person 클래스를 보여 준다.

Listing 1. Serializable Person
---------------------------------------------------------------------
public class Person implements java.io.Serializable {
    public Person(String fn, String ln, int a) {
        this.firstName = fn; this.lastName = ln; this.age = a;
    }

    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public int getAge() { return age; }
    public Person getSpouse() { return spouse; }

    public void setFirstName(String value) { firstName = value; }
    public void setLastName(String value) { lastName = value; }
    public void setAge(int value) { age = value; }
    public void setSpouse(Person value) { spouse = value; }

    public String toString() {
        return "[Person: firstName=" + firstName + 
            " lastName=" + lastName +
            " age=" + age +
            " spouse=" + spouse.getFirstName() +
            "]";
    }    

    private String firstName;
    private String lastName;
    private int age;
    private Person spouse;
}
---------------------------------------------------------------------

Person을 직렬화한 후에는 JUnit 4 유닛 테스트에서 본 것처럼 매우 쉽게 오브젝트 그래프를 디스크에 쓰고 다시 읽어올 수 있다.


Listing 2. Person의 직렬화 해제하기
---------------------------------------------------------------------
public class SerTest
{
    @Test public void serializeToDisk() {
        try {
            com.tedneward.Person ted = new com.tedneward.Person("Ted", "Neward", 39);
            com.tedneward.Person charl = new com.tedneward.Person("Charlotte", "Neward", 38);

            ted.setSpouse(charl); charl.setSpouse(ted);

            FileOutputStream fos = new FileOutputStream("tempdata.ser");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(ted);
            oos.close();
        } catch (Exception ex) {
            fail("Exception thrown during test: " + ex.toString());
        }
        
        try {
            FileInputStream fis = new FileInputStream("tempdata.ser");
            ObjectInputStream ois = new ObjectInputStream(fis);
            com.tedneward.Person ted = (com.tedneward.Person) ois.readObject();
            ois.close();
            
            assertEquals(ted.getFirstName(), "Ted");
            assertEquals(ted.getSpouse().getFirstName(), "Charlotte");

            // Clean up the file
            new File("tempdata.ser").delete();
        } catch (Exception ex) {
            fail("Exception thrown during test: " + ex.toString());
        }
    }
}
---------------------------------------------------------------------

지금까지 살펴본 작업이 바로 Serialization 101이며, 새롭거나 흥미로운 내용은 아니지만 시작하기에 좋은 주제이다. 
이 기사에서는 Person을 사용하여 아마도 Java Object Serialization에 대해 지금까지 모르고 있었을 5가지 사항을 살펴본다.

1. Serialization은 리팩토링을 지원한다.

Serialization은 어느 정도의 클래스 변형을 허용한다. 즉, 리팩토링 후라도 ObjectInputStream은 클래스를 잘 읽는다.
중요한 점은 Java Object Serialization 스펙이 다음 사항을 자동으로 관리할 수 있다는 것이다.
클래스에 새 필드 추가하기
정적 필드를 비정적 필드로 변경하기
투명 필드를 불투명 필드로 변경하기
역으로(비정적에서 정적 또는 불투명에서 투명으로) 변경하거나 필드를 삭제하려면 필요한 이전 버전과의 호환성 정도에 따라 추가 작업이 필요하다.
직렬화된 클래스 리팩토링하기
이제 Serialization에서 리팩토링이 지원되는 것을 알았으므로 Person 클래스에 새 필드를 추가할 경우 어떤 일이 발생하는지 살펴보자.
Listing 3에서 PersonV2는 원본 Person 클래스에 성별에 대한 필드를 추가한다.

Listing 3. 직렬화된 Person에 새 필드 추가하기
---------------------------------------------------------------------
enum Gender {
    MALE, FEMALE
}

public class Person implements java.io.Serializable {
    public Person(String fn, String ln, int a, Gender g) {
        this.firstName = fn; this.lastName = ln; this.age = a; this.gender = g;
    }
  
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public Gender getGender() { return gender; }
    public int getAge() { return age; }
    public Person getSpouse() { return spouse; }

    public void setFirstName(String value) { firstName = value; }
    public void setLastName(String value) { lastName = value; }
    public void setGender(Gender value) { gender = value; }
    public void setAge(int value) { age = value; }
    public void setSpouse(Person value) { spouse = value; }

    public String toString() {
        return "[Person: firstName=" + firstName + 
            " lastName=" + lastName +
            " gender=" + gender +
            " age=" + age +
            " spouse=" + spouse.getFirstName() +
            "]";
    }    

    private String firstName;
    private String lastName;
    private int age;
    private Person spouse;
    private Gender gender;
}
---------------------------------------------------------------------

Serialization은 지정된 소스 파일의 모든 정보(메소드 이름, 필드 이름, 필드 유형, 액세스 한정자 등)를 기반으로 하는 계산된 해시를 사용하며 이 해시 값을 직렬화된 스트림의 해시 값과 비교한다.
Java 런타임에서 이 두 유형이 동일하다는 것을 확신할 수 있도록 두 번째 및 이후 버전의 Person에는 첫 번째와 동일한 직렬화 버전 해시(private static final serialVersionUID 필드로 저장됨)가 있어야 한다. 따라서 이제 필요한 것은 원본(또는 V1) 버전의 Person 클래스에 대해 JDK serialver 명령을 실행하여 계산된 serialVersionUID 필드이다.
Person의 serialVersionUID가 있으면 이제 원본 오브젝트의 직렬화된 데이터를 사용하여 PersonV2 오브젝트를 작성할 수 있다. (새 필드가 있을 경우 이러한 필드는 기본적으로 필드의 기본값에 상관 없이 대부분 "널"이다.) 또한 그 역의 경우도 가능하다. 즉, 추가 작업 없이 PersonV2 오브젝트를 사용하여 원본 Person 오브젝트를 직렬화 해제할 수 있다.

2. Serialization은 안전하지 않다.

Serialization 2진 형식이 완전히 문서화되어 있고 역으로 변환할 수 있다는 사실이 Java 개발자에게는 유쾌한 일이 아닐 것이다. 실제로 직렬화된 2진 스트림의 컨텐츠를 콘솔에서 보면 클래스의 형태와 내용을 충분히 파악할 수 있다.
여기에는 보안과 관련하여 약간 걱정되는 부분이 있다. 예를 들어, RMI를 통해 원격 메소드를 호출할 경우 네트워크를 통해 전송되는 오브젝트의 private 필드는 소켓 스트림에서 거의 일반 텍스트로 표시된다. 이는 가장 단순한 보안 사항마저도 명백하게 위반하는 것이다.
다행스럽게도 Serialization에는 직렬화 이전과 직렬화 해제 이후에 직렬화 프로세스를 "선택"해서 필드 데이터에 대한 보안 설정(또는 감추기)을 수행할 수 있는 기능이 있다. Serializable 오브젝트의 writeObject 메소드를 사용하여 이를 수행할 수 있다.
직렬화된 데이터 감추기
Person 클래스의 age 필드에 중요한 데이터가 있다고 가정하자. 신사와 숙녀라면 자신의 나이를 밝히지 않는다. 직렬화 전에 비트를 왼쪽으로 한 번 회전시켜서 이 데이터를 감춘 후 직렬화 해제 이전에 반대 방향으로 회전시켜서 복구할 수 있다. (이는 간단한 예제에 불과하며 더욱 안전한 알고리즘 개발은 독자에게 맡긴다.)
직렬화 프로세스를 "선택"하기 위해 Person에 writeObject 메소드를 구현한다. 그리고 직렬화 해제 프로세스를 "선택"하기 위해 동일한 클래스에 readObject 메소드를 구현한다. 두 메소드 모두 세부 사항을 자세히 알아야 한다. 액세스 한정자, 매개변수 또는 이름이 Listing 4와 다르면 어떠한 경고도 없이 코드가 실패하며 Person의 나이가 다른 사람에게 표시되기 때문이다.

Listing 4. 직렬화된 데이터 감추기
---------------------------------------------------------------------
public class Person
    implements java.io.Serializable
{
    public Person(String fn, String ln, int a)
    {
        this.firstName = fn; this.lastName = ln; this.age = a;
    }

    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public int getAge() { return age; }
    public Person getSpouse() { return spouse; }
    
    public void setFirstName(String value) { firstName = value; }
    public void setLastName(String value) { lastName = value; }
    public void setAge(int value) { age = value; }
    public void setSpouse(Person value) { spouse = value; }

    private void writeObject(java.io.ObjectOutputStream stream) throws java.io.IOException {
        // "Encrypt"/obscure the sensitive data
        age = age << 2;
        stream.defaultWriteObject();
    }

    private void readObject(java.io.ObjectInputStream stream) throws java.io.IOException, ClassNotFoundException {
        stream.defaultReadObject();
        // "Decrypt"/de-obscure the sensitive data
        age = age << 2;
    }
    
    public String toString() {
        return "[Person: firstName=" + firstName + 
            " lastName=" + lastName +
            " age=" + age +
            " spouse=" + (spouse!=null ? spouse.getFirstName() : "[null]") +
            "]";
    }      

    private String firstName;
    private String lastName;
    private int age;
    private Person spouse;
}
---------------------------------------------------------------------
감춘 데이터를 볼 필요가 있는 경우에는 언제라도 직렬화된 데이터 스트림/파일을 살펴볼 수 있다. 
그리고 형식이 완전히 문서화되어 있으므로 클래스를 사용할 수 없더라도 직렬화된 스트림의 컨텐츠를 읽을 수 있다.

3. 직렬화된 데이터를 서명 및 봉인할 수 있다.

앞의 팁에서는 직렬화된 데이터를 감추기만 했을 뿐이다. 데이터를 암호화하지도 않았고 데이터가 수정되지 않았음을 보장하지도 않았다. writeObject 및 readObject를 사용하여 암호화 및 서명 관리가 분명 가능하기는 하지만 더 좋은 방법이 있다.
전체 오브젝트를 암호화하고 서명할 필요가 있을 경우 가장 간단한 방법은 javax.crypto.SealedObject 및/또는 java.security.SignedObject 랩퍼에 전체 오브젝트를 포함시키는 것이다. 두 랩퍼 모두 직렬화가 가능하므로 SealedObject로 오브젝트를 랩핑하면 원본 오브젝트를 포장하고 있는 일종의 "선물 상자"가 작성된다. 암호화를 수행하려면 대칭 키가 필요하며 이 키는 개인적으로 관리해야 한다. 이와 마찬가지로 SignedObject를 사용하여 데이터를 확인할 수 있으며, 이 경우에도 대칭 키를 개인적으로 관리해야 한다.
이 두 오브젝트를 함께 사용하면 디지털 서명 확인 또는 암호화에 대한 세부 사항을 몰라도 직렬화된 데이터를 봉인하고 서명할 수 있다. 간단하지 않은가?

4. Serialization은 프록시를 스트림에 배치할 수 있다.

클래스에 클래스의 나머지 부분을 파생 또는 검색할 수 있는 데이터의 핵심 요소가 있는 경우도 있다. 그러한 경우에는 오브젝트 전체를 직렬화하지 않아도 된다. 해당 필드를 투명으로 표시할 수 있다. 하지만 클래스는 여전히 메소드가 해당 필드에 액세스할 때마다 필드가 초기화되었는지 확인하는 코드를 명시적으로 생성해야 한다.
주제가 직렬화이므로 스트림에 배치할 플라이웨이트(flyweight) 또는 프록시에 대해 알아보자. 원본 Person의 writeReplace 메소드를 제공하면 다양한 오브젝트를 적절하게 직렬화할 수 있다. 마찬가지로 readResolve 메소드도 직렬화 해제 동안에 사용된다. 이를 호출자에게 대체 오브젝트를 제공한다라고 한다.
프록시 패킹 및 언패킹하기
writeReplace 및 readResolve 메소드를 함께 사용하면 Person 클래스에서 PersonProxy를 모든 데이터(또는 일부 핵심 데이터)와 함께 패킹하여 스트림에 배치한 다음 나중에 직렬화 해제 동안 패킹을 해제할 수 있다.

Listing 5. 완성과 대체
---------------------------------------------------------------------
class PersonProxy implements java.io.Serializable {
    public PersonProxy(Person orig) {
        data = orig.getFirstName() + "," + orig.getLastName() + "," + orig.getAge();
        if (orig.getSpouse() != null) {
            Person spouse = orig.getSpouse();
            data = data + "," + spouse.getFirstName() + "," + spouse.getLastName() + ","  + spouse.getAge();
        }
    }

    public String data;
    private Object readResolve() throws java.io.ObjectStreamException {
        String[] pieces = data.split(",");
        Person result = new Person(pieces[0], pieces[1], Integer.parseInt(pieces[2]));
        if (pieces.length > 3) {
            result.setSpouse(new Person(pieces[3], pieces[4], Integer.parseInt
              (pieces[5])));
            result.getSpouse().setSpouse(result);
        }
        return result;
    }
}

public class Person implements java.io.Serializable {
    public Person(String fn, String ln, int a) {
        this.firstName = fn; this.lastName = ln; this.age = a;
    }

    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public int getAge() { return age; }
    public Person getSpouse() { return spouse; }

    private Object writeReplace() throws java.io.ObjectStreamException {
        return new PersonProxy(this);
    }
    
    public void setFirstName(String value) { firstName = value; }
    public void setLastName(String value) { lastName = value; }
    public void setAge(int value) { age = value; }
    public void setSpouse(Person value) { spouse = value; }   

    public String toString() {
        return "[Person: firstName=" + firstName + 
            " lastName=" + lastName +
            " age=" + age +
            " spouse=" + spouse.getFirstName() +
            "]";
    }    
    
    private String firstName;
    private String lastName;
    private int age;
    private Person spouse;
}
---------------------------------------------------------------------
PersonProxy는 Person의 모든 데이터를 추적해야 한다. 
이는 private 필드에 액세스하기 위해 프록시가 Person의 내부 클래스여야 한다는 것을 의미하기도 한다.
또한 프록시가 다른 오브젝트 참조(예: Person의 배우자)를 추적하고 수동으로 직렬화해야 하는 경우도 있다.
이 방법은 읽기/쓰기를 함께 사용하지 않아도 되는 몇 가지 방법 중 하나이다. 
예를 들어, 다른 유형으로 리팩토링된 버전의 클래스는 직렬화된 오브젝트를 새 유형으로 자동으로 전환하는 readResolve 메소드를 제공할 수 있다. 
마찬가지로 writeReplace 메소드를 사용하여 기존 클래스를 가져와서 새 버전으로 직렬화할 수도 있다.

5. 믿지만 검증한다.
직렬화된 데이터는 스트림에 원래 기록되었던 데이터와 항상 같다고 가정하는 것이 좋다.
하지만 전 미국 대통령의 말대로 "믿지만 검증"하는 것이 더 안전한 정책이다.
직렬화된 오브젝트의 경우 이 말은 직렬화 이후 필드의 값이 유효한지 "확인"하기 위해 필드를 유효성 검증한다는 의미이다. 
ObjectInputValidation 인터페이스를 구현하고 validateObject() 메소드를 오버라이드하여 이를 수행할 수 있다. 
이 메소드를 호출할 때 오류가 발생하면 InvalidObjectException이 발생한다.

결론
Java Object Serialization은 대부분의 Java 개발자가 인식하고 있는 것보다 훨씬 유연하며 까다로운 상황을 해결할 수 있는 충분한 기회를 제공한다.
다행스럽게도 이와 같은 코딩 기술은 JVM 어디에서나 발견할 수 있다. 중요한 것은 이러한 코딩 기술을 알고 있어야 하고 어려운 문제가 발생했을 때 활용해야 한다는 것이다.



by holyjohn | 2010/12/21 12:16 | Java - 자바 | 트랙백 | 덧글(0)

java system command call


1. exec call
 public void Mon() {
   try {
      String[] comm = {"/bin/sh", "-c", "ps -ex | grep XXX > pslist.txt" };
      Process ps = Runtime.getRuntime().exec(comm );
.....

2. memory use

public class sample {
String lm_sPs;

public sample() {
lm_sPs = "ps -ex";
};

public void Mon() {
try {
Process ps = Runtime.getRuntime().exec(lm_sPs);
byte[] msg = new byte[1024];
String str;
int len;
while ((len = ps.getInputStream().read(msg)) > 0) {
str = new String(msg).substring(10);
System.out.println(str);
}
System.out.println("수행결과: " + ps.exitValue());
} catch (Exception e) {
e.printStackTrace();
}
return;
}

public static void main(String[] arg) {
sample mon = new sample();
mon.Mon();
}
}

by holyjohn | 2010/11/18 10:45 | Java - 자바 | 트랙백 | 덧글(0)

동일 컨테이너상 서로다른 webapp 선언으로 인한 문제해결


동일 작업대상을 협업으로 개발할때 log4j설정중 로그파일 위치는 각 개발자간 서로다른 경로상에 위치하게 된다.

웹 base개발인 경우 본인은 이 문제를 해결하기 위해
web.xml의 context-param 을 통해서 각 개발자간 상이한 환경에서도 동적으로 webroot경로를
얻기 위해 다음과 같이 정의하여 사용한다.
web.xml
    <context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>webapp.root</param-value>
    </context-param>

log4j.xml
    <appender name="DAILY" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="datePattern" value="'.'yyyy-MM-dd" />
        <param name="file" value="${webapp.root}/WEB-INF/logs/epms.log" />
        <param name="append" value="true" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%t> [%d{yyyy-MM-dd HH:mm:ss}] [%c{1}] [%L] [%p] %m %n"/>
        </layout>
    </appender>

 단일 컨테이너(WAS)상에 위의 형태를 사용하는 web app가 한개인 경우는 아무런 문제 없이 작동한다. 
하지만 두 개 이상의 web app가 구동되면 여지없이 기동시에 에러를 발생시킨다.

이유인즉 context-param에 선언되는 webAppRootKey name의 경우
system property의 key로 설정된다는것이다.
즉, System.getProperty("webapp.root") 이렇게 사용될 수 있다는것이다.
그러므로 서로다른 webapp내에 context-param이라 할지라도
system property로 활용되기에 중복된 key값을 사용할 수 없으므로 나타나는 에러인것이다.
어쩔수 없이 context-param의 value는 유니크한 값으로 선언할 수 밖에 없을것 같다.
다음과 같이 말이다. ㅡㅡ

webapp1
    <context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>first.webapp.root</param-value>
    </context-param>

    <appender name="DAILY" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="datePattern" value="'.'yyyy-MM-dd" />
        <param name="file" value="${first.webapp.root}/WEB-INF/logs/epms.log" />
        <param name="append" value="true" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%t> [%d{yyyy-MM-dd HH:mm:ss}] [%c{1}] [%L] [%p] %m %n"/>
        </layout>
    </appender>

webapp2
    <context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>second.webapp.root</param-value>
    </context-param>

    <appender name="DAILY" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="datePattern" value="'.'yyyy-MM-dd" />
        <param name="file" value="${second.webapp.root}/WEB-INF/logs/epms.log" />
        <param name="append" value="true" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%t> [%d{yyyy-MM-dd HH:mm:ss}] [%c{1}] [%L] [%p] %m %n"/>
        </layout>
    </appender>

by holyjohn | 2010/11/17 10:44 | 트랙백 | 덧글(0)

톰캣에 SSL 등록하기



Tomcat은 자바의 "keystore" 를 이용하여 인증서를 설정하므로 Tomcat으로
SSL 웹서버를 운영하려는 서버에 JDK(Java Development Kit) 1.4 버전 이상의
프로그램이 먼저 설치되어 잇어야 합니다.

테스트 환경은 다음과 같다.
tomcat 5.5
jdk 1.5
windows7

SSL 사설인증서(보안서버인증서) 발급과 JKS 등록방업은 
을 참조한다.

<Connector SSLEnabled="true" clientAuth="want"
 keystoreFile="c:/cert/sslserver.jks" keystorePass="1q2w3e4r"
 maxThreads="150" port="8443" protocol="HTTP/1.1" scheme="https"
 secure="true" sslProtocol="TLS" />

jks를 첨부한다


by holyjohn | 2010/11/02 10:43 | 트랙백 | 덧글(0)

톰캣 포트를 다르게 하여 호스팅하기


톰캣에서도 다른 웹서버 처럼 포트 번호에 따라 호스팅하는 것이 가능하다.

 아파치와 연동없이 톰캣만으로도 하나의 IP로 다수의 웹사이트를 운영하는 것이 가능하다.
(아파치와 톰캣을 연동하는 방법에 대해서는 차후에 자세히 올리도록 하겠다)

다수의 웹 사이트를 세팅하는 방법은 크게 2가지가 있다.

가상호스트를 이용하는 방법
IP Address의 port를 여러개 사용하는 방법

가상호스트를 이용하는 방법은 도메인을 이용하여 실제로 서비스를 운영하는 경우다. 
가상 호스트를 이용하여 톰캣에서 여러개의 웹사이트를 운영하는 방법에 대해서 설명하고자 한다.
설치 환경은 다음과 같다.

O/S : Windows XP (Windows Server 동일)
Tomcat 6.0.10
설명의 편의를 위해 톰캣의 설치 디렉토리는 'TOMCAT_HOME' 으로 표기할 것이다. 참고로 내 경우는 C:\Server\Tomcat6.0 이다.

설정하는 방법은 /TOMCAT_HOME/conf/에 있는 server.xml 파일만 수정하면 된다. 
server.xml의 쓸데없는 주석부분을 다 없애고 관련 부분만 남겨놓으면 아래와 같다. 

<Service name="Catalina">
      <Connector port="8080" protocol="HTTP/1.1"
       maxThreads="150" connectionTimeout="20000" redirectPort="8443" />
      <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
      <Engine name="Catalina" defaultHost="localhost">
       <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
        resourceName="UserDatabase"/>

       <Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="true"
        xmlValidation="false" xmlNamespaceAware="false">
       </Host>

      </Engine>
     </Service>

위의 내용에서 핵심 부분은 <Host></Host> 영역이다.

1. 우선 <Connector port="8080" protocol="HTTP/1.1" 부분의 port를 80으로 수정한다.
도메인명이 기본으로 80포트를 사용하기 때문이다.

2. <Host>... </Host> 에 해당하는 부분을 복사하여 2개를 만든다. 
   appBase="webapps" 는 톰캣의 기본 웹루트인 TOMCAT_HOME/webapps 디렉토리를 가르킨다. 
   웹사이트를 원하는 디렉토리에 두고 싶다면 d:/webapps/myweb1 처럼 자기가 지정하고 싶은 곳으로 수정하면 된다.

       <Host name="service.myweb.com"  appBase="f:/webapps/myservice" unpackWARs="true" autoDeploy="true"
        xmlValidation="false" xmlNamespaceAware="false">
       </Host>

       <Host name="portal.myweb.com"  appBase="f:/webapps/myportal" unpackWARs="true" autoDeploy="true"
        xmlValidation="false" xmlNamespaceAware="false">
       </Host>

3. 마지막으로 웹사이트들의 ROOT 디렉토리를 지정해주자. 아래의 폴더를 생성한다.

f:/webapps/myservice/ROOT/ 
f:/webapps/myservice/ROOT/WEB-INF/

f:/webapps/myportal/ROOT/ 
f:/webapps/myportal/ROOT/WEB-INF/

   그리고 WEB-INF 폴더 밑에 각각 web.xml 파일을 추가한다. 
   그냥 /TOMCAT_HOME/webapps/ROOT/WEB-INF/에 있는 web.xml 을 복사하면 된다.

4. 테스트하기 위해 ROOT/index.html 또는 index.jsp를 만든다.

5. 이제 톰캣을 재시작하고 웹브라우저로 접속해 보자.
http://service.myweb.com
http://portal.myweb.com


이제 두번째 방법인 IP 어드레스의 포트를 이용하는 방법에 대해서 설명하겠다.

설치 환경은 동일하고 설정하는 방법도 간단하다. 
/TOMCAT_HOME/conf/server.xml 파일만 수정한다.

<Service name="Catalina">
  <Connector port="8080" protocol="HTTP/1.1" 
   maxThreads="150" connectionTimeout="20000" redirectPort="8443" />
  <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
  <Engine name="Catalina" defaultHost="localhost">
   <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
    resourceName="UserDatabase"/>

   <Host name="localhost"  appBase="f:/webapps/myservice" unpackWARs="true" autoDeploy="true"
    xmlValidation="false" xmlNamespaceAware="false">
   </Host>

  </Engine>
 </Service>

Connector port="8080"은 HTTP로 넘어오는 포트를 지정하는 것이다. 

Host 지시어의 appBase="webapps" 는 웹어플리케이션(웹사이트)의 위치를 나타낸다. 
appBase="./webapps"와 같은 의미다. 실제 위치는 TOMCAT_HOME/webapps이다. 
물론 "d:/weapps/myweb1" 과 같이 절대경로로 지정하는 것도 가능하다.

그럼 웹사이트를 하다 더 추가하고 싶다면 위의 <Service>...</Service>를 하나 더 만들면 된다. 
위의 코드를 복사한 다음 server.xml 에 추가한다. 


<Service name="Catalina2">
  <Connector port="9090" protocol="HTTP/1.1" 
     maxThreads="150" connectionTimeout="20000" redirectPort="8443" />
  <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
  
  <Engine name="Catalina" defaultHost="localhost">
   <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
 
   <Host name="localhost"  appBase="f:/webapps/myportal"
    unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
   </Host>
  </Engine>
 </Service>

다른 웹어플리케이션을 돌리기 위해 서비스를 하나 더 추가한 것이다.
port="9090" 은 새로 추가하고 싶은 포트이다. 
appBase="f:/webapps/myportal"는 9090 포트에서 돌아갈 웹사이트 위치이다.

이제 server.xml 설정은 끝난 것이다. 
마지막으로 웹사이트의 ROOT 디렉토리를 지정해주자. 아래의 폴더를 생성한다.

f:/webapps/myportal/ROOT/   (
f:/webapps/myportal/ROOT/WEB-INF/ 
(WEB-INF 폴더를 만들고 web.xml 파일을 추가한다. 그냥 /TOMCAT_HOME/webapps/ROOT/WEB-INF/에 있는 web.xml 을 복사하면 된다.

테스트하기 위해 ROOT/index.html 또는 index.jsp를 만든다.
이제 톰캣을 재시작하고 웹브라우저로 접속해 보자.


by holyjohn | 2010/11/02 10:14 | Java - 자바 | 트랙백 | 덧글(0)

설정파일 관리방법 정리

 

 

config 덮어쓰기를 활용한 각 서버별 설정 지정하기#

절차#

  1. java -Drunlevel=[real | test | staging ]

    1. 예) -Drunlevel=real
  2. applicationContext.xml 파일에 PropertyPlaceholderConfigurer 설정

    1. <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
          <property name="ignoreUnresolvablePlaceholders" value="true" />
      </bean>
      <bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
          <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
          <property name="searchContextAttributes" value="true" />
          <property name="contextOverride" value="true" />
          <property name="ignoreResourceNotFound" value="true" />
          <property name="locations">
              <list>
                  <value>classpath:spring/connection.properties</value>
                  <value>classpath:spring/noti.properties</value>
                  <value>classpath:spring/${runlevel}.properties</value>
              </list>
          </property>
      </bean>
  3. xxx.properties를 생성하고 덮어쓸 속성을 선언해줌.

    1. jdbc.driverClassName=core.log.jdbc.driver.OracleDriver
      jdbc.url=jdbc:oracle:thin:@10.98.98.41:1521:POLARDB
      jdbc.username=adquest
      jdbc.password=adquestdb

 

해설#

  • -Drunlevel 설정을 해주면 해당 설정을 읽어들여서 덮어쓰고, 안해주면 그냥 무시하고 넘어간다.

    • 덕분에 각 개발pc에서는 별도 설정 없이 정상동작이 가능하고
    • 각 [ real | test | staging ] 서버군에서는 적절한 설정파일을 추가해줘서 실행이 가능하다.
    • -Drunlevel 옵션이 지정될 경우에만 해당 파일들이 읽혀지기 때문에
      미리 classpath:real.properties | classpath:test.properties 등등의 파일들을 만들어 놓아도 문제 없다.
    • 각 서버군에서는 JAVA_OPTS를 지정하기 위한 설정파일(주로 catalina.sh 혹은 startup.sh)에 한줄만 추가해 주면 된다.
  • runlevel

    • 각 runlevel 별로 설정이 바뀌어야 할 내용들을 지정해준다.
    • real 안에서도 서버군에 따라 바뀌어야 할 내용이 있으면 적절하게 이름을 지어서 properties 파일을 생성해준다.
  • applicationContext.xml

    • -Drunlevel=xxx 설정을 해주지 않으면 옵션에 의해 ${runlevel}이 있는 설정을 무시하고 넘어감.

      • 아래와 같은 오류 메시지들이 나옴
      • [20100714 22:42:14] [INFO ] Bean factory for application context [org.springframework.web.context.support.XmlWebApplicationContext@7bd33a6b]: org.springframework.beans.factory.support.DefaultListableBeanFactory@d34eb84
        Could not resolve placeholder 'runlevel' in [classpath:spring/${runlevel}.properties] as system property: neither system property nor environment variable found
        ...
        [20100714 22:42:15] [INFO ] Loading properties file from class path resource [spring/${runlevel}.properties]

 

config 변수 설정 방법#

java -Dxxx=yyyy#

  • 실행 시점에서 system property를 지정한다.
  • 런타임에 아래와 같은 코드로 사용할 수 있다.
  1. System.getProperty( "xxx" )
  2. System.getProperties().getProperty( "xxx" )
-Dproperty=value

Set a system property value. Ifvalueis a string that contains spaces, you must enclose the string in double quotes:

                  java -Dfoo="some string" SomeClass          

applicationContext.xml에서 xxx.properties 읽어들이기#

Basic#
  • 하나의 설정파일 읽어들이기
  1. <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="/WEB-INF/config/jdbc.properties" />
    </bean>
  • 여러개의 설정파일 읽어들이기
  1. <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>/WEB-INF/config/jdbc.properties</value>
                <value>/WEB-INF/config/settings.properties</value>
            </list>
        </property>
    </bean>
ADVANCED : polaris에서 사용한 설정 덮어쓰기.#
  • ${db-config}에 들어갈 설정파일을 java -Ddb-config=xxx.properties 형태로 선언함으로써
  1.     <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="ignoreUnresolvablePlaceholders" value="true" />
        </bean>
  2.     <bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
            <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
            <property name="searchContextAttributes" value="true" />
            <property name="contextOverride" value="true" />
            <property name="ignoreResourceNotFound" value="true" />
            <property name="locations">
                <list>
                    <value>classpath:spring/connection.properties</value>
                    <value>classpath:spring/noti.properties</value>
                    <value>${db-config}</value>
                </list>
            </property>
        </bean>

 

web.xml : applicationContext.xml 지정하기#

  • applicationContext.xml 파일을 읽어들일 위치는 web.xml 파일에서 지정한다.
  1.     <context-param>
            <description>Spring 설정 파일 지정</description>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/settlementContext.xml,classpath:spring/applicationContext.xml,classpath:spring/reportContext.xml,classpath:spring/frontContext.xml,classpath:spring/security.xml</param-value>
        </context-param>

 

기본 system properties 참조하기.#

  1. System.getProperties()
  • Properties 클래스의 인스턴스 형태로 존제함.
  • Java 코드 어디에서나 참조가 가능하다.
  • applicationContext.xml 파일 안에서도 참조가 가능하다.
  • action-servlet.xml 파일에서는 참조가 안된다.
  • web.xml 파일에서도 읽혀진다.

    • 테스트방법 :
      web.xml 파일에서 servlet 설정의 servlet-class 요소 값을 ${dispatcher}로 선언해둔 뒤에
      launcher의 arguments에서 -Ddispatcher="org.springframework.web.servlet.DispatcherServlet" 설정을 추가해준다.
  1. $ web.xml
  2.     <servlet>
            <description>Spring MVC Dispatcher</description>
            <servlet-name>spring</servlet-name>
            <servlet-class>${dispatcher}</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
    ...
  3. $ catalina.sh or startup.sh or Eclipse의 Edit launch configuration properties > Arguments > VM arguments 설정.
    java ... -Ddispatcher="org.springframework.web.servlet.DispatcherServlet"

 

Tips#

설정파일의 ${} ?#

  • 시스템변수 혹은 xxx.properties 파일의 속성을 읽어들일 때 사용함.
  • applicationContext.xml 파일에서의 확인방법

    • <bean>을 이용해서 객체를 하나 선언하면서 해당 객체의 setter에 ${..} 변수를 할당해 둔 뒤, 디버거로 해당 setter에 중단점을 찍어둔다.
  1.     <bean id="memberTO" class="net.daum.adquest.user.to">
            <property name="name" value="${db-config}"/>
            <property name="userid" value="${java.runtime.name}"/>
        </bean>
  • spring-servlet.xml

    • 참조할 수 없다!!
    • applicationContext.xml과 같은 방법으로 확인해보면 ${java.runtime.name}과 같은 변수를 참조할 수 없음을 확인할 수 있다!!

 

classpath:...?#

  • 설정을 읽어들일 때 WEB/classes 경로를 참조하는 키워드이다.
  • web.xml > context-param > contextConfigLocation
  1.     <context-param>
            <description>Spring 설정 파일 지정</description>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/settlementContext.xml,classpath:spring/applicationContext.xml,classpath:spring/reportContext.xml,classpath:spring/frontContext.xml,classpath:spring/security.xml</param-value>
        </context-param>

by holyjohn | 2010/10/01 15:52 | Java - 자바 | 트랙백 | 덧글(0)

이클립스 SVN 에서의 계정 캐시 정보


이클립스에서 SVN 연결 쓰다보면

이상하게 사용자 계정 정보 캐시를 지우는게 없다.

이부분은 SVNKit을 쓴다는데...

윈도우는 %APPDATA%\Subversion\auth
Linux나 OSX에서는  ~/.subversion/auth

에 정보들이 있다...

svn.simple
svn.ssl.client-passphrase
svn.ssl.server
svn.username

이 폴더들 중에서 svn.simple 보면 연결 정보들이 있다.

지우거나 변경하면 될듯...


by holyjohn | 2010/09/01 10:16 | 이클립스-Eclipse | 트랙백 | 덧글(0)

남자의 자격 - 합창편[배다해]

소름이 돋다 돋아 오늘같이 돋게 만드는 청아함.

굵디 굵다못해 좀 쨰지는 내 목소리는 그저 부러울 뿐..ㅠ_ㅠ

출처 : 
http://cuepoe.tistory.com/31

Nella Fantasia - IL DIVO 도 들어보고

사라브라이트만 꺼도 들어보면 좋네요

by holyjohn | 2010/08/30 09:13 | 트랙백 | 덧글(0)

iBatis CacheModel(캐시모델)

개발자들이 Application에 도입하는 대부분의 캐싱은 오랜 시간동안 변경되지 않는 데이터를 위한 것이다. 그러나 의외로 캐싱된 데이터가 쓰기에 의해 변경되는 경우도 있다.

개발자들은 나름대로 캐싱모델을 정의하고, 구현하여 사용한다. 그러나 캐싱 될 데이터를 추가하고, 더 이상 사용되지 않는 데이터는 삭제하고, 정해진 시간별로 캐싱 된 데이터를 삭제하는 등의 많은 기능들을 정의하고 구현하여 사용하려고 하는 솔직히 쉬운 일도 아니고, "노력 대비 성능" 또한 보장할 수가 없다.

그래서 요즘 많은 ORM 솔루션들이 데이터 캐싱을 지원한다. iBatis 또한 여타 ORM 솔루션과 같이 데이터 캐싱을 지원한다. 그러나 iBatis의 데이터 캐싱은 다른 ORM 솔루션의 데이터 캐싱과는 조금 개념이 다르다. 여타의 ORM 솔루션들은 주로 DataBase Table 객체에 매핑하는 데 중점을 두고, iBatis는 SQL 구문을 객체에 매핑하도록 되어 있다.


-- CacheModel 이해하기

CacheModel은 iBatis의 모든 캐시 구현체를 정의하는 기반이 되는 곳이다. SQL Maps 설정 안에서 캐시 모델 설정을 정의하고 하나 이상의 쿼리 매핑 구문이 이를 사용할 수 있다.


* CacheModel 속성들 *

id (필수) : 유일한 ID를 지정. CacheModel에 설정 된 캐시를 사용하고 하는 쿼리 매핑 구문에서 ID를 참조한다.

type (필수) : 이 값은 CacheModel이 설정하는 캐시의 타입을 의미한다. 사용 가능한 값으로 MEMORY, LRU, FIFO, OSCACHE가 있다. 이 속성은 사용자 정의 CacheController 구현체의 완전한 클래스 이름으로 지정해도 된다.

readOnly (선택) : 읽기전용 여부. 이 값을 true로 지정하면 캐시가 읽기 전용 캐시로 사용될 것임을 의미한다. 읽기 전용 캐시에서 가져온 객체는 객체의 Property들을 변경할 수 없다.

serialize (선택) : 캐시의 내용을 가져올 때 객체의 모든 값을 복사하여 새로운 객체를 생성하여 전달할지 여부를 지정한다.


여기서 readOnly 속성 값에 대해 좀더 알아보면 readOnly 속성은 단순히 CcheModel에게 캐시된 객체를 어떻게 가져와서 저장할지 알려주는 지시자이다. 이 속성값이 true이면 가져온 내용을 변경해도 CacheModel은 변경되지 않는다. 만약 false로 지정되면 주의해야 하는데 두 명 이상의 사용자가 캐시된 참조의 동일한 인스턴스를 가져갈 수 없음을 의미한다.


* 내장 CacheModel 타입들 *

MEMORY : 단순하게 캐시된 데이터를 가비지 컬렉터가 삭제할 때까지 메모리에 저장한다.

FIFO : 고정 된 크기의 모델로 "first in first out (먼저 들어간 값을 먼저 삭제)" 알고리즘을 사용하여 메모리에서 캐시 항목들을 삭제한다.

LRU : FIFO와 다른 고정 된 크기의 모델로 "least recently used (최근에 가장 오랜동안 사용하지 않은 값을 캐시에서 먼저 삭제)" 알고리즘을 사용하여 메모리에서 캐시 항목들을 삭제한다.

OSCACHE : OpenSymphony Cache를 사용한다. (OSCashe Lib와 설정이 함께해야 한다.)


* readOnly와 serialize 속성의 조합에 대한 요약 *

readOnly (true) / serialize(false) : 성능 좋음. 캐시 된 객체를 가장 빠르게 가져온다. 캐시 된 객체의 공유 인스턴스를 반환하며, 잘못 사용하면 문제를 일으킬 수도 있다.

readOnly (false) / serialize(true) : 성능 좋음. 캐시 된 객체를 빠르게 가져온다. 캐시 된 객체를 깊은 복사 작업을 통해 가져온다.

readOnly (false) / serialize(false) : 주의요망! 캐시는 오직 호출하는 스레드의 세션이 살아있는 동안에만 관련되고, 다른 쓰레드는 사용할 수 없다.

readOnly (true) / serialize(true) : 성능 나쁨. 무의미한 설정이라는 점을 제외하면 readOnly (true) / serialize(true)와 동일하게 작동한다.


readOnly=true이고 serialize=false이면 캐시 된 객체가 전역적으로 공유되기 때문에 모든 사용자는 다른 세션에서 부적절하게 변경 된 객체를 가져오게 될 수 있는 문제의 소지가 있다. readOnly=false이고 serialze=true를 사용하면 캐시에서 가져온 객체의 깊은 복사작업의 결과를 가져오기 때문에 캐시에서 가져온 객체가 값은 비록 같지만 동일한 인스턴스가 아니라는 의미이다. 이것은 캐시에서 가져온 객체의 변경사항이 호출한 세션 안에서만 작용된다.


-- 캐시 비우기 (Cache Flushing)

사용자가 정의 한 CacheModel은 캐시된 데이터를 모두 비울때 사용하는 공통적인 요소를 가지고 있다. 이것은 이전에 캐시된 모든 데이터를 비우고 새롭게 데이터를 캐시하게 된다.


* 캐시를 비우는 flush 요소들 *

<flushOnExecute> : 지정 된 쿼리 매핑 구문이 실행되면 캐시의 모든 데이터를 비운다.

<flushInterval> : 캐시를 비우는 시간 간격을 정의한다.


<flushOnExecute>

속성으로 statement 하나만을 가지며, 이 속성에 지정된 매핑 구문이 실행이 될 때 자동으로 캐시의 데이터를 모두 비운다. 예를 들어 상품카테고리의 카테고리가 추가, 수정, 삭제 될면 이전에 캐시된 신빙성이 떨어지는 데이터를 모두 비우게 할 수 있다.

그러나, 이 속성은 캐시된 데이터를 모두 비우고 새롭게 캐시를 하게 되므로 데이터를 자주 변경하는 매핑 구문에 의존하게 되면 캐시의 효율성이 떨어지게 된다.


   <SqlMap namespace="category">

      <cacheModel id="categoryCache" type="MEMORY">

         ...

         <flushOnExecute statement="category.insert" />

      </cacheModel>

      ...

      <insert id="insert" parameterClass="java.util.Map">

         ...

      </insert>


<flushInterval>

<flushOnExecute> 보다 좀더 간편하게 사용할 수 있는 요소로 시간에 의존하여 지정한 시간이 경과되면 반복적으로 캐시된 데이터를 모두 비운다. 가격은 시(hours),분(minutes),초(seconds) 또는 밀리초(milliseconds)로 지정할 수 있다. <flushInterval>은 하나의 속성만을 허용하기 때문에 5시간 13분 40초와 같이 지정하고 싶다면 초로 계산하여 지정해야 한다. 그리고, 특정 시간을 지정할 수는 없다.


   <cacheModel id="categoryCache" type="MEMORY">

      ...

      <flushInterval hours="12" />

   </cacheModel>


-- CacheModel Properties 설정하기

CacheModel은 플러그인의 형태로 제공되기 때문에 임의의 설정 값을 제공할 수 있는 방법이 필요한데 <property>를 이용하여 처리한다.


* <property> 요소의 속성들 *

name : 필수입력으로 설정할 프로퍼티의 이름

value : 필수입력으로 설정할 프로퍼티의 값


-- CacheModel Type

앞에서 언급했듯이 iBatis에서 사용할 수 있는 CacheModel Type은 MEMORY, LRU, FIFO, OSCACHE 4가지가 있다.


MEMORY

객체 참조를 기반으로 한 캐시이다. 캐시 내의 각 객체의 참조 타입(<reference-type>) 속성을 갖고 있다. 객체의 참조 타입은 가비지 켈렉터에게 객체를 어떻게 다룰지에 대한 힌트를 제공한다. MEMORY CacheModel은 객체에 접근하는 방식보다는 메모리 관리에 중점을 둔 Application에 적합하다.


* MEMORY CacheModel 참조 타입 *

WEAK : 캐시된 데이터를 빨리 비운다. 기본설정 값이며 가비지 켈렉터에 의해 수거되는 것을 막지않고 놔둔다. 이 방식은 일관성 있게 객체에 접근하는 캐시를 사용할 때 잘 적동한다. 캐시를 비우는 비율이 빠른편이기 때문에 메모리 제한을 넘기지 못하도록 보장은 하나 DataBase 접근확률이 높아진다.

SOFT : 메모리 용량이 허락하는 한 캐시된 객체를 보관한다. 가비지 켈렉터는 더 많은 메모리가 필요하다고 판단되기 전까지는 이 객체들을 수거하지 않는다. 메모리 제한을 넘기지 못하도록 보장하며, WEAK 참조보다 DataBase 접근확률은 적은 편이다.

STRONG : 메모리의 한계가 얼마든지 관계없이 객체를 계속 보관한다. 가비지 켈렉터에 의한 수거는 없다. 이 참조타입은 정적이고 작고 장기적으로 사용할 객체를 캐시할 때 유용하다. DataBase 접근확률은 최소할 수 있으나, 캐시되는 객체의 용량이 너무 커져서 메모리 부족이 발생될 수 있다.


   <cacheModel id="categoryCache" type="MEMORY">

      <flushInterval hours="12" />

      <flushOnExecute statement="insert" />

      <property name="reference-type" value="WEAK" />

   </cacheModel>


LRU

가장 최근에  가장 오랫동안 사용되자 않은 것을 제거하는 방식으로 캐시를 관리한다. 캐시의 객체를 제거하는 것은 오직 캐시의 용량이 제한을 넘겼을 때 한번만 발생된다. 특정 객체에 아주 빈번하게 접근하는 캐시를 관리할 때 매우 적합하다. <property> 요소를 사용하여 지정할 수 있는 프로퍼티는 size 하나로 캐시에 저장될 수 있는 최대 개수를 지정한다.


   <cacheModel id="categoryCache" type="LRU">

      <flushInterval hours="12" />

      <flushOnExecute statement="insert" />

      <property name="size" value="200" />

   </cacheModel>


FIFO

먼저 캐시 된 객체를 먼저 삭제한다. 캐시의 객체를 제거하는 것은 오직 캐시의 용량이 제한을 넘겼을 때 한번만 발생된다. 생존기간에 기반을 두고 있기 때문에 초기에 캐시에 저장되는 그 순간에 더 많이 사용되는 객체를 캐싱할때 효과적이다. <property> 요소를 사용하여 지정할 수 있는 프로퍼티는 size 하나로 캐시에 저장될 수 있는 최대 개수를 지정한다.


   <cacheModel id="categoryCache" type="FIFO">

      <flushInterval hours="12" />

      <flushOnExecute statement="insert" />

      <property name="size" value="200" />

   </cacheModel>


OSCACHE

Open Symphoy(http://www.opensymphony.com/oscache)의 OSCache를 사용한다.OSCACHE를 사용하기 위해서는 OSCache 라이브러리와 설정정보가 필요하다.


  <cacheModel id="categoryCache" type="OSCACHE">

      <flushInterval hours="12" />

      <flushOnExecute statement="insert" />

   </cacheModel>


사용자가 만든 캐시 모델

앞에서 언급했듯이 iBatis의 캐시 모델은 플러그인되는 형태여서 사용자가 캐시모델을 만들어서 사용할 수 있다. 사용자가 자신의 캐시모델을 만들기 위해서는 com.ibatis.sqlmap.engine.cache.CacheController 인터페이스를 구현하면 되고, 이름은 alias를 사용하면 된다.

by holyjohn | 2010/08/27 10:59 | Java - 자바 | 트랙백 | 덧글(0)

자바 프로젝트를 웹프로젝트로 바꾸기

먼저 이클립스의 프로젝트 설정파일인 ".project" 파일을 열어서 편집하면 된다.

다음의 natures 엘리먼트만 추가한다.
  <nature>org.eclipse.wst.common.project.facet.core.nature</nature>  <nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
그리고 이클립스로 가서 프로젝트 설정 - properties - Project Facets로 가서
Java 와 Dynamic Web Module facets 를 선택하면 끝

by holyjohn | 2010/08/27 09:46 | 이클립스-Eclipse | 트랙백 | 덧글(0)

웹 표준 프레임워크


요즘 웹 프레임워크 보다가 정부 표준 프레임워크가 있음을 보았다...
스프링 기반에 쓸만한 놈들 모아놓은 것 같다.

http://www.egovframe.org/

일단 정리가 잘되어 있으니 볼만하고

Acegi(Spring security) Framework 도 인증과 인가를 지원하니 쓸만한듯...


공부 할것은 많고 

자바 진영은 이제 뭘 개발해야 하나...
웹은 프레임워크 안정적으로 되어 가고 자동화가 무지하게 잘되고 있고
데몬도 잘되어 있고
기반은 거의 완전하게 오픈 소스로 되고 있는듯....

by holyjohn | 2010/08/19 11:59 | Java - 자바 | 트랙백 | 덧글(0)

maven 설치 및 설정하기...


오래만에 메이븐 설정하려고 하니 잘 기억이 안난다...


1. 메이븐 클라이언트 설치하고
2. 메이븐 클라이언트 환경설정 하고
3. 로컬 리포지터리 설정하고
4. 라이브러리를 로컬 리포지터리에 저장하고

또 필요한 경우에 메이븐 리포지터리 서버를 구축하면 되겠당...

근데 서버 구축은 잘 안되는듯... 게이트웨이 방식이 잘 안되고 있다...

왜 잘 안될까...흑흑...

2. 환경 잡아주자
M2=d:\apache-maven-2.2.1\bin
M2_HOME=d:\apache-maven-2.2.1

윈도우7의 경우
M2_HOME/conf/settings.xml 열어서 (없는경우 생성)

<settings ...>
<localRepository>d:\apache-maven-2.2.1\repo</localRepository>
</settings>

3. mvn 로컬 저장소에 jar넣는명령어 
* 명령옵션
mvn install:install-file
  -Dfile=<path-to-file>
  -DgroupId=<group-id>
  -DartifactId=<artifact-id>
  -Dversion=<version>
  -Dpackaging=<packaging>
  -DgeneratePom=true

Where: <path-to-file>  the path to the file to load
       <group-id>      the group that the file should be registered under
       <artifact-id>   the artifact name for the file
       <version>       the version of the file
       <packaging>     the packaging of the file e.g. jar

 예) mvn install:install-file -Dfile=ytl-photo-0.6.1.jar -DgroupId=ytl -DartifactId=photo -Dpackaging=jar -Dversion=0.6.1 -DgeneratePom=true

프로젝트 pom.xml 파일로 돌아와서 아래 항목 추가하면 끝
<dependency>
<groupId>com.ytl</groupId>
<artifactId>photo</artifactId>
<version>0.6</version>
</dependency>

by holyjohn | 2010/08/03 15:34 | 개발 | 트랙백 | 덧글(0)

sendRedirect()와 getRequestDispatcher() 차이점


1. response.sendRedirect("/sso/LoginOK.jsp");
 전송되는 페이지의 HTTP HEADER에 리다이렉트 정보를 담아 보낸다. 
 브라우저가 HEADER 정보를 분석한 후 원하는 URL로 리다이렉트 시킨다. 
 sendRedirect 이후에 세션, 쿠키, 헤더 정보를 조작하는 로직이 있다면 
 Cannot create a session after the response has been committed 에러를 내뱉는다. 
 비지니스 로직에서 sendRedirect를 하게 된다면 return 시키자. 
 경로에는 컨텍스트명을 포함한 절대 경로나 풀 주소를 적어주어야 한다.

2. request.getRequestDispatcher("/helloWorld.jsp").forward(request, response);

해당 코드가 실행되는 순간 이동할 페이지를 강제로 읽어들여 리다이렉트 한다. 
브라우저에게 의사 결정권이 없으며 이후의 코드가 무시된다. 
sendRedirect와 같은 기능을 하지만 sendRedirect는 request/response를 잃는 반면 
getRequestDispatcher의 경우 재활용 한다. 경로에는 컨텍스트명을 제외한 경로를 적는다.

by holyjohn | 2010/08/02 10:20 | 트랙백 | 덧글(0)

request.getRealPath() 메서드 deprecate되었네요


request.getRealPath() 메서드를 사용할려고 보니 deprecate되었다네요...

이유는 ServletContext에 있어서 중복이라서 그렇다고 한다.

request.getSession().getServletContext().getRealPath("")

이렇게 써야 합니다...


by holyjohn | 2010/08/02 09:58 | Java - 자바 | 트랙백 | 덧글(0)

JAVA + SSL Tutorial (server and client examples)

Certificate

First we need to make certificate, this is done by using keytool that is part of J2SE SDK (program will ask for certificate owner information and password, enter 123456 as

password
password, or you can enter your password, but notice that you have to change it in other commands listen in this tutorial):

keytool -genkey -keystore mySrvKeystore -keyalg RSA

After this command you will have certificate file in working directory of issuing keytool command.

Server source code (EchoServer.java)

import javax.net.ssl.SSLServerSocket;import javax.net.ssl.SSLServerSocketFactory;import javax.net.ssl.SSLSocket;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;publicclass EchoServer {    public            static    void            main(String[] arstring) {        try {            SSLServerSocketFactory sslserversocketfactory =                    (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();            SSLServerSocket sslserversocket =                    (SSLServerSocket) sslserversocketfactory.createServerSocket(9999);            SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();            InputStream inputstream = sslsocket.getInputStream();            InputStreamReader inputstreamreader = new InputStreamReader(inputstream);            BufferedReader bufferedreader = new BufferedReader(inputstreamreader);            String string = null;            while ((string = bufferedreader.readLine()) != null) {                System.out.println(string);                System.out.flush();            }        } catch (Exception exception) {            exception.printStackTrace();        }    }}      

Compile it by using simple command:

javac EchoServer.java

Client source code (EchoClient.java)

import javax.net.ssl.SSLSocket;import javax.net.ssl.SSLSocketFactory;import java.io.*;publicclass EchoClient {    public            static    void            main(String[] arstring) {        try {            SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();            SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("localhost", 9999);            InputStream inputstream = System.in;            InputStreamReader inputstreamreader = new InputStreamReader(inputstream);            BufferedReader bufferedreader = new BufferedReader(inputstreamreader);            OutputStream outputstream = sslsocket.getOutputStream();            OutputStreamWriter outputstreamwriter = new OutputStreamWriter(outputstream);            BufferedWriter bufferedwriter = new BufferedWriter(outputstreamwriter);            String string = null;            while ((string = bufferedreader.readLine()) != null) {                bufferedwriter.write(string + '\n');                bufferedwriter.flush();            }        } catch (Exception exception) {            exception.printStackTrace();        }    }}      

Compile it by using simple command:

javac EchoClient.java

Running server and client using SSL

First copy certificate file that you created before into working directory and run server with these parameters (notice that you have to change keyStore name and/or trustStrorePassword if you specified different options creating certificate:

java -Djavax.net.ssl.keyStore=mySrvKeystore -Djavax.net.ssl.keyStorePassword=123456 EchoServer

And now again copy certificate file that you created before into working directory and run client with these parameters (notice that you have to change keyStore name and/or trustStrorePassword if you specified different options creating certificate:

java -Djavax.net.ssl.trustStore=mySrvKeystore -Djavax.net.ssl.trustStorePassword=123456 

EchoClient

If you want SSL debug information just add these parameters when running server and/or client:

-Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol -Djavax.net.debug=ssl

Playing with server and client

Now just type any string on client console and press return. The same string has to appear on server console.

Copyright

This document is copyrighted to Tomas Vilda. You can use it in all ways, but don't change this section and allways include it.

by holyjohn | 2010/07/30 18:12 | Java - 자바 | 트랙백 | 덧글(0)

BGDB, GDB, GVA 인증 및 실행 파일


BGDB, GDB, GVA 인증 및 실행 파일












by holyjohn | 2010/07/30 13:10 | 동영상 | 트랙백 | 덧글(1)

자카르타 파일 업로드 개발


자카르타 파일 업로드 개발을 위한 HTTPClient 사용 샘플

Jakarta_Commons_HttpClient_and_FileUpload.doc




by holyjohn | 2010/07/19 13:35 | Java - 자바 | 트랙백 | 덧글(0)

OpenSource Java Cache Solution 비교


java 오픈소스 캐시 솔루션 비교 자료


by holyjohn | 2010/07/19 11:58 | Java - 자바 | 트랙백 | 덧글(0)

◀ 이전 페이지다음 페이지 ▶