겸손하기

ENJOY MY LIFE 2009/12/23 02:45
사람이 겸손해지기란 참 어려운 것 같습니다. 제 스스로도 반성하고 겸손해지려 노력하지만 문득 습관처럼 잘난 맛에 도취되기 십상인 것이 아직도 멀었나 봅니다.
크리에이티브 커먼즈 라이센스
Creative Commons License
2009/12/23 02:45 2009/12/23 02:45

자바에서 제공하지 않는 기능인 명령 옵션을 파싱하려면 부가적인 작업이 필요합니다. 이미 unix나 c에서는 getopt를 이용하여 명령행 옵션을 처리하고 있으나 java에서는 일일이 split을 해줘야 합니다.

여기에 gnu의 getop API를 이용하여 명령행 옵션을 받아들이는 샘플은 다음과 같습니다.

import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;

public class Main {
    public void process(final String [] args) throws Exception {
        processCommandLine(args);
    }
    
    private void processCommandLine(String[] args) throws Exception {
       
          String programName = System.getProperty("program.name", "JBoss Chameleon");
          String sopts = "-:h:w:p:c:";
          LongOpt[] lopts =
          {
             new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'),
             new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'V'),
             new LongOpt("wastype", LongOpt.REQUIRED_ARGUMENT, null, 'w'),
             new LongOpt("sourcedd", LongOpt.REQUIRED_ARGUMENT, null, 'c'),
             new LongOpt("target", LongOpt.REQUIRED_ARGUMENT, null, 'p')
          };
          Getopt getopt = new Getopt(programName, args, sopts, lopts);
          int code;
          String arg;
          
          while ((code = getopt.getopt()) != -1)
          {
             switch (code)
             {
                case ':':
                case '?':
                   // for now both of these should exit with error status
                   System.exit(1);
                   break; // for completeness

                case 1:
                   System.err.println(programName +
                                      ": unused non-option argument: " +
                                      getopt.getOptarg());

                case 'h':
                   // show command line help
                   System.out.println("usage: " + programName + " [options]");
                   System.out.println();
                   System.out.println("options:");
                   System.out.println("    -h, --help                    Show this help message");
                   System.out.println("    -V, --version                 Show version information");
                   System.out.println("    --                            Stop processing options");
                   System.out.println("    -w, --wastype=[<name>]        Set proprietary app server; Must be one of belows");
                   System.out.println("                                  jeus4, jeus5, jeus6, weblogic8, weblogic9, weblogic10");
                   System.out.println("    -c, --sourcedd=<filename>     Set deployement descriptor for converting");
                   System.out.println("    -p, --target=<jboss_ver>      Set JBoss target version; Must be jboss4,jboss5 or jboss6");
                   System.out.println();
                   System.exit(0);
                   break; // for completeness

                

                case 'c':
                {
                   // set the patch URL
                   arg = getopt.getOptarg();
                   System.out.println("SOURCE DEPLOYMENT DESCRIPTOR : " + arg);

                   break;
                }
            
                case 'w':
                {
                   // set the patch URL
                   arg = getopt.getOptarg();
                   System.out.println("SOURCE APP SERVER : " + arg);

                   break;
                }
                
                case 'p':
                {
                   // set the patch URL
                   arg = getopt.getOptarg();
                   System.out.println("TARGET APP SERVER : " + arg);

                   break;
                }

                case 'V':
                {
                   System.out.println("JBoss Chameleon 1.0 M1");
                   System.out.println();
                   System.out.println("Distributable under LGPL license.");
                   System.out.println("See terms of license at gnu.org.");
                   System.out.println();
                   System.exit(0);
                   break; // for completness
                }
                            
                
                default:
                   // this should not happen,
                   // if it does throw an error so we know about it
                   throw new Error("unhandled option code: " + code);
             }
          }


        
    }
    
    public static void main(String[] args) throws Exception {
        Main main = new Main();
        main.process(args);
}
}

받아들일 옵션은 -h, -w, -p, -c를 받아들이며 각각 매핑은 아래와 같이 LongOpt를 이용하여 문자열을 기술하면 매핑됩니다.
이 결과를 이용하여 switch 안에서 처리하고 싶은 작업을 진행하면 됩니다.
크리에이티브 커먼즈 라이센스
Creative Commons License
2009/12/18 17:10 2009/12/18 17:10

XML을 변환하여 다른 속성의 결과를 보고자 할 때 보통 StreamSource를 이용하는 것이 일반적인데 이 때 파서가 DTD를 찾게 됩니다. 해당 URL이나 file path에서 DTD를 찾지 못하게 되면 FileNotFoundException을 발생시키게 됩니다.

이것저것 테스트해보다가 가장 효과적인 방법은 입력 소스의 DTD선언부를 없애고 나머지 XML에 대한 변환을 만드는 것입니다. 그렇지 않게 된다면 입력 xml에 대한 것을 개발자가 매번 변경해줘야 한다는 단점이 발생을 합니다.

아래의 메소드를 이용하여 DTD선언부를 comment 처리해버리는 방법을 사용할 수 있습니다.

public static void ignoreDTD(String xmlFileName) {
        try {
            java.io.File fXML = new java.io.File(xmlFileName); 
            java.io.FileReader fr = new java.io.FileReader(fXML);
            long nChars = fXML.length();
            char[] cbuf = new char[(int) nChars];
            int n = fr.read(cbuf);
            String sXML = new String(cbuf); 

            java.util.regex.Pattern p = java.util.regex.Pattern.compile("<!DOCTYPE.*[\n\r]*.*>");
            java.util.regex.Matcher m = p.matcher(sXML);
            boolean bFound = m.find();
            System.out.println(bFound);
            sXML = m.replaceFirst("<!-- " + m.group() + " -->"); // With DOCTYPE commented out,

            System.out.println(sXML);
            fr.close();
        } catch (java.io.FileNotFoundException e) {
            System.out.println(e.getMessage());
        } catch (java.io.IOException e) {
            System.out.println(e.getMessage());
        }
    }


RegEx 설명
<!DOCTYPE.*[\n\r]*.*>
<!DOCTYPE : <!DOCTYPE 으로 시작하는 문자열이 첫부분임을 선언합니다.
.* : 어떤 문자든 0번 이상 받을 수 있습니다.
[\n\r]* : 다음 라인으로 0번이상 넘어갈 수 있음을 알립니다.
.* : 다음 라인에서 어떤 문자든 받을 수 있습니다.
> : 마지막글자가 ">"로 끝나는 것을 받도록 합니다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2009/12/17 14:35 2009/12/17 14:35

Jeus DTD 파싱 처리시 FileNotFoundException 때문에 Jeus 4.X를 받아서 다시 설치하고 거기의 DTD 파일을 이용하여 처리하였습니다.

혹시 나중에 필요할지 몰라 기록 남깁니다.
크리에이티브 커먼즈 라이센스
Creative Commons License
2009/12/16 07:51 2009/12/16 07:51

Caused by: java.io.FileNotFoundException: http://www.tmaxsoft.com/jeus/dtd/4.0/jeus-ejb-dd.dtd
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1168)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:973)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:905)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startDTDEntity(XMLEntityManager.java:872)
    at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.setInputSource(XMLDTDScannerImpl.java:282)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDispatcher.dispatch(XMLDocumentScannerImpl.java:1021)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:368)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:834)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:148)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1242)
    at org.milyn.delivery.sax.SAXParser.parse(SAXParser.java:50)


위의 에러를 보고 뭐라 할말을 잊었습니다. 해당 dtd 파일에 대한 url을 브라우저 창에 복사해보면 404에러. 정말 이런 말해서는 안되지만, 정말 이런 말 해서는 안되지만...

정말 WAS 벤더인지 의심스럽습니다.


크리에이티브 커먼즈 라이센스
Creative Commons License
2009/12/15 18:29 2009/12/15 18:29

가끔 어려운 말로 이야기하면 본인이 좀 있어보인다거나 똑똑해 보인다고 생각하는 사람 혹은 블로거들이 있는 것 같습니다. 가장 중요한 건 눈높이를 맞추고 대화 상대와의 교감이 이루어질 때 진정한 의사소통이 이루어지게 되는 것입니다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2009/12/14 08:43 2009/12/14 08:43

JBoss용 모니터링 엔진 및 OA&M(Operation Administration and Management) 을 만들고 있는 중입니다. 얼핏 보면 개념이 단순하면서도 파고 들어가면 복잡한 것이 JBoss입니다. Multiplexing 기능 및 개념이 있었다면 편했었을 텐데 플러그인 구조의 아픔이 이 툴을 만들때 나타나고 있습니다.

각설하고 smooks라는 도구가 있습니다. EAI에서 자주 사용하는 transformation 도구입니다. 즉 다양한 source-target을 변환시킬 수 있는 도구인데 configuration 핸들러로 xml binding시에도 편리하게 사용 가능합니다.

이러한 변환도구의 output filter에 freemarker 템플릿을 이용하면 어떠한 메시지라도 다양한 결과값으로 전환해지는 것이 가능합니다. 안타깝게도 smooks라는 걸 모르는 분들이 대부분이고 제가 나중에 또 다른 모듈을 만들어야 할지 모르므로 기억을 남깁니다.

Java to XML이기 때문에 입력은 없습니다. Smooks config에서는 단순히 결과값이 freemarker 템플릿인 ftl 파일로 옮겨갈 것이라고 선언합니다.

smooks-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd" xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.2.xsd">
  <ftl:freemarker applyOnElement="#document">
    <ftl:template>/org/jboss/jam/admin/jdbc/model/xa-datasource.ftl</ftl:template>
  </ftl:freemarker>
</smooks-resource-list>


그리고 변환을 실제 실행시키는 자바 코드를 작성합니다.

Test Java Source
package org.jboss.jam.admin.jdbc.model;

import org.jboss.jam.admin.jdbc.model.CommonDataSourceConfig;
import org.milyn.Smooks;
import org.milyn.SmooksException;
import org.milyn.payload.JavaSource;
import org.xml.sax.SAXException;

import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

public class XATest {
    @SuppressWarnings("unchecked")
    protected String runSmooksTransform(Object inputJavaObject) throws IOException, SAXException {
        Smooks smooks = new Smooks("smooks-config.xml");

        try {
           
            StringWriter writer = new StringWriter();
    
            JavaSource source = new JavaSource(inputJavaObject);
            source.setEventStreamRequired(false);
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("dsconfig", (CommonDataSourceConfig)inputJavaObject);
            source.setBeans(map);
            smooks.filterSource(source, new StreamResult(writer));
            
            return writer.toString();
            
        } finally {
            smooks.close();
        }
    }

    public static void main(String[] args) throws IOException, SAXException, SmooksException {
        XATest smooksMain = new XATest();
        XADataSourceConfig config = new XADataSourceConfig();
        config.setJndiName("JiraDS");
        config.setUseJavaContext("true");
        config.setXaDatasourceClass("com.mysql.jdbc.DataSource");
        config.addDataSourceProperty("DatabaseName", "multi1");
        config.addDataSourceProperty("ServerName", "localhost");
        config.addDataSourceProperty("PortNumber", "1360");

        System.out.println("This needs to be transformed to XML.");

        String transResult = smooksMain.runSmooksTransform(config);
        System.out.println("\n");
        System.out.println(transResult);
    }

}

        config.addDataSourceProperty("DatabaseName", "multi1");
        config.addDataSourceProperty("ServerName", "localhost");
        config.addDataSourceProperty("PortNumber", "1360");
붉은 글씨로 쓰여진 부분은 hashmap에 저장하는 부분입니다.
JavaSource의 setBean메소드에서 freemarker에서 사용하게 될 객체를 내보내고 있습니다.
map.put("dsconfig", (CommonDataSourceConfig)inputJavaObject);
위의 map에 저장한 dsconfig는 JavaBeans 규약에 의해 getter를 통한 접근을 시도하게 될 것입니다.

마지막으로 freemarker template를 선언합니다.

<?xml version="1.0" encoding="UTF-8"?>
<#-- ignore white space for common config -->
<datasources>
  <xa-datasource>
    <jndi-name>${dsconfig.jndiName}</jndi-name>
    <xa-datasource-class><#if dsconfig.xaDatasourceClass??>${dsconfig.xaDatasourceClass}<#else></#if></xa-datasource-class>
    <#list dsconfig.xaDatasourceProperty?keys as key>
        <xa-datasource-property name="${key}">${dsconfig.xaDatasourceProperty[key]}</xa-datasource-property>
    </#list>
    <#include "/org/jboss/jam/admin/jdbc/model/common-datasource.ftl">
    
  </xa-datasource>
</datasources>




그냥 자바 객체의 getter에 접근할 때는 dsconfig.getterName을 호출하면 됩니다. HashMap의 경우 key, value의 쌍으로 구성되어 있으므로 ?keys를 이용하여 해당 키값을 'key'로 추출한 후 "object.getter[key]" 의 형태로 추출하면 됩니다.



크리에이티브 커먼즈 라이센스
Creative Commons License
2009/12/11 14:59 2009/12/11 14:59

뉴스 구독 중 삼성 바다 런칭 이야기가 있어 혹시나 해서 보았는데 이건 아닌가 하는 생각이 듭니다. 혹시 T모사의 박모 회장님께 배운 건 아닌지 모르겠습니다.

주변에 아이폰을 많이 사셔서 안드로이드폰은 언제 나오나 이래 저래 검색하던 차에 아래의 소식을 접하고 살짝 놀랐습니다.

아래는 바다 플랫폼 출시 행사에 대한 뉴스와 영어 공부겸 번역한 내용입니다.
원문은 http://www.informationweek.com/blog/main/archives/2009/12/samsung_redefin.html;jsessionid=ZJW3EXBAECIDBQE1GHPSKHWATMY32JVN에서 보실 수 있습니다.

 삼성이 베이퍼웨어를 재정의하다. '바다'
삼성은 오늘 런던에서 새로운 자신만의 모바일용 OS인 Bada에 대한 정보를 공유하기 위한 기자회견을 열었다. 그곳에 참석하기 위해 대서양을 건넜지만 불행하게도, 삼성이 보여준 모든 것은 참석자들에게 바다를 온갖 선전문구로 어지럽히는 능력뿐이었다.

오늘의 행사가 시간낭비였다고 하는 것도 아주 약하게 표현하는 것이다.  바다는 삼성의 새로운 플랫폼이 될 것이고, 스마트폰의 능력을 일반폰에게도 제공할 수 있게 될 것이라며 지난 달에 처음 발표를 했다. 오늘 참석자들은 오늘의 행사가 새로운 모바일 플랫폼의 공식적인 출시였기 때문에  더 많은 것을 알고 싶어했지만 결과적으로 '출시'은 없었다.

화려한 직급을 가진 사람들이 모여 이야기했지만 새로운 것은 없었으며 삼성은 어떤 시연이나 폰, 심지어 새로운 운영체제가 어떻게 생겨먹었는지와 작동되는지 보여주는 스크린샷 조차 보여주지 못했다. 대신 끊임없는 선전 문구만이 울려퍼졌다.
삼성은 허구만을 보여주었고 그게 전부였다. (삼성은 C++이 사용가능한 바다는 플래시 기반의 UI와 차세대 TouchWiz 사용자 인터페이스가 될 거라고 전했다)

삼성은 수많은 기대만 만들어낸 후 몇몇 개발 협력사를 자랑스럽게 소개했는데 가장 좋았던 사람은 트위터의 모바일 비즈니스 총책임자였다. 그는 일어나서 자신을 소개한 후 "우리는 바다가 제공하는 기회들에 대해 매우 흥미롭다"로 말한 후 바로 자리를 떠났다. 이 한마디를 위해 지구의 반을 날아오게 했어야 했나?

가장 볼썽사나웠던 순간은  바다 플랫폼 상에서 돌아가는 잠깐의 게임데모였다. 그 게임의 이름이 생각 안나지만 1인 슈팅 게임이었다. 시연 도중 그 큰 스크린 상에서 사람의 머리에 총을 쏘는 것이 삼성으로써 품위가 있는 것인가?

아래의 글들은 오늘 행사에서 삼성 중역의 몇 가지 이야기를 발췌한 것이다.
"안드로이드는 너무 비싸다"
"안드로이는 사용하기 너무 어렵다"
"바다는 가장 뛰어난 사용자 인터페이스를 가지고 있다."
"폰은 그저 폰이 아닌 완전한 게임 플랫폼이다."
"게임은 Bada가 가진 능력의 중요한 부분이 될 것이다."
"바다는 아름답다."
"휴대폰은 최고의 리모컨이 될 것이다."

결론 : 현재 바다는 아무것도 없으며 아주 명확한 베이퍼웨어(개발은 요란하지만 실제로 완성될 가능성이 없는 소프트웨어)이다. 삼성이 실제로 휴대폰에서 작동하는 버전의 플랫폼을 만들수 있을 때까지는 그저 흥미로운 아이디어일 뿐이다.

대체 안드로이드가 비싸다는 이야기는 무슨 근거고 하고 있는지 모르겠습니다.아래의 기사를 읽어보면 다음의 문구가 나옵니다. 오픈 소스와 연관이 있는 의미있는 말입니다.
"구글폰 언제나와?" 구글도 몰라

어쨌거나 현재까지 구글 내부의 분위기는 전구를 쓸 때마다 에디슨을 떠올리지 않는 것처럼 구글 안드로이드를 쓴다고 해서 꼭 구글을 기억해야 할 필요는 없다는 의견이 지배적이다. 웹이 '개방'과 '공유'에서 출발했던 것처럼, 안드로이드의 개발철학 역시 '개방'이라는 것이다.

구글이 안드로이드 개발에 들인 비용은 공개되지 않았지만, 관련 애플리케이션 공모대회에 상금으로 내건 금액만 1000만달러(약 157억원)에 이른다. 개발하는데도 꼬박 1년이 걸렸다. 그러나 구글은 안드로이드를 통해 거둬들인 수익은 전혀 없다. 덕을 본 쪽은 오히려 휴대폰제조사들이다. 힘들게 운영체제를 개발하지 않아도 되는데다, 라이센스 비용까지 들지않아 스마트폰의 원가를 10% 가량 줄일 수 있다고 한다.





크리에이티브 커먼즈 라이센스
Creative Commons License
2009/12/10 10:51 2009/12/10 10:51

SQL> Begin                                                    
   for c in (select table_name from user_tables) loop
  execute immediate ('drop table '||c.table_name||' cascade constraints');
  end loop;
  End;
  .
SQL> run
SQL> PURGE RECYCLEBIN;





크리에이티브 커먼즈 라이센스
Creative Commons License
2009/12/09 15:46 2009/12/09 15:46

JEE6의 final ballot이 승인받음과 동시에 JBoss AS6 M1이 릴리즈(http://www.jboss.org/community/wiki/AS600M1ReleaseNotes)되었습니다. 어찌된 일인지 SpringSource는 JEE6와 관련한 대부분의 스펙에서 투표를 하지 않았습니다.(Apache의 반대는 예상한 일이었습니다).

레드햇이 주도하고 있었던 EJB3, JSF2, CDI(JSR-299, Contextual and Dependency Injection for Java), Bean Validation, Servlet 3 중 아래의 내용이 포함되었습니다.

  • JSF 2 - Includes major improvements over JSF1 such as AJAX support and integration with Bean Validation.
  • Bean Validation (JSR-303) -  Provides centralized validation of model objects accross all tiers (presentation, data, etc).
  • CDI [Weld RI] (JSR-299) - Offers an innovative and feature rich context management and dependency injection framework for EE applications.
JSF2는 Ajax 모델을 근간으로 한 웹 컴포넌트이며 여기에 Hibernate Validation 4를 표준화한 Bean Validation이 JEE의 모든 레이어에서 전방위적인 검사를 진행할 수 있게 되었습니다. CDI는 이러한 것들을 모두 한데로 묶어주는 역할을 하게 되었습니다.

Gavin의 이야기처럼 lightweight WAS(Web Profile)를 함께 위의 기술들을 이용하면 애플리케이션의 생산성은 극대화되지 않을까 합니다. 궁금하신 분은 Seam 애플리케이션을 개발해보시길 권해드립니다. XML의 설정이 많지 않은 상황에서 모든 레이어가 엮일 수 있다는 것이 획기적입니다.

혹자는 "자바 표준이 뭐 그리 대단하냐, 스펙이 요구를 제대로 따라주지 않는데.."라고 말한다면 한 방 갈겨주고 싶습니다. 금번 JEE6는 모든 부분이 오픈소스 커뮤니티의 요구사항들이 받아들여졌기 때문에 그 동안 골치아팠던 문제들을 상당히 해소한 상태입니다. 게다가 스펙들이 SPI를 통해 자연스럽게 플러그인시켜 다른 모듈들을 개발할 수 있도록 설계되어 응용 패키지를 만들 수 있는 손쉬운 환경을 제공한다는 것도 하나의 장점으로 보여집니다.

개인적으로 6.0이 나와서 개발자분들에게 보다 좋은 기회를 주는 것은 정말 좋지만 request pre-processor에 muxer도입이 먼저 좀 이루어졌으면 하는 바램입니다.
크리에이티브 커먼즈 라이센스
Creative Commons License
2009/12/03 11:28 2009/12/03 11:28