달력

4

« 2024/4 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
2009. 7. 24. 16:06

AspectJ ITD를 이용한 ActiveRecord 구현 Java2009. 7. 24. 16:06

aspectj의 ITD(Inter-Type Declaration)은 roo로 인해 보다 유명해 진 기술로써 클래스에 존재하지 않는 메소드를 추가하거나 존재하지 않았던 관계(extends, implements 등)을 추가하여 클래스의 타입을 변경할 수 있는 기술이다.

이 글에서는 find 메소드를 POJO에 추가하는 것을 aspectj ITD를 이용해서 구현해 본다.  또 ITD로 find 메소드 정의시 generic을 사용해서 타입 캐스팅이 불필요하도록 한다.

1. Test

public class ArticleTest {

@Test

public void find() {

Article key = new Article();

Article a = (Article) new Article().find(key);

assertThat(a, is(key));


ArticleContent key2 = new ArticleContent();

ArticleContent a2 = (ArticleContent) new ArticleContent().find(key2);

assertThat(a2, is(key2));

}

}


위 테스트는 find 메소드를 호출하여 그 값이 key와 동일한지 비교하는 테스트이다.

Article, ArticleContent 클래스에는 find 메소드가 존재하지 않는다.


2. Aspect

privileged aspect ActiveRecordITD {

public interface ActiveRecord {};

declare parents : Article implements ActiveRecord;

declare parents : ArticleContent implements ActiveRecord;

@SuppressWarnings("unused")

private void ActiveRecord.log(String methodName) {

System.out.println(this.getClass().getSimpleName()+"#" + methodName +" called !!!");

}

public <T> T ActiveRecord.find(T key) {

log("find");

return key;

}

}

 

위 aspect에서 특이한 부분을 살펴보도록 하자.

privileged: weaving할 클래스의 private까지도 access 가능하도록 한다.

declare parents: 해당 클래스가 특정 인터페이스를 구현하도록 한다.

public <T> T ActiveRecord.find(T key): ActiveRecord를 구현하는 클래스들에 해당 메소드를 제공한다.


지금까지 간략하게 generic 메소드를 제공하는 ITD를 구현하는 방법을 간략하게 알아보았다. 실제의 경우라면 find 메소드가 보다 복잡해야겠지만서도...


:
Posted by codetemplate
2008. 4. 25. 11:43

jvm option Java2008. 4. 25. 11:43

jmx 연결을 위한 옵션

java 
-Dcom.sun.management.jmxremote.port=portNo
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false MyApp
:
Posted by codetemplate
2008. 3. 10. 17:32

쓰레드 동기화 Java2008. 3. 10. 17:32

CyclicBarrier를 이용하는 방법.
public class SynchronizedThread extends Thread {
private CyclicBarrier entryBarrier; private CyclicBarrier exitBarrier;
public SynchronizedThread(Runnable runnable, CyclicBarrier entryBarrier, CyclicBarrier exitBarrier) {
super(runnable); this.entryBarrier = entryBarrier; this.exitBarrier = exitBarrier;
}
@Override public void run() {
try {
entryBarrier.await(); super.run(); exitBarrier.await();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public class SynchronizedThreadTest {
@Test public void testMultithread() throws InterruptedException, BrokenBarrierException {
Runnable r = new Runnable() {
public void run() {
System.out.println("thread=" + Thread.currentThread().getId() + ", started !!!"); long millis = 3 * 1000l; try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("thread=" + Thread.currentThread().getId() + ", done !!!");
}
};
int numberOfThreads = 10;
System.out.println("before start");
CyclicBarrier entryBarrier = new CyclicBarrier(numberOfThreads + 1); CyclicBarrier exitBarrier = new CyclicBarrier(numberOfThreads + 1);
for (int i = 0; i < numberOfThreads; i++) {
// new Thread(r).start(); new SynchronizedThread(r, entryBarrier, exitBarrier).start();
}
System.out.println("after start");
System.out.println("before entryBarrier.await();"); entryBarrier.await();
System.out.println("before exitBarrier.await();"); exitBarrier.await();
System.out.println("after exitBarrier.await();");
}
}
:
Posted by codetemplate
2006. 10. 30. 19:58

한글이 들어가 프로퍼티 파일이 깨지는 문제 Java2006. 10. 30. 19:58

navite2ascii를 이용하여 프로퍼티 파일을 변환한다.

:
Posted by codetemplate

java.lang.SecurityException: Could not lockSystem prefs.Lock file access denied.
       at java.util.prefs.FileSystemPreferences.checkLockFile0ErrorCode(FileSystemPreferences.java:926)
       at java.util.prefs.FileSystemPreferences.lockFile(FileSystemPreferences.java:915)
       at java.util.prefs.FileSystemPreferences.sync(FileSystemPreferences.java:723)
       at java.util.prefs.FileSystemPreferences.flush(FileSystemPreferences.java:814)
       at java.util.prefs.FileSystemPreferences.syncWorld(FileSystemPreferences.java:475)
       at java.util.prefs.FileSystemPreferences.access$1200(FileSystemPreferences.java:33)
       at java.util.prefs.FileSystemPreferences$10.run(FileSystemPreferences.java:449)
       at java.util.TimerThread.mainLoop(Timer.java:432)
       at java.util.TimerThread.run(Timer.java:382)

이런 에러가 종종 생기는데 이유가 먼가요?


이 에러는 JDK 1.4의 XML Configuration 기능 사용시 발생 가능합니다.
다음과 같은 방법으로 해결됩니다.

$JAVA_HOME 디렉토리와 java를 사용하는 user(vep, msgsvr, jaaad 등)들이 그룹이 같아야 합니다.

$JAVA_HOME/.systemPrefs 디렉토리에 group에게 모든 권한을 준다.
$JAVA_HOME/jre/.systemPrefs 디렉토리에 group에게 모든 권한을 준다.

그리고 아래의 디렉토리에 있는 파일들을 다 삭제한다.

$JAVA_HOME/.systemPrefs
$JAVA_HOME/jre/.systemPrefs
$HOME/.java

이렇게 하면 해결 됩니다.

:
Posted by codetemplate
2006. 10. 30. 19:49

Understanding J2EE Class Loading Java2006. 10. 30. 19:49

Java Class Loading Concepts

The following two basic principles will always apply:

o Each class retains an association with the class loader that loaded it. The getClassLoader() method of java.lang.Class returns the class loader that loaded the class, which cannot be changed after the class is loaded. This is the class loader that will be used if the class attempts to load classes by name.

o If the same class is loaded by two class loaders, classes loaded by the two class loaders will not be type compatible (although serialization will work).

The documentation of the java.lang.ClassLoader class further defines the following behavior for class loaders:

o Class loaders are hierarchical.
When a class loader is asked to load a class, it first asks its parent class loader to try to load the class. Only if the parent (and parent's ancestors) cannot load the class, will the original classloader attempt to load the class. The top of the class loader hierarchy is the bootstrap loader built into the JVM, which loads java.lang.Object().

o Although a class loader can see classes loaded by its parent(s), it cannot see classes loaded by its children.

Class Loading in J2EE

J2EE servers use multiple class loaders, largely because this allows dynamic application reloading. Clearly we don't want to reload all the application server's own classes on redeploying an application. This would mean that the application server would always need to be restarted. So application servers use different class loaders for application code to those they use for their own standard libraries, for example. Typically one or more new class loaders will be created for each application deployed on a server.

However, multiple class loaders are not usually used for different application-specific classes in the same application unless we use EJB (JSP pages may be given a separate class loader, but this doesn't usually affect application code).

In an EAR, the EJB class loader is often the parent of the WAR class loader. Orion and WebLogic, for example, both use this approach. This is a natural implementation approach, as WARs will typically access EJBs (and therefore need to be able to see at least EJB client classes), while EJBs do not access web components.

In this diagram the class loader hierarchy is represented by enclosing boxes. The parent-child relationship is represented by an enclosing box:

Assuming standard J2SE hierarchical class loading behavior, such a hierarchy will mean that any class can access classes in boxes that enclose its class loader. However, classes associated with the outer boxes cannot load classes in the inner boxes. Thus web application classes can see classes deployed in the application's EJBs and system classes. However, EJB classes cannot see web application classes, and classes installed at server-wide level cannot see any application-specific classes.

Class Loading in Integrated Enterprise Applications

Thus there are two basic problems relating to J2EE class loading, in the common case where EJB and web modules are included in the same enterprise application:

o Where do we hold the definitions of classes used in both EJBs and web applications?

o How do we ensure that two class loaders don't end up holding independent versions of the same class, resulting in class cast exceptions?

The Servlet 2.3 Specification's Class Loading Recommendations

The Servlet 2.3 specification (9.7.2) states that "It is recommended also that the application class loader be implemented so that classes and resources packaged within the WAR are loaded in preference to classes and resources residing in container-wide library JARs".

This clearly conflicts with the standard J2SE class loading behavior, as described in the Javadoc for the java.lang.ClassLoader class. As the WAR class loader must be a dynamic class loader, it must be the child of another class loader provided by the application server. Hence the Servlet 2.3 recommendation is the opposite of normal Java 2 class loading behavior, which clearly states that classes will be loaded from the child class loader (in this case the WAR class loader) only if they cannot be resolved by the ancestor class loaders.

The Java 1.3 Extension Mechanism Architecture in J2EE

Changes in J2SE 1.3 make it possible for JAR files to specify dependencies on other JAR files, by specifying a space-separated list of relative file paths in a Class-Path header in their /META-INF/MANIFEST.MF file.

Class-Path: Iog4j-1.2.jar 121-core.jar 121-ejbimpl.jar 121-jdbc.jar

This declares that the application-specific classes in the ticket-ejb.jar file depend on four infrastructure JARs, meaning that the EJBJAR file doesn't need to include any third party classes. These paths are relative. All these JAR files are included with the EJBJAR file in the root directly of the application EAR, as the following listing of the EAR's contents shows:

       META-INF/
       META-INF/MANIFEST.MF
       META-INF/application.xml
       121-core.jar
       i21-ejbimpl. jar
       121-jdbc.jar
       ticket-ejb.jar
       ticket.war
       Iog4j-1.2.jar

how to enable WAR manifest classpaths on Oracle 9iAS Release 2.

IBM documentation, WebSphere 4.0 supports manifest classpaths for WAR filesM

above links recommends using this when WARs and EJBs reference the same classes.

Orion and Oracle will also load manifest classpaths in EARs by default.

JBoss/Jetty does not appear to respect manifest classpaths in WARs
I haven't relied on this non-portable feature in packaging the sample application. The J2EE Reference Implementation also ignores manifest classpaths in WARs.

BEA discussing the use of manifest classpath

Thread Context Class Loader

getContextClassLoader() method on java.util.Thread.

o Class.forName (classname):
Will use the class loader of the helper class: in this case, the EJB class loader. This means that, if the EJB class loader is the parent of the WAR class loader, the helper will never be able to load classes in the WAR by name.

o Class.forName(classname, true, Thread.currentThread().getContextClassLoader()):
Will use the current container's class loader. This means that the helper will behave differently wherever it is running. If the EJB class loader is the parent of the WAR class loader, when the helper is used in the EJB container, it will only be able to load EJB classes and classes loaded by higher class loaders. If the helper is used in the WAR, it will be able to load WAR classes as well.

:
Posted by codetemplate
2006. 10. 30. 19:26

nio Selector을 이용한 Multiplexed I/O Java2006. 10. 30. 19:26

새로운 소켓 컨넥션(채널)마다 새로운 쓰레드를 생성하는 대신 채널을 multiplexing하면 하나 이상의 쓰레드가 모든 채널을 처리할 수 있다.
특정 오퍼레이션에 관심사항을 등록한다. 그러면 가용한 쓰레드가1G사용해서 키의 상태를 검사할 수 있다. 이러한 메소드들은 어떤 키를 기다리는 오퍼레이션이 처리될 것인지 알려준다.

  if (selectionKey.isAcceptable()) {
    socket = serverSocket.accept();
    channel = socket.getChannel();
    if (channel != null) {
      channel.configureBlocking(false);
      channel.register(selector, SelectionKey.OP_READ);
    }
  }

만일 exception이 발생하면 아래와 같이 key를 cancel할 수 있다.

  } catch (SomeException e) {
    selectionKey.cancel();
  }

위의 코드는 key와 관련된 컨넥션을 무효화한다.

다음은 지금까지 설명한 예로 작성한 서버와 클라이언트이다.

package test;

import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;

public class SelectorTest {
  private static int PORT = 9876;
  private static int BUFFER_SIZE = 1024;
 
  public static void main (String args[]) {
       ByteBuffer sharedBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
       Selector selector = null;
       ServerSocket serverSocket = null;
      
       try {
           ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
           serverSocketChannel.configureBlocking(false);
          
           serverSocket = serverSocketChannel.socket();
           InetSocketAddress inetSocketAddress = new InetSocketAddress(PORT);
           serverSocket.bind(inetSocketAddress);
          
           selector = Selector.open();
           serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
       }
       catch (IOException e) {
           System.err.println("Unable to setup environment\");
           System.exit(-1);
       }
      
       try {
           while (true) {
               int count = selector.select();
              
               // nothing to process
               if (count == 0) {
                   continue;
               }
              
               Set keySet = selector.selectedKeys();
               Iterator itor = keySet.iterator();

               while (itor.hasNext()) {
                   SelectionKey selectionKey = (SelectionKey) itor.next();
                   itor.remove();
                  
                   Socket socket = null;
                   SocketChannel channel = null;
                  
                   if (selectionKey.isAcceptable()) {
                       System.out.println("["+Thread.currentThread().getName()+"] Got acceptable key\");
                       try {
                           socket = serverSocket.accept();
                           System.out.println("["+Thread.currentThread().getName()+"] Connection from: " + socket);
                           channel = socket.getChannel();
                       }
                       catch (IOException e) {
                           System.err.println("["+Thread.currentThread().getName()+"] Unable to accept channel");
                           e.printStackTrace();
                           selectionKey.cancel();
                       }
                       if (channel != null) {
                           try {
                               System.out.println("["+Thread.currentThread().getName()+"] Watch for something to read");
                               channel.configureBlocking(false);
                               channel.register(selector, SelectionKey.OP_READ);
                           }
                           catch (IOException e) {
                               System.err.println("["+Thread.currentThread().getName()+"] Unable to use channel\");
                               e.printStackTrace();
                               selectionKey.cancel();
                           }
                       }
                   }
                  
                   if (selectionKey.isReadable()) {
                       System.out.println("["+Thread.currentThread().getName()+"] Reading channel");
                       SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                       sharedBuffer.clear();
                      
                       int bytes = -1;
                       try {
                           while ((bytes = socketChannel.read(sharedBuffer)) &gt; 0) {
                               System.out.println("["+Thread.currentThread().getName()+"] Reading...\");
                               sharedBuffer.flip();
                               while (sharedBuffer.hasRemaining()) {
                                   System.out.println("["+Thread.currentThread().getName()+"] Writing...");
                                   socketChannel.write(sharedBuffer);
                               }
                               sharedBuffer.clear();
                           }
                       }
                       catch (IOException e) {
                           System.err.println("["+Thread.currentThread().getName()+"] Error writing back bytes\");
                           e.printStackTrace();
                           selectionKey.cancel();
                       }
                      
                       try {
                           System.out.println("["+Thread.currentThread().getName()+"] Closing...\");
                           socketChannel.close();
                       }
                       catch (IOException e) {
                           e.printStackTrace();
                           selectionKey.cancel();
                       }
                   }
                   System.out.println("["+Thread.currentThread().getName()+"] Next...\");
               }
           }
       }
       catch (IOException e) {
           System.err.println("["+Thread.currentThread().getName()+"] Error during select()\");
           e.printStackTrace();
       }
  }
}


package test;

import java.net.*;
import java.io.*;

public class Connection {
  private static final int LOOP_COUNT = 100;
  private static final int SLEEP_TIME = 500;
  private static final int PORT = 9876;
 
  public static void main(String args[]) throws IOException, InterruptedException {
       for (int i=0; i&lt;LOOP_COUNT; i++) {
           Socket socket = new Socket("localhost", PORT);
           InputStream is = socket.getInputStream();
           OutputStream os = socket.getOutputStream();
           Writer writer = new OutputStreamWriter(os, "US-ASCII");
           PrintWriter out = new PrintWriter(writer, true);
           out.println("Hello, World");
           BufferedReader in = new BufferedReader(new InputStreamReader(is, "US-ASCII"));
           String line;
           while ((line = in.readLine()) != null) {
               System.out.println(i + \": \" + line);
           }
           socket.close();
           Thread.sleep(SLEEP_TIME);
       }
  }
}

:
Posted by codetemplate