Java를 이용한 웹개발시 Servlet 과 같은 Dynamic Page의 구현시에는 Servlet Container를 지원하는 Web Server를 사용하는 수 밖에는 없습니다. Servlet 을 지원하는 Java 다수의 경량 서버가 있습니다만, PDA 같은 임베디드급 머신에 포팅하여 동작 시키기에는 상당한 부담임에는 틀림없습니다.


 그래서 임베디드나 PDA 같은 H/W 리소스가 제한적인 장비에서 동작시키기 위한 경량 웹서버를 취미 생활(?)로 만들어 보았습니다. 이름하여 YG HttpServer 이며 아래 원칙하에 제작되었습니다.


- HTTP Fullspec이 아닌 단순 페이지뷰나 파일전송 정도의 기본적 기능만 지원하고 필요한 기능은 사용자가 확장할 수 있을 것

- 장치에 Capability에 맞춰 Setting이 쉬울 것

- Servlet 과 유사한 동적인 페이지를 지원 할 것


즉, WebServer의 평가항목 Performance, Scalability, Security, Avaliability, Compliance to standards, Flexibility, Platform requirements, Manageability 중  굵은 글씨로 표시된 항목에만 중점을 두었습니다.


 일단 위와 같은 특성들을 지원하기 위하여 웹서버를 최대한 단순화하여, 필요한 역할을 하는 부분들을 나누고 사용자의 요구에 따라 사용자가 직접 필요한 부분은 tuning 할 수 있도록, 


 Server를 구성하는 부분항목에 대해서는 OpenAPI를 제공 기능의 추가, 삭제, 부하량을 고려한 퍼포먼스 튜닝등을 용이하게 하였습니다.

 

 서버의 대략적인 서버 Core 모듈의 구조는 아래와 같습니다.

  

1. MicroWebServer에서 ServerSocket의 생성 관리 및 Client Socket Binding을 담당하여,


2. ServiceMapper에서는 생성된 Client Socket의 Header를 parsing 하여 요구 Resource를 추출, Resource를 처리할 수 있는 Service Object에 넘겨주는 역할을 합니다.


3. AbstractService를 extends 하여 사용자가 원하는 형태의 서비스를 구현도 가능하며, 현재 Web Server에서는 기본적으로 Html이나 일반적은 파일을 전송하는 StaticResourceService와 Servlet과 같은 동적인 웹페이지를 사용자가 구현할 수 있도록 경량화한 GdpService를 지원합니다.


4. Gdp 요청에 대해서는 사용자가 직접 작성한 Gdplet Object에 전달되어 개별적인 동적 처리가 이루어 지는 구조입니다.

4-1. 예제로 쓰인 Hello gdp 와 파일전송은 이후에 살펴보겠습니다.


5. IServiceScheduler는 다수의 요청의 처리를 위한 스케쥴러 Interface이며, 사용자는 Device의 Capa에 맞추어 적절한 형태의 스케쥴러를 직접 작성할 수도 있습니다.

5-1. 기본적으로 내장된 DefaultServiceScheduler는 ThreadPool의 형태로 구현되었고, Client의 요구를 순차적으로 저장했다가 처리하는 저장 순서에 따라 처리하는 Unbounded queue 가 사용되었으며, Pool 공간은 10입니다.(동시에 10건 이내의 요청만 처리 가능합니다.)

 

눈치 채신분도 많겠지만,

위의 웹서버는 Java의 NIO가 아닌 Standard IO로 구동되는 구조입니다. Standard IO는 다수의 동시처리를 위해 쓰레드를 사용할 수 밖에 없습니다. 


Performance가 떨어지는 Standard IO + ThreadPool 기반으로 제작한 이유는 NIO는 OS에 종속성이 있으며, JVM에 제약이 있는 임베디드 장비에서는 정상적인 동작이 어려울 수 도 있고, 애초에 의도가 대량 접속처리가 아니라 최대한 가벼운 환경에서 동작되는것을 목표로 하였기 때문입니다.

NIO 버전은 현재 ME 버전이 아닌 SE 버전에서 적용될 예정입니다.


아래는 위에서 설명한 Server의 Core 모듈입니다. (상업적 이용은 불가입니다. 모든 소스 오픈예정)




앞서 설명한 바와 같이 기본 Service 만을 탑재하여 아래와 같이 Cosole에서 실행 시킬 수도 있습니다.

주석으로 처리된 부분은 GpeService를 추가시키는 부분으로 사용자가 작성한 Gdplet 서비스의 탑재도 가능합니다.


Gdp는 Gonni Dynamic Page의 약자로 서블릿과 같이 사용자가 동적인 웹페이지를 구성할 수 있도록 제공하는 API를 제공합니다. 사용자는 Gdplet 이라는 class의 service(XRequest req, XResponse res) method만 구현하면 됩니다.


아래는 Hello GDP 예제입니다.


Http 프로토콜은 기본적으로 Header와 Body의 구조로,  Header에는 기본적으로 사용자가 Client에게 전송하는 data의 mime type의 지정이 필요하며,  Body부분에 실제 전송을 위한 data를 write 해주면 됩니다. (API상으로는 Character와 Binary 두가지 형태의 write를 모두 지원합니다. )

 

아래는 위의 Gdp 예제의 실행결과 입니다.


새로고침 버튼을 눌렀을 경우 페이지가 동적으로 현재시간에 맞추어 갱신되는 것을 확인 할 수 있습니다.


아래는 해당 모듈을 이용하여 만들어본 GUI 모듈입니다. 실행시 TrayIcon에 서버 실행이 활성된 Icon이 생성되고 사용자가 웹서버의 Root 폴더 지정 및 현재 서버의 상태를 확인 할 수 있습니다.(귀찮아서 할 수 있는게 많은데 여기까지 밖에 못했네요)



트레이 아이콘, Server 의 상징인 'G' 모양이 아이콘이 생성되었음을 확인 할 수 있습니다. ^^



서버 설정화면, Http File Server 운용을 위한 root dir 설정 및 서버의 로그, 순간 접속량 등을 확인 할 수 있습니다. 


아래의 파일은 UI 및 일부 Gdp를 포함하는 샐행파일이 포함된 Jar 파일입니다. (실행은 콘솔에서 java -jar  cKserver.jar 이며, 반드시 동일 폴더에 org.ygsoft.webserver.core_20100102.jar 파일이 같이 있어야만 동작합니다.)



Gdplet 테스트를 위해 UI 및 Demo gdp 가 포함된 Eclipse Project도 첨부합니다. 관심있으신 분들은 아주 적절하게 필요한 서비스를 확장하여 사용할 수 있으리라 확신합니다. (Gdplet 개발시 공유해 주시면 감사 ^^)



해당 프로젝트 내에서 Client에서 WebServer로 파일을 전송하는 gdp 예제도 포함되어 있습니다.  



아래의 웹페이지에 파일 전송시 서버 실행을 하였던 jar 파일이 포함된 폴더내에 download 폴더가 생성 파일이 저장되는 구조입니다. 원격지에서 집 Server에 파일 전송이 가능하죠. 본 예제를 참고, gdp를 확장하여 다양한 형태의 데이터 송수신 서비스를 구현 할 수 있을 거라 생각됩니다.


다음 버전에는 아래의 기능들을 추가해 볼까합니다.

- Session을 추가하여 간단한 사용자 인증 구현 (Open API 제공)

- Reflection을 활용하여 Service를 추가하는 기능

- GUI 개선

- GDP 예제 : 웹기반 파일 탐색기, CCTV, 원격제어 



어디까지나 취미생활 인지라 다음이 언제가 될 지는 모르겠네요..ㅋㅋ 

김영곤 (mailto:gonni21c@gmail.com)


디자인패턴에서 일반적으로 Rollback 구현을 위해서 memento 패턴을 소개하고 있습니다만,
일반적으로 Job이나 Work를 생성하는 유형의 Application의 구현에서,

Rollback을 기능을 구현하는 것이 그다지 쉬운 구현은 아니다. (memento로 커버하기엔 복잡도가 높다)

더 정확하게 말해 특정기능을 부분부분 프로세스로 나누어 프로세스를 하나하나 순차적으로 수행을 하는 과정을 하나의 Job으로 본다면,

각 프로세스에 대한 구현을 역으로 수행하는 부분의 구현이 필요하다. 

일반적으로 특정 프로세스가 수행될때 수행의 상태 정보를 저장해두고,
사용자에 의해 Rollback 요청이 왔을때  지금까지 했던 작업 상태 정보를 역으로 수행하는 프로세스를 역순으로 가동시켜야 한다. (상당히 귀찮은 작업이다)

그림으로 나타내면 대략적으로 아래와 같은 구동흐름을 보인다.

하나의 Job이 A -> B -> C -> D와 같은 4개의 단계의 프로세스로 실행되는 흐름을 가진다고 가정했을때,
C가 실행중인 시점에 사용자로 부터 Cancel 요청이 왔을때,
각 프로세스별 Rollback 프로세스가 C' -> B' -> A' 의 순서로 실행되어야,
해당 Job의 이전 상태로 복원되는 Rollback이 최종 수행된다

하나의 Job Class내에 이러한 Rollback의 구동흐름을 구현은 상당히 복잡해 진다.
(프로세스, 롤백프로세스를 구현하고 현재 실행상태와 흐름을 관리하는 코드를 구현해야 하기 때문이다)

이러한 복잡한 구현을 다른 class로 분리하고,
프로세스의 구현에 어려운 상태정보 관리를 개선하고자 아래와 같은 개념의 디자인의 흐름을 생각하게 되었다.

프로세스를 하나의 클래스로 정의하여,
하나의 프로세스 클래스 내에서 해당 프로세스의 수행 상태를 저장하고, 해당 프로세스의 rollback code까지 포함한다.

Tree와 같은 형태로 프로세스를 구성하여,
Root로 부터 특정 프로세스를 연쇄적으로 수행하여(재귀recursive호출),
특정 시점에 사용자로 부터 cancel 요청이 왔을때,
Tree를 역으로 순회하면서 rollback 프로세스를 수행하면,
A -> B -> C -> userCancel -> C' -> B' ->A'의 흐름으로 프로세스가 진행된다.

Tree 기반의 수행 흐름을 구현하기 위해,
Chain of Responsibility 와 Template Method 디자인 패턴의 아이디어를 적절히 조합하여 하나의 프로세스를 추상화한 아래와 같은 클래스를 구현하였다.

 
import java.util.*;

public abstract class AbstractJobProcess{
 
 private static int pid = 0;
 private String strCommand = null;
 
 private AbstractJobProcess nextProcess = null;
 private volatile boolean cancelByUser = false;
 
 private ArrayList<IProcessEventListener> lstListeners = null;
 //private Vector<String> lstResultLog = null;
 
 protected AbstractJobProcess(String command)
 {
  pid++;
  strCommand = command;
 }
 
 public String getCommand()
 {
  return this.strCommand;
 }
 
 public AbstractJobProcess addNextProcess(AbstractJobProcess process)
 {
  this.nextProcess = process;    
  return this.nextProcess;
 }
   
 public void cancel()
 {
  this.cancelByUser = true;
  if(this.nextProcess != null) this.nextProcess.cancel();
 }
 
 public boolean start()
 {
  if(this.cancelByUser) return false;
  
  if(this.process())
  {
   if(this.cancelByUser){
    // excute rollback process
    this.rollback();
    return false;
   } else {
    if(this.nextProcess != null)
    {
     boolean nextJobResult = this.nextProcess.start();
     if(nextJobResult)
      return true;
     else
     {
      this.rollback();
      return false;
     }
    }
    return true;
   }
  } else {
   
   this.rollback();
   return false;
  }
 }
 
 public abstract boolean process();  // 정상 process 구현
 public abstract boolean rollback();  // rollback 구현

}


 위의 추상클래스를 상속하여 개별 프로세스를 구현할 수 있으며, 구현 클래스에서는 abstract method인 process와 rollback을 구현해야 한다. (프로세스별 동작 방식과 상태 저장 방식이 틀리기 때문이다.)

 process와 rollback외 추가적으로 프로세스의 재귀적인 호출을 위한 start() method와 수행중인 Job의 취소를 위한 cancel을 제공하며, cancel() method 또한 연결 프로세스간 재귀적인 호출이 이루어 지는 구조이다
 
 프로세스간 chain과 같은 연결구조를 설정하기 위해 addNextProcess(AbstractJobProcess process) method도 제공한다.

 이 외에 프로세스에 상태정보를 event로 전송하기 위해 Observer Pattern을 추가적으로 적용할 수도 있다.

 위의 AbstractJobProcess를 이용하여 아래 구조의 rollback을 수행하는 Job(Task)을 구현해 보자.


TempJob은 Porcess A, B, C, D 순서의 실행구조를 가진 임의의 Job Class로 job의 시작과 중단을 위한 start()와 cancelJob() method를 제공한다.

ProcessA .. D는 Console에 각 프로세스의 message를 0.3초간 지연후 출력해주는 구현이 되어있다. process의 수행중 cancel 요청이 왔을때 실행시점에 rollback 프로세스가 실행됨을 보여주기 위해 임의의 sleep을 삽입하였다.

코드는 아래와 같다. ( Process 별 구현은 동일 )

 public class ProcessA extends AbstractJobProcess{

 protected ProcessA(String command) {
  super(command);
  }

 @Override
 public boolean process() {
  try { Thread.sleep(300); } catch (InterruptedException e) {}
  
  System.out.println("A excuted by " + this.getCommand());
  return true;
 }

 @Override
 public boolean rollback() {
  System.out.println("A' excuted by " + this.getCommand());
  return true;
 }

}


A, B, C, D 4개의 Process를 연결하여 실행시키는 코드는 아래와 같다. (TempJob.java)

 import java.util.Observable;

public class TempJob extends Observable{
 
 private AbstractJobProcess rootProcess = null;
 
 public TempJob()
 {
  ;
 }
 
 public void setProcess(AbstractJobProcess buildedProcess)
 {
  this.rootProcess = buildedProcess;
 }
  
 public void startJob()
 {
  this.rootProcess.start();
 }
 
 public void cancelJob()
 {
  System.out.println("[LOG] Request to cancel..");
  if(this.rootProcess != null)
   this.rootProcess.cancel();
 }
 
 public static void main(String...v)
 {
  // A, B, C, D 연결된 Process를 생성한다
  AbstractJobProcess rootProc = new ProcessA("procA");
  {
   rootProc.addNextProcess(new ProcessB("procB"))
    .addNextProcess(new ProcessC("procC"))
    .addNextProcess(new ProcessD("procD"));
  }
  
  final TempJob job = new TempJob();
  job.setProcess(rootProc);

  // 생성된 Process를 실행한다.
  job.startJob();
 }
 
}


chain의 형태로 아래와 같이 프로세스를 생성후 연결 시켰다
AbstractJobProcess rootProc = new ProcessA("procA");
  {
   rootProc.addNextProcess(new ProcessB("procB"))
    .addNextProcess(new ProcessC("procC"))
    .addNextProcess(new ProcessD("procD"));
  } 


해당 프로그램을 실행하면 아래와 같이 기대한 A->B->C->D의 연속적인 실행이 이루어 지는 것을 확인 할 수 있다.



Process가 실행중에 사용자에 의한 Cancel 요청시 Process의 rollback이 이루어 지는것을 확인하기 위해서,
main method 내에 아래의 code를 삽입하였다.

 public static void main(String...v)
 {
  // build proc
  AbstractJobProcess rootProc = new ProcessA("procA");
  {
   rootProc.addNextProcess(new ProcessB("procB"))
    .addNextProcess(new ProcessC("procC"))
    .addNextProcess(new ProcessD("procD"));
  }
  
  final TempJob job = new TempJob();
  job.setProcess(rootProc);
  
  // cancel job
  new Thread() {
   public void run() {
    try { Thread.sleep(800); } catch (InterruptedException e) {}
    job.cancelJob();
   }
  }.start();

  
  job.startJob();
  
 }

쓰레드는 실행 0.8초후 job의 cancelJob() method를 호출한다.
프로세스별 0.3 초간의 sleep이 있으므로 cancelJob()이 호출되는 시점은 대략적으로 ProcessC가 실행되는 시점이다.

위 코드를 실행하면 기대했던 A->B->C->userCancel->C'->B'->A'의 실행흐름을 확인 할 수 있다.



C가 실행중 cancel 요청이 왔으므로 C의 process() method가 최종 return을 한후 재귀적인 rollback이 수행된다.

정리 :
필자가 프로그램 구현중 그냥 생각해본 디자인 방식(? 패턴?) 이다. 
Template method, Chain of resposibility의 아이디어로 적절히 조합한 형태이다.
앞서 언급한 바와 같이 UI 와 연결된 프로그램이라면 Observer pattern등을 적용하여 Job의 정교한 진행상태를 확인 할 수 있을 것으로도 생각되며,
Interrupt 기반의 cancel이 아니기에 수행시간이 긴 Process가 실행중 User Cancel이 왔을때 rollback 처리가 즉각적이지 못하다는 단점도 있으나,
여러 사람이 작업하는 프로젝트에서 손쉽게 프로세스를 구현하여 추가, 삭제가 가능하다는 장점이 있다.

더 좋은 아이디어가 있다면 알려주면 감사하겠다.. ^^ 

김영곤 (gonni21c@gmail)
 

Java에서는 문자열 처리를 위해서 String이라는 기본 Class를 제공합니다.

하지만 String은 구조적 특성상 속도상으로는 효율이 떨어지는 편입니다.

아래의 코드를 예로 들면,

String str = "abc" + "def" + "gh";

str에는 "abcdefgh" 라는 문자열이 최종적으로는 저장되지만..

실제 str에 값이 할당되기 전까지 아래의 4개의 String 객체가 생성됩니다.


new String("abc");

new String("def");

new String("gh");

new String("abcdefgh");


new 로 4개의 객체가 생성되는데, 실제 프로그램에서 느껴지는 체감속도에는 크게 문제가 없는것 처럼 느껴지지만,

parser 개발시 추천, 수만의 위와 같은 String연산을 처리했을시 속도에는 치명적이다.

실제 객체지향 프로그램의 Coding에서 new 를 써서 객체를

메모리상에 할당하는 시간의 비중이 프로그램 수행속도에  가장 큰 영향을 미친다고 볼수 있다.

즉, new를 사용하여 객체를 생성하는 것은 비용이 매우 높은 일중에 하나라 볼 수 있다


그래서 자바에서는 이러한 String상의 문제를 해결하기 위해 StringBuffer를 제공한다.


StringBuffer str = new StringBuffer("abc");

str.append("def");

str.append("gh");


실제 위의 코드는 String과는 달리 str 객체를 단한번 생성하고, 문자열을 추가시킨다.

원리는 String과 달리 StringBuffer는 문자열을 저장하기위해 초기배열을 10정도로 메모리상에 할당한다.

그래서 매번 메모리에 고정된 문자 저장 공간을 할당하는 String에 비해  객체생성에 대한 비용이 상당히 줄어들기 때문에,

문자열 처리의 연산속도가 String에 비해 상당히 빠르다.


하지만 StringBuffer도 고정크기 Buffer를 사용하므로, Buffer크기 이상의 문자열이 할당될 경우,

내부적으로 새로운 Buffer크기의 StringBuffer 객체를 생성하므로 자료의 구조에 따라

일시적으로 속도가 느려지는 문제가 생긴다. 궁극의 해결책은 못된다.


String과 StringBuffer의 공통적인 문제는 문자열 저장을 위해 내부적으로는,

고정크기의 char 배열을 사용하는데 있어, 배열보다 큰 공간이 필요할때,

새로운 객체를 생성하기 때문에 시간적인 비효율이 존재한다.


그래서, 고정공간 배열이 아닌 Linked List 구조의 가변공간 기반의 자료형이 있으니,

이름하여 Java Rope이다.


아래의 사이트에 자세한 설명이 있으니 살펴보도록 하자.

http://www.ibm.com/developerworks/kr/library/j-ropes/index.html


아래는 특정 웹사이트의 URL의 내용을 긁어 오는 코드이다.


 private void loadRope(URL url, int timeout) throws IOException
{
  
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  
String ct = conn.getContentType(), charset = "euc-kr";
if (ct.indexOf("charset=") > 0)
  charset = ct.substring("charset=".length() + ct.indexOf("charset="));

conn.setConnectTimeout(timeout);
conn.setInstanceFollowRedirects(true);
String contentType = conn.getContentType();

if (contentType != null && contentType.startsWith("text/html")) {
InputStreamReader isr = new InputStreamReader(
conn.getInputStream(), charset);
BufferedReader br = new BufferedReader(isr);
String line = null;
//this.sBuf = new StringBuffer();
while ((line = br.readLine()) != null) {
         this.rope.append(line + "\r\n");
}
   br.close();
} else {
throw new IOException("Invalid URL :" + url);
}
}

BufferedReader로 읽은 문자열을,

StringBuffer와 Rope를 이용해 수행 속도를 측정한 결과 Rope가 5배 이상 빠른 결과를 보여주었다.


물론,

Rope도 만능은 아니다. 하지만 Cursor의 rollback이 없는 parser(lexer)의 구현이라면, 

사용해볼만한 가치가 충분히 있다고 보여진다.


김영곤(gonni21c@gmail)



- 켄트 백 :  프로그램을 짤 때는 자신과 컴퓨터뿐 아니라, 다른 사람들을 생각해야 한다! 
                ,공동체에 대한 기여가 개인으로써의 최대 목표


- 구현패턴 : 커뮤니케이션을 돕는 프로래밍기법에 초점


- 코드를 통한 커뮤니케이션을 위한 단계

1. 생각을 하며 프로그래밍 하는것
2. 다른 사람들의 중요성을 인정하는 것 - 프로그래밍은 한 사람과 기계 사이의 외로운 교류, 혹은 그이상
3. 구현패턴을 이용해 실천으로 옮기기


- 디자인패턴과 구현패턴
1. 디자인패턴 : 개발 기간 동안 하루에 몇 하례 해야하는 결정(보통 객체 간의 관계를 결정)
2. 구현패턴 : 몇 초에 한 번씩 하게 되는 결정

- 프로그램의 법칙
1. 새로 짜는 경우 보다 기존 프로그램을 읽는 경우가 많다.
2. 프로그램에 있어 '완성'은 없다. 최초 개발 비용보다 이후 수정 비용이 더 크다
3. 프로그램 구조는 몇 가지 상태와 제어 흐름 개념으로 결정된다
4. 프로그램을 읽는 사람은 개념과 더불어 세부 사항까지도 이해해야 한다.

- 패턴은 결정 요소들의 패턴이다.
- 결정 요소들 가의 상대적 우선 순위가 높은 패턴이 적용된다

- 패턴은 시간과 에너지를 줄여준다
- 패턴은 절대적인 진리가 아니므로, 사람의 의사 결정을 돕는 도구 정도로 생각하자.



- 결정 사항에 영향을 미치는 동력 force : 가치value 와 원칙principle

- 가치 : 모든 프로그래밍에 적용되는 주제 : 커뮤니케이션, 단순성, 유연성
- 원칙 : 패턴 : 중요하지만 때로는 직접 적용하기 어려운 가치와, 적용법은 명확하지만 조금은 지엽적인 패턴 사이의 가교 역할

- 패턴 : 지금 당장 무엇을 해야 할지 알려주고,
- 가치: 패턴을 사용해야 하는 동기를 알려주며,
- 원칙 : 동기를 행동으로 어떻게 바꿔줄지 알려준다.

- 훌륭한 프로그램의 공통적 가치 : 커뮤니케이션, 단순성, 유연성, 확장성을 고래해서 프로그램을 짜지만, 불필요한 요소를 사용하지 않으며, 읽고 이해하기 쉬운 프로그램을 짜야한다.

[가치]
- 커뮤니케이션
 개발자가 코드를 쉽게 이해하고, 수정하고, 사용할 수 있는 상태
- 단순성
 의미 없는 코드는 모두 제거, 설계 시에도 과도한 요소는 모두 제거, 요구 사항을 분석해 꼭 필요한 사항만 뽑아내라
- 유연성
 개발 비용은 대부분 처음 프로그램이 작성된 후부터 들어간다, 프로그램의 수정은 가급적 쉬워야 한다.
 유연성이 있으면서도 당장 이득을 얻을 수 있는 패턴 사용

=> 구현패턴의 학습은 단순하고 이해하기 쉬우면서 수정이 쉬운 코드를 작성하는 것이 최종목표!!

[원칙]
- 지역적 변화
- 최소 중복
- 로직과 데이터의 결합
- 대칭성
- 선언적 표현
- 변화율


/** 본 내용과 관련 하여 대학생 졸작, 프로젝트 관련 문의 메일은 사절입니다 **
** 그동안 문의메일만 수십통 왔습니다 **
** 20세기에나 먹혔을 법한 과제 아이템을 아직도 할려고 하는 분들, 부끄러운줄 아셔야 합니다 **/


Simple remote control application based on Java.

반나절 걸려 대충 완성한 웹기반의 원격접속 제어 프로그램입니다.
Windows 에서만 test 해봤는데 파일 경로 문제로 Linux에서는 약간의 수정이 필요할 듯 하네요..

자바에서는 응용프로그램의 UI Test 용으로 Robot 라는 class를 제공합니다.
Robot를 이용하여 화면영역, Keyboard, mouse 등을 api를 통해 제어할 수 있습니다..



위의 프로그램을 다운 받아서 아래와 같이 실행하면 현재 화면 웹서버가 가동됩니다.


default로 8181 포트에서 동작하지만 사용자가 임의로 포트를 설정할 수 도 있습니다.
위의 명령어는 8989 port에서 웹서버를 가동 시킵니다.

브라우저를 열고 주소창에 다음과 같이 입력합니다.



그러면 아래와 같은 현재 캡쳐된 바탕화면이 뜹니다.


이미지의 특정 부분을 클릭하면, 로컬 pc에서 마우스 포인터가 화면상 지정위치와 같은 위치에서 클릭이 일어납니다.
(내부적으로 이미지 클릭좌표는 자바스크립트로 추출, HTTP GET protocol spec으로 서버와 동작합니다.)
물론 대충 만들어서 화면의 갱신속도는 일반 사용프로그램과 비교시 매우 느린 편입니다.
또한 현재 지원 되는 이벤트는 마우스 클릭뿐입니다.

하지만 별도 프로그램 필요없이 별도의 브라우저만 열어서 집이나 회사의 PC를 원격제어 할 수 있다는 점에서 괜찮은 시도가 아닌가 생각됩니다..(^^흐뭇..)
브라우저가 내장된 PDA에서도 접속해서 쉽게 사용할 수 있으니까요..

향후 키입력이나, 드래그앤 드뢉, 분할 이미지 전송, Session 기반 사용자 AA, 시스템 트레이 등을 구현할 예정입니다.

현재의 구현상태로 주식HTS 프로그램 뛰워두고 입력이 많이 필요치 않은 정적인 작업으로 쓰기에 큰 부족함은 없을 듯 하네요.. 직장내에서 몰래 주식질 하는 묘미란..ㅎㅎ

** 참고로 웹서버 부분의 구현은 최범균님의 NIO기반 Simple http server 코드를 약간 수정하였습니다.
** 코드는 정리후 공개하도록 하겠습니다.
** 급하신 분들은 첨부 파일을 역컴파일 하셔도 되겠습니다..ㅋ

김영곤(gonni21c@gmail.com)

드라마나 영화로 영어공부 할 때
자막 파일에 두개의 언어가 포함된 경우,
영어공부를 하는 입장에서 하나로 묶어서 출력해서 보고 싶다는 생각이 드는건 저 뿐이 아니라는 생각이 드네요..ㅎ

다행히 smi 파일이 tag구조라..
제가 만들어둔 YGHtml parser를 이용해서 한시간만에 자막 추출 프로그램을 만들어 보았습니다..

해당프로그램 다운로드

이 프로그램을 실행시키기 위해선 Java Virtural Machine이 pc에 설치되어 있어야 합니다.
없으신 분들은 적절한 검색으로 JVM 환경을 pc에 setting 합니다.

파일을 다운받고 임의의 폴더에 저장합니다.
콘솔에서 명령어로           java -jar smi2view.jar          를 입력합니다.

아래와 같은 Simple한 화면이 뜨고, Open을 누르면 파일 선택창이 뜹니다.
변환을 원하는 smi 파일을 선택후..


Convert 버튼을 누르면 아래와 같이 영어와 한글이 적절히 Mix된 Text를 확인 할 수 있습니다.
Drag&Copy후 메모장이나 기타 워드 프로그램에 Paste해서 출력해서 사용하면 될 것 같네요..

아시는 분들은 알겠지만 프랜즈 자막입니다..^^


한시간 만에 급조된 프로그램이라, 퀄리티가 현저히 떨어지니 정말로, 꼭, 꼭 필요한 분들만 이 프로그램의 사용에 도전해 보도록 합시다..ㅎㅎ


김영곤(gonni21c@gamil.com)

+ Recent posts