객체 직렬화(Object Serialization)

출처: http://www.javastudy.co.kr/docs/lec_java/io/lecture.htm

송 재승(angel1011@hanmail.net) 

 

자바에서는 자바에서 제공하는 기본적인 데이터 유형 이외에도 여러 객체들을 스트림에 쓰고, 읽을 수 있는

기능을 제공하는데 이것이 바로 객체 직렬화를 통해서 가능하다. 이러한 객체 직렬화 기법은 원격 메소드

호출, 보안 및 암호화 등 실제 프로그래밍 시에 유용하게 사용되어 질 수 있다.

 객체 직렬화의 비밀

그러면 먼저 객체 직렬화가 어떠한 과정을 거쳐서 이루어 지는지 비밀을 벗겨보도록 하자.
먼저 객체들은 ObjectOutputStream에 의해서 직렬화가 되며, ObjectInputStream에 의해서 직렬화가

해제된다는 사실을 기억하도록 하자. 이러한 일련의 과정을 그림으로 나타내면 다음과 같다.

 

[그림 객체 직렬화]

 

여기에 사용되는 ObjectOutputStream과 ObjectInputStream은 모두 java.io 패키지의 일부분이고, 각각 DataOutput과 DataInput클래스를 implement한 ObjectOutput과 ObjectInput을 extends 해서 만들어 진것이다. 그리고 이 두 클래스에는 비원시 객체와 배열등을 스트림에 읽고 쓰기 위한 메쏘드들이 추가되어져 있다.

 

[그림 클래스 구조도]

 

객체 직렬화의 과정

지금부터는 보다 자세히 객체의 직렬화 과정에 대해 알아보도록 하자.

객체는 ObjectOutputStream의 writeObject() 메쏘드에 자신을 넘김으로써 직렬화 된다.
WirteObject()메쏘드는 private 필드와 super 클래스로부터 상속받은 필드를 포함, 
객체의 모든 것을 기록하게된다. 직렬화 해제는 직렬화와 반대의 과정을 거치게 되는데 ObjectInputStream의 readObject()메쏘드를

호출함으로써 스트림으로부터 읽어 들이고 이를 직렬화가 되기전의 객체로 다시 만들게 된다.

다음은 객체가 직렬화 되고 해제되어 원래의 객체로 복원되는 과정에 대한 간단한 예제이다.

눈여겨 볼 부분은 ObjectInputStream을 생성해서 writeObject()를 사용해서 객체를 직렬화 하는것과 ObjectInputStream을 생성해서 readObject()를 통해서 객체를 복원하는 부분이다. 또한 SerializableClass가 Serializable을 implements한 것을 주의해서 보길 바란다.

 

import java.io.*;

public class ObjectSerializeTest {

    public static void main(String[] args) throws Exception {
/* 파일을 열어서 그곳에 객체를 직렬화 시켜서 저장한다. */
// 파일 출력 스트림을 연다. 

    FileOutputStream fos = new FileOutputStream("_imsi.txt");

// 객체 스트림을 열고, 객체스트림을 통해 객체를 파일에 저장
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(new SerializableClass("Serialize Test Program", 1004));

// 스트림을 닫는다. 
    oos.close();

/* 직렬화 된 객체가 저장된 파일로 부터 객체를 해제시켜 원래의 객체를 복원*/
// 파일 입력 스트림으로부터 객체 입력 스트림을 연다.

    FileInputStream fis = new FileInputStream("_imsi.txt");
    ObjectInputStream ois = new ObjectInputStream(fis);

// 객체 입력 스트림으로부터 객체를 읽어온다.
    SerializableClass sc = (SerializableClass)ois.readObject();

// 스트림을 닫는다. 
    ois.close();

/* 스트림으로부터 읽어들인 객체의 내용을 출력 원래 생성되었던 객체와 같은 값을 갖는다는 것을 알수가 있다. */
    System.out.println("String : " + sc.mString);
    System.out.println("Integer : " + sc.mInt);
   }
}

/* 하나의 문자열과 정수를 저장하고있는 클래스
Serializable을 implements 함으로써 스트림을 통해 직렬화되고 해제되어질수 있다. */

class SerializableClass implements Serializable {

    public String mString;
    public int mInt;

// 생성자 
    public SerializableClass(String s, int i) {
        this.mString = s;
        this.mInt = I;
    }
}

[실행방법]

javac.exe ObjectSerializeTest.java
java.exe ObjectSerializeTest

 

[실행결과]

String : Serialize Test Program
Integer : 1004

 

 

커스텀 직렬화

  • Serializable

스트림을 통해서 직렬화 또는 해제 될수 있는 객체는 Serialiazable을 implements한 객체만이 가능하다.자바 2에서는 java.awt, javax.swing, etc등의 클래스들이 Serializable을 implements하고 있으며, 이러한 상위 클래스들이 implements하므로 하위 클래스들 또한 같이 직렬화/해제의 범주에 들 수가 있다.

다음은 Serializable을 implements하고있어서 객체의 직렬화가 가능한 객체들의 리스트이다.

[직렬화 가능 객체 리스트]

 

패키지

Serializable

java.awt

BorderLayout, CardLayout, CheckboxGroup, Color, Component, ComponentOrientation, Cursor, Dimension, Event, FlowLayout, FocusManager, Font, FontMetrics, GraphicsConfigTemplate, GridBagConstraints, GridBagLayout, GridBagLayoutInfo, GridLayout, ImageMediaEntry, Insets, LightweightDispatcher, MediaTracker, MenuComponent, MenuShortcut, Point, Polygon, Rectangle, ScrollPaneAdjustable, SystemColor

java.awt.dnd

DropTarget

java.awt.font

TransformAttribute

java.awt.geom

AffineTransform

java.awt.image.renderable

ParameterBlock

java.beans

PropertyChangesSupport, VetoableChangeSupport, BeanContext, BeanContextChildSupport, BeanContextSupport

java.io

ExternalizableFile, FilePermission, FilePermissionCollection, ObjectStreamClass

java.net

InetAddress, SocketPermission, SocketPermissionCollection, URL

java.rmi

MarshalledObject

java.rmi.activation

ActivationDesc, ActivationGroupDesc, ActivationGroupID, ActivationID

java.rmi.dgc

Lease, VMID

java.rmi.server

ObjID, RemoteObject, UID

java.security

AllPermissionCollection, BasicPermission, BasicPermissionCollection, CodeSource, GuardedObject, Identity, Key, KeyPair, Permission, PermissionCollection, Permissions, PermissionsHash, SecureRandomSpi, SignedObject, UnresolvedPermission, UnresolvedPermissionCollection

java.text

BreakIterator, Collector, DateFormatSymbols, DecimalFormatSymbols, Format, SpecialMapping, TextBoundaryData, UnicodeClassMapping, WordBreakTable

java.util

ArrayList, BitSet, Calendar, Date, EventObject, HashMap, HashSet, Hashtable, LinkedList, Locale, PropertyPermissionCollection, Random, TimeZone, TreeMap, TreeSet, Vector

javas.swing.table

AbstractTableModel, DefaultTableCellRenderer, DefaultTableColumnModel, DefaultTableModel, TableColumn

javax.swing.text

AbstractDocument, EditorKit, GapContext, SimpleAttributeSet, StringContent, StyleContext, TabSet, TabStop

javax.swing.tree

DefaultMutableTreeNode, DefaultTreeModel, DefaultTreeSelectionModel, TreePath

 

Serializable은 Cloneable과 같은 마커 인터페이스로서, 어떠한 메쏘드들을 정의해놓은 것이 아닌, serizlVersionUDI라는 변수 하나만을 가지며, 이 객체가 직렬화가 가능하다는 것을 알려주는 역할만을 하는 인터페이스일 뿐이다.

  • transient

스트림을 이용해서 직렬화 하는데 있어서, 커다란 프로그램 전체가 직렬화된다면, 여러가지 면에서 많은 낭비일 것이다. 예를 들어, 한 객체가 마우스가 눌려진 위치를 알기 위해서 마우스 클릭시에 위치를 저장하는 필드를 가지고 있다고 가정하자, 이 경우 마우스의 위치값은 프로그램이 돌아가는 상태에서 마우스가 눌려졌을 당시에만 유효한 값으로, 객체가 직렬화 되었다가 해제 되었을 경우에는 쓸모없는 값이 되어버린다.

이런 객체 직렬화에 쓸모없는 값들은 transient로 선언해 줌으로써 객체 직렬화에서 제외되어질수 있다.

Private transient int x;

이러한 선언은 플랫폼에 따라 다른 값을 가지는 필드나, 현재의 환경에서만 유효한 필드등을 객체 직렬화에서 제외하는데 유용하게 쓰일 수가 있다.

transient선언이 적용된 예이다.

 

import java.io.*;

public class ObjectSerializeTest1 extends TestClass implements Serializable {

// 변수 선언, x는 객체직렬화에서 제외되도록 transient로 선언 
    private int i;
    private transient int x;

// 생성자 Serializable을 implements한 i와 x는 받아온 인자를 그대로 대입하고, TestClass를 extends한 j는
// i값에 5배를 한 값을 대입했다. 

    public ObjectSerializeTest1(int i, int x) {
        this.i = i;
        this.x = x;
        j = i*5;
    }

/* 객체직렬화를 하기전의 객체의 값들을 알아본뒤에, 해당 객체를 스트림에 직렬화 시킨다.*/
    private void writeObject(ObjectOutputStream out) throws IOException {
        System.out.println("writeObject");
        System.out.println("write ==> i = "+this.i+", j = "+j+", x = "+this.x);
        out.defaultWriteObject(); 
// 객체를 직렬화 한다.
        out.writeInt(j); 
// 임시파일에 기록
    }

/* 스트림으로 부터 객체를 해제시킨다. */
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        System.out.println("readObject");
        in.defaultReadObject(); 
// 객체 직렬화 해제 작업
        j=in.readInt(); 
// 임시파일에서 읽어옴
    }

/* 객체의 값을 알기 위해서 오버라이드 */ 
    public String toString() { 
        return "i = "+i+", j = "+j+", x = "+x;
    }

    public static void main(String[] args) throws Exception {
// 객체직렬화에 사용될 화일을 열고 스트림을 얻는다. 
        FileOutputStream fos = new FileOutputStream("temp.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);

// 객체를 생성시킨뒤, 스트림에 객체를 쓴다.(10은 transient로 선언된 필드의 값임을 유의)
        oos.writeObject(new ObjectSerializeTest1(1, 10));
        oos.close();

// 정보를 저장한 화일을 열고 스트림을 통해 읽은뒤 객체직렬화를 해제시킨다.
        FileInputStream fis = new FileInputStream("temp.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ObjectSerializeTest1 ser = (ObjectSerializeTest1)ois.readObject();
        ois.close();

// print문 수행시 형 변환이 이루어져서 이미 오버라이드된 toString()메쏘드가 수행된다.
        System.out.println("read ==> "+ser);
    }
}

/* Serialize되지 않은 클래스 객체 직렬화 작업으로부터는 제외, 만약 사용자가 이 클래스를 객체 직렬화에 사용하고 싶다면 Serializable을 implements하면 된다. 만약 그렇게 할수 없을 경우에는 위와 같이 하도록 한다. */
class TestClass {
    int j;
}

[실행방법]

javac.exe ObjectSerializeTest1.java
java.exe ObjectSerializeTest1

 

[실행결과]

writeObject
write ==> i = 1, j = 5, x = 10
readObject
read ==> i = 1, j = 5, x = 0

 

 

  • externalizable

객체직렬화의 또 다른 방법으로는 Externalizable 인터페이스를 들수 있는데, 이 방법은 Serializable보다 더 강력하게 객체 직렬화를 제어하고 싶을 경우에 사용되어진다. 어떤 클래스가 이 인터페이스를 구현하게 되면 readExternal()과 writeExternal()메쏘드를 사용하여 객체를 스트림으로부터 읽고 쓸수있다.

Externalizable객체들은 자신의 상태를 직렬화 및 해제하는 과정에서 강력한 제어를 할수 있으며, Serializable 객체에서의 기본 데이터 포멧을 사용하고싶지 않을때에 사용한다.

다음은 externalizable을 사용한 객체 직렬화의 예이다.

 

import java.io.*;

public class ObjectExternalizeTest implements Externalizable {

    int i;
    String s;

/* 인자없는 생성자(직렬화된 데이터를 읽어들여서 객체를 만들기 위해 필요) */
    public ObjectExternalizeTest() { }

/* 생성자 */
    public ObjectExternalizeTest(String s, int i) {
        this.s = s;
        this.i = i;
    }

/* readObject()메쏘드로부터 호출이 되며, 직렬화된 객체를 해제시킨다. */
    public void 
readExternal(ObjectInput oi) throws IOException, ClassNotFoundException {
        System.out.println("Execute readExternal()");
        s = (String)oi.readObject();
        i = oi.readInt();
    }

/* writeObject()메쏘드로부터 호출이 되며, 변수들을 직렬화한다.*/
    public void 
writeExternal(ObjectOutput oo) throws IOException {
        System.out.println("Execute writeExternal()");
        oo.writeObject(s);
        oo.writeInt(i);
    }

/* 변수들의 값을 보여준다.*/
    public String toString() {
        return Integer.toString(i)+", "+s;
    }

    public static void main(String[] args) throws Exception {
        ObjectExternalizeTest oet1 = new ObjectExternalizeTest("Externalizable Test", 1000);

// 객체를 직렬화시켜 저장할 화일로부터 쓰기 스트림을 얻어온다.
        FileOutputStream out = new FileOutputStream("temp.txt");
        ObjectOutputStream oos = new ObjectOutputStream(out);

/* 객체를 직렬화 시킨다. 이경우 writeObject는 oet1의 writeExternal을호출하게되고writeExternal() 메쏘드의 코드들이 확장되어 실행이 된다. */
       oos.writeObject(oet1);

// 객체가 직렬화되어 저장된 파일로부터 스트림을 얻어온다.
        FileInputStream in = new FileInputStream("temp.txt");
        ObjectInputStream ois = new ObjectInputStream(in);
        ObjectExternalizeTest oet2 = (ObjectExternalizeTest)ois.readObject();

// 객체 직렬화 이전의 객체와 직렬화를 거친 뒤 다시 해제된 객체의 값을 출력한다.
        System.out.println(oet1);
        System.out.println(oet2);
// 만약 모든 과정이 제대로 이루어졌다면 oet1과 oet2객체는 같은 값을 가지고있을 것이다. 
    }
}

 

 

[실행방법]

javac.exe ObjectExternalizeTest.java
java.exe ObjectExternalizeTest

[실행결과]

Execute writeExternal()
Execute readExternal()

1000, Externalizable Test
1000, Externalizable Test

 

 

애플릿 직렬화

객체직렬화는 애플릿을 직렬화하는데에도 사용이 되어진다. 이러한 경우 <Applet>태그는 클래스 파일 대신에 직렬화 된 객체 파일을 지정하게 되고, 브라우저는 이러한 태그를 만나면 직렬화된 애플릿을 해제하여 화면에 보여주게 된다. 즉, 그래픽 유저 인터페이스를 생성하기 위한 코드를 전혀 가지고 있지 않으면서도 완벽한 그래픽 유저 인터페이스를 가지게 되는것이다.

일반적으로 직렬화된 애플릿은 *.ser의 확장자를 가진다.

직렬화된 애플릿을 호출하는 html 파일은 다음과 같은 내용을 가진다.

<Applet Object="SerializableApplet.ser" Width=300 Height=300></Applet>
 

소켓을 통한 객체 직렬화

소켓을 생성한 뒤 스트림을 얻어서 ObjectInputStream과 ObjectOutputStream을 얻어서 사용한다면,

파일이 아닌 네트웍 너머의 다른 컴퓨터와도 객체를 직접 주고 받을수 있다.

소켓을 통해 직렬화 되어 쓰고 읽혀질 객체는 역시 Serializable을 구현하고 있어야 하며,

사용법은 파일에 쓰고 읽는것과 동일하다. 다음은 서버에서 이미지를 포함하고 있는 Serializable을 구현한

객체를 생성하여 소켓을 이용하여 클라이언트 애플릿에게 직렬화된 객체를 보내고, 애플릿에서는 이를

해제하여 화면에 나타내는 예제이다. 여기서 주의깊게 살펴보아야 할 점은 이미지를 직렬화 하기 위한

방법이다. 이미지는 Serializable을 구현하고 있지 않기 때문에 직접적으로 객체 직렬화에 사용되어질 수는

없다. 하지만, 자바 2에서는 배열을 직렬화에 사용할 수가 있고, 이미지는 각각의 픽셀들이 배열에

저장되어질수 있으므로 직접적으로 이미지를 직렬화 할 수는 없지만 배열을 사용하여 간접적으로나마

이미지를 객체 직렬화에 사용할 수가 있다.

  • 서버측 프로그램

 

import java.awt.*;
import java.awt.image.*;
import java.net.*;
import java.io.*;
import java.util.*;

public class ImageSerializeServer {

// 서버는 3434 포트로 임의로 설정 
    public static final int DEFAULT_PORT = 3434;
    protected int port;
    protected ServerSocket server;

// ImageSerializeServer를 실행한다.
    public static void main (String args[]) {
        new ImageSerializeServer ();
    }

/* 생성자. 서버의 소켓을 생성하고 클라이언트의 연결을 대기 */
    public ImageSerializeServer() {

// 포트를 지정(3434) 
        port = DEFAULT_PORT;
        this.port = port;

// 지정된 포트로 서버의 소켓을 생성한다.
        try {
            server = new ServerSocket (port);
        } catch (IOException e) {
            System.err.println ("Error creating server");
            System.exit (1);
        }

// 클라이언트의 연결을 대기 
        connectClient();
    }

/* 클라이언트로 부터의 연결을 기다리면서 무한루프를 돈다. */
    public void connectClient() {
        System.out.println ("Server Running");
        try {

// 클라이언트로 부터의 연결이 요청되면 클라이언트의 소켓을 생성 하고 이미지 픽셀을 클라이언트로 보내는 쓰레드를 생성한다.
            while (true) {
                Socket client = server.accept();
                System.out.println ("Connection from: " + client.getInetAddress().getHostName());
                appletConnection c = new appletConnection (client);
 // 쓰레드 생성
            }
        } catch (IOException e) {
            System.err.println ("Exception listening");
            System.exit (1);
        }
        System.exit (0);
    }
}

/* 간단한 이미지를 이루는 픽셀을 만들어 이를 네트웍너머의 클라이언트로 보내는 클래스 */
class appletConnection extends Thread {

// 변수 선언 
    private final static int b = Color.blue.getRGB();
    private final static int g = Color.green.getRGB();
    private final static int r = Color.red.getRGB();
    protected ObjectOutputStream oos;

// 간단한 이미지 픽셀 
    protected int ipTemp[] = { b, b, b, b, b, b, b, b, b, b,
                                         b, b, b, b, b, b, b, b, b, b,
                                         b, b, g, g, g, g, g, g, b, b,
                                         b, b, g, g, g, g, g, g, b, b,
                                         b, b, g, g, r, r, g, g, b, b,
                                         b, b, g, g, r, r, g, g, b, b,
                                         b, b, g, g, g, g, g, g, b, b,
                                         b, b, g, g, g, g, g, g, b, b,
                                         b, b, b, b, b, b, b, b, b, b,
                                         b, b, b, b, b, b, b, b, b, b };

    public appletConnection (Socket client) {

// 객체 직렬화를 위한 출력 스트림을 생성 
        try {
            oos = new ObjectOutputStream(client.getOutputStream());
        } catch (IOException e) {
            try {
                client.close();
            } catch (IOException e2) { }
            System.err.println ("Unable to connect.");
            return;
        }
        start();
    }

    public void run () {

/* Serializable을 구현한 imagePixel객체를 생성해서 Vector에 집어넣는다. 따라서 이미지가 하나가 아닌 여러개 일지라도 객체 직렬화를 통해 전달하는것이 가능하다.*/
        Vector vt = new Vector();
        ImagePixel ip = new imagePixel(ipTemp);
        vt.addElement(ip);

// 이미지 픽셀들을 포함하고 있는 벡터를 객체 직렬화 시켜 스트림에 쓴다. 
        try {
         
   oos.writeObject(vt);
        } catch (Exception e) {
            System.out.println("Object write exception : " + e);
        }
    }
}

/* 객체의 직렬화를 위해 Serializable을 구현한 imagePixel 클래스 */
class imagePixel implements Serializable {
    protected int ip[];

// 생성자
    public imagePixel(int[] ip) {
        this.ip = ip;
    }
}

  •  

[실행방법]

javac.exe ImageSerializeServer.java
java.exe ImageSerializeServer

Server Running.....(서버가 대기하고 있는 상태)

 

 

  • 클라이언트측 프로그램

 

import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import java.util.*;

public class ImageSerializeClient extends Applet {

// 기본 포트는 서버와 같이 3434로 하고 host는 localhost로 설정한다. 
    private int defaultPort = 3434;
    private String host = "localhost";
    private Socket s;
    private ObjectInputStream oin;

// 변서 선언
    Vector vt;
    Image myImage;

    public void init() {

/* 서버에 접속하여 소켓을 구한뒤에 객체 직렬화를 해제하기 위한 스트림을 얻는다. 이것은 마치 파일로부터 스트림을 얻는 과정과 비슷하다. */
        try{
            s = new Socket(host, defaultPort);
            oin = new ObjectInputStream(s.getInputStream());
        } catch(IOException e) { }
    }

    public void start() {

// 객체를 스트림으로 부터 읽어들여 직렬화를 해제한다. 
        try {
         
   vt = (Vector)oin.readObject();
        } catch(Exception e) {}

// 직렬화가 해제된 객체는 Vector에 들어가게 되고 Vector의 요소인 imagePixel을 구한다. 
        imagePixel myPixel = (imagePixel)vt.elementAt(0); 

// 이렇게 구해진 imagePixel의 이미지 픽셀을 가지고 실제 이미지를 생성 
        myImage = createImage(new MemoryImageSource(10, 10, myPixel.ip, 0, 10));
    }

// 생성된 이미지로 그림을 그린다. 
    public void paint(Graphics g) {
        g.drawImage(myImage, 0, 0, 100, 100, this);
    }
}

  •   

[실행방법]

javac.exe ImageSerializeClient.java
java.exe ImageSerializeClient

 

 

  • 애플릿 포함 Html 내용

 

<HTML>
<TITLE>객체 직렬화</TITLE>
<BODY>

<APPLET CODE = "ImageSerializeClient.class" WIDTH = 100 HEIGHT = 100 ></APPLET>

</BODY>
</HTML>

 

 

  • 최종 결과 화면

 프로그램을 실행하기 위해서는 다음의 과정으로 실행하면 된다.

  1. 서버측 프로그램인 ImageSerializeServer.java를 컴파일하고, 서버 쪽에서 실행시면, "Server Running..."이라는 메시지가 나타나면서 서버측 프로그램이 제대로 작동을 하는 것이다.
  2. 다음으로는 클라이언트 애플릿 프로그램인 ImagSerializeClient.java를 컴파일한다.
  3. 브라우져를 실행시킨뒤에 클라이언트 애플릿 클래스를 실행시키기 위한 html파일이 있는 URL주소를 주소 입력란에 넣는다.
  4. 이때 서버프로그램이 돌아가는 곳과 애플릿을 위한 코드 및 html파일이 위치한 곳은 같은 Machine상에 있어야 한다.

참고로 이번 예제에서는 이미지 픽셀을 직접 그 값을 대입해서 이미지를 대신했는데, 실제 이미지를 이미지 픽셀로 바꾸는 기능이 자바에서 지원되고 있으므로 이를 간단히 소개토록 하겠다. 이미지는 여러개의 색깔을 표현하고 있는 픽셀들의 집합으로 볼 수 있고, 보다 정교하게 이미지를 다루는 작업을 하고 싶다면, 이미지의 픽셀들을 얻어서 조작을 해야한다. 그렇다면 어떻게 이미지의 픽셀들을 얻을 수 있을까? 그것은 PixelGrabber를 통해서 가능하다. PixelGrabber는 이미지와 해당 이미지의 정보들을 기반으로 이미지 픽셀을 구해준다. 
다음은 실제로 이러한 것을 보여주는 코드이다.

 

.
.

int imagePixel[];                         // 이미지의 픽셀들을 저장할 배열
int imageWidth, imageHeight;      
// 이미지의 폭과 높이를 가지게 될 변수들
Image imageObject;                    
// 이미지 객체
.
.

/* imageObject로부터 픽셀들을 얻어서 imagePixel배열에 저장한다. */
private void getImagePixel() {

    imagePixel = new int[imageWidth * imageHeight];    // imagePixel을 초기화 한다. 
    PixelGrabber grabber = new PixelGrabber(imageObject, 0, 0, imageWidth, imageHeight, imagePixel, 0,                                                           imageWidth);

// grabber를 생성해서 픽셀들을 얻는다. 
    try {
        grabber.grabPixels();
    } catch(Exception e) {
        return; }

    if((grabber.status() & ImageObserber.ABORT) != 0)
        return;
}

.
.

 

 참고 문헌

  • Java Examples In a Nutshell (O'RELLY)
  • Java I/O (O'RELLY)





Posted by linuxism
,


Exception message

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'nServiceImpl': Bean with name 'nServiceImpl' has been injected into other beans [dServiceImpl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.







Resolve circular dependency in Spring Autowiring

I would consider this post as best practice for using Spring in enterprise application development.

When writing enterprise web application using Spring, the amount of services in the service layer, will probably grow.
Each service in the service layer will probably consume other services, which will be injected via @Autowire .
The problem: When services number start growing, a circular dependency might occur. It does not have to indicate on design problem... It's enough that a central service, which is autowired in many services, consuming one of the other services, the circular dependency will likely to occur.

The circular dependency will cause the Spring Application Context to fail and the symptom is an error which indicate clearly about the problem:

Bean with name ‘*********’ has been injected into other beans [******, **********, **********, **********] in its raw version as part of a circular reference,

but has eventually been wrapped (for example as part of auto-proxy creation). This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching – consider using ‘getBeanNamesOfType’ with the ‘allowEagerInit’ flag turned off, for example.


 The problem in modern spring application is that beans are defined via @nnotations (and not via XML) and the option of allowEagerInit flag, simply does not exist.
The alternative solution of annotating the classes with @Lazy, simply did not work for me.

The working solution was to add default-lazy-init="true" to the application config xml file:

<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true" xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd >
    <context:component-scan base-package="com.package">
        
    </context:component-scan>
    <context:annotation-config/>
   ...
</beans>

Hope this helps.  Not sure why it is not a default configuration.
If you have suggestion why this configuration might be not ok, kindly share it with us all.

Update:
Following redesign I had, this mentioned solution simply did not do the trick.
So I designed more aggressive solution to resolve that problem in 5 steps.

Good luck!



출처 - http://gal-levinsky.blogspot.kr/2012/03/resolve-circular-dependency-in-spring.html







Saturday, April 14, 2012

Judgement day weapon for circular autowiring dependency error

On recent post I demonstrated a way to resolve circular dependency error.
However, sometimes, even the mentioned solution  is not enough and redesign is not possible.

I would like to suggest a solution of Dependency Injection which will be as much loyal as possible to the Spring IoC way and will help to overcome the circular dependency error.

This means that we will have a single class that will have a reference to all the classes which need to be injected and will be responsible for the injection.

I'll try to walk you through the steps.

Assuming we have the following service classes which cause the circular error:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Service
public class ServiceA{
 
@Autowire
ServiceB serviceB;
@Autowire
ServiceC serviceC;
 
}
 
@Service
public class ServiceB{
 
@Autowire
ServiceA serviceA;
@Autowire
ServiceC serviceC;
}

First step is to remove the @Autowire annotations, so we will move the wiring responsibility out of Spring hands.

Second step is to create a class which will hold a reference to all the classes to inject. 
Such as:
1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class BeansManager{
 
@Autowire
private ServiceA serviceA;
@Autowire
private ServiceB serviceB;
@Autowire
private ServiceC serviceC;
 
get...
set...
}


Third step is to create an interface name Injectable with method inject.
1
2
3
public interface Injectable{
public void inject(BeansManager beansManager);
}


Forth step is to set each service class/interface to implement the injectable interface.

e.g. :

1
2
3
4
5
6
7
8
9
10
@Service
public class ServiceA implements Injectable{
 
ServiceB serviceB;
ServiceC serviceC;
//method to inject all the beans which were previously were injected by Spring
public void inject(BeansManager beansManager){
this.serviceB =  beansManager.getServiceB();
this.serviceC = beansManager.getServiceC();
}

}


Fifth and final step is to set the BeansManager to be ready for the injection.
But take a moment to think -
It's obvious that we need  a reference of all the classes which need to be injected in the BeansManager, however, how can we make sure that the following sequence is maintained:
1. All Service classes are initiated
2. BeansManager is initiated with all the services injected by Spring
3. After BeansManager initiated and exist in its steady state, call all the service classes which need injection and inject the relevant service.

Step 3 can be achieved by a method which executed after constructor finished (via @PostConstruct annotation), however the big question here is how to make sure the BeansManager is initiated last? (after all the relevant services are initiated)
The trick is to have @Autowire on the Injectable set. This way Spring will make sure that:
a. All the injectable classes are subscribed to the BeansManager for the injection
b. The BeansManager will be initiated last (after all injectable services are initiated)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BeansManager
 
//This line will guarantee the BeansManager class will be injected last
@Autowired
private Set<Injectable> injectables = new HashSet();
 
//This method will make sure all the injectable classes will get the BeansManager in its steady state,
//where it's class members are ready to be set
@PostConstruct
private void inject() {
   for (Injectable injectableItem : injectables) {
       injectableItem.inject(this);
   }
}
 
}

Make sure you understand all the magic that happened in the fifth step, it's really cool.

Note:
If you think on a way to implement even more generic mechanism that will achieve the same, please drop me a note.

Good luck!

acknowledgement:
http://stackoverflow.com/questions/7066683/is-it-possible-to-guarantee-the-order-in-which-postconstruct-methods-are-invoke



출처 - http://gal-levinsky.blogspot.kr/2012/03/resolve-circular-dependency-in-spring.html













Posted by linuxism
,


* openfire plugin configuration

public interface RMIService {

public int geStatus(String jid);

}


public class RMIServiceImpl extends HessianServlet implements RMIService {

public int geStatus(String jid) {

// process

}

}


public void initializePlugin(PluginManager pluginManager, File pluginDirectory) {

try {

this.server = new Server(8080);

ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.SESSIONS);

   handler.setContextPath("/"); 

   handler.addServlet(RMIServiceImpl.class, "/rmi");

   this.server.setHandler(handler);

   this.server.start();

} catch(Exception e) {

logger.warn("Exception occurred!", e);

}

}




* client configuration


public interface RMIService {

public int geStatus(String jid);

}


public class HessianTest {

public static void main(String[] args) {

String url = "http://localhost:8080/rmi";

HessianProxyFactory factory = new HessianProxyFactory();

RMIService rs = null;

try {

rs = (RMIService) factory.create(RMIService.class, url);

} catch (MalformedURLException e) {

e.printStackTrace();

}

System.out.println("status : " + rs.getStatus("test@test.com/test"));

}

}

Posted by linuxism
,