본문 바로가기
Web Program/Java Lecture

jsp 프리컴파일

by 현이빈이 2008. 8. 13.
반응형
  • 인트로

 

웹어플리케이션이 개발이 끝난 후 배치를 할 서버가 개발서버와 다른 경우이거나,

하나의 패키지를 여러 사이트에 설치하게 된 경우,,

혹은 어떤 제약상 컴파일된 jsp클래쓰외에 자바파일이나 jsp source 자체를 지워야 하는 경우..

 

jsp 파일이 이미 컴파일(precomiled) 되어있지 않았다면

요청을 받은 서버는 최초 요청시에 jsp->java->class 상태로 변환이 되고,

이 때 발생하는 오버헤드가 서버의 성능(물리적인 머신의 퍼포먼스) 여하에 따라 문제가 될 수 있으며

이러한 변환을 해야하는 jsp (혹은 fragment)들이 수백(많게는 수천) 페이지가 된다면

최초에 각 페이지를 요청한 사람들은 느릿느릿한 페이지 전송 속도에 무척이나 답답하게 될 것입니다.

 

또한 이번에 작업한 패키지를 여러 사이트에 설치하게 되면서 가뜩이나 느린 서버들이 몇 군데 있어

프리컴파일의 필요를 느껴 간단하게 제작하였습니다.

 

 

이 소스 코드의 아이디어는 록스(빨간책.. 팬입니다.) professional jsp 2nd의

프리컴파일 프로토콜(쿼리 스트링에 jsp_precompile 을 이용한)에서 아이디어를 얻었으며,

책 소스중 Precompile class 에 jsp 파일을 검색(재귀를 이용하여 하위 디렉토리까지 검사)하는 기능 정도를

추가한 것입니다.

프리컴파일이 진행되는것을 모니터링하기 위해서 log4j를 간략하게 add-on 시켜 봤습니다.

한빛 ITexpertModel2 jsp 책을 참조했습니다.(강력하고 매우 좋았습니다. 필자분들께 감사를 표합니다.)

역시나 isDebugEnabled() 메서드등이 훌륭한거 갔습니다.

 

 

  • 설치

log4j api(jar)와 log4j.properties파일을 클래스패쓰가 인식할수 있는 곳에 배치

PreCompiler.java 파일을 컴파일/실행

(PreCompiler class는 메인을 포함한 한 클래스로 구현된 간단한 구조입니다.

 

  • 실행

[root@www ext]# java PreCompiler www.jazzvm.net 80 struts /xxx/webapps/struts/
 INFO (PreCompiler.java:66) - ############# constructor initiated!! #############
 INFO (PreCompiler.java:67) - target server      :
www.jazzvm.net
 INFO (PreCompiler.java:68) - given port         : 80
 INFO (PreCompiler.java:69) - webmodule(context) : struts
 INFO (PreCompiler.java:70) - context directory(absolute dircetory path)
                            
 :/xxx/webapps/struts/
DEBUG (PreCompiler.java:127) - found JSP : /xxx/webapps/struts/loginFb.jsp
DEBUG (PreCompiler.java:159) - [41 / 6]/struts/loginFb.jsp?jsp_precompile
DEBUG (PreCompiler.java:127) - found JSP : /xxx/webapps/struts/main.jsp
DEBUG (PreCompiler.java:159) - [41 / 6]/struts/main.jsp?jsp_precompile
DEBUG (PreCompiler.java:127) - found JSP : /xxx/webapps/struts/admin.jsp
DEBUG (PreCompiler.java:159) - [41 / 6]/struts/admin.jsp?jsp_precompile
DEBUG (PreCompiler.java:127) - found JSP : /xxx/webapps/struts/error.jsp
DEBUG (PreCompiler.java:159) - [41 / 6]/struts/error.jsp?jsp_precompile
DEBUG (PreCompiler.java:127) - found JSP : /xxx/webapps/struts/login.jsp
DEBUG (PreCompiler.java:159) - [41 / 6]/struts/login.jsp?jsp_precompile
 INFO (PreCompiler.java:88) - #### total 5 file precompiled ####
[root@www ext]#

  • 소스

/*
 * @(#)PreCompiler.java
 *
 * Copyright (c) 2002 Jazz Virtual Machine, Inc.
 * HaeUnDae, Busan, Korea.  All rights reserved.
 *
 * 저작권자 표시는 삭제 할 수 없습니다.
 * 저작권자 표시를 삭제 하지 않는 경우에 한하여
 * 소스의 수정/재배포는 자유롭게 할 수 있으며
 * 기능 개선이 되었을 경우 반드시 알릴 필요는 없습니다.
 */
package net.jazzVM.jsp;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.log4j.Logger;

/**
 * 서브디렉토리의 jsp파일을 검색하여 프리(pre)컴파일을 실행하는 클래스
 * 당연히 타겟서버가 기동하고 있어야 함
 * visible을 제공하는 메서드는 현재 컨스트럭터와 build 2가지 뿐
 *
 * @version  v0.0  2003. 7. 25.
 * @author      Yoon YongHyun(jazzvm)
 */
public class PreCompiler {

 /** apache log4j를 사용하기 위한 Logging 대상 클래스 */
 private static  Logger logger =
      Logger.getLogger(PreCompiler.class.getName());

 /** jsp precompile기능을 사용하기 위해 URL에 어팬드할 쿼리스트링 */
 private final static String queryString = "?jsp_precompile";

 /** 서버 host name (dns혹은 ip) */
 private String  host;
 /** 포트번호 */
 private int     port;
 /** 웹모듈(컨텍스트)네임 */
 private String  webmoduleName;
 /** 웹모듈(컨텍스트)까지의 실제 경로 */
 private String  baseDirectory;

 /** 프리컴파일이 실행된 갯수를 저장하는 변수 */
 private int     compiledCount = 0;
 

 /**
  * 디폴트 컨스트럭터(사용할 수 없음)
  */
 private PreCompiler() throws Exception {
  throw new Exception("can't use default constructor!!");
 }

 /**
  * 사용가능한 컨스트럭터
  * 멤버 필드 셋팅
  * (추후 디폴트 포트에 대한 PreCompiler(url, webmoduleName, baseDirectory)
  * 와 같은 시그너처의 컨스트럭터 오버라이딩이 요구됨)
  *
  * @param url
  * @param port
  * @param webmoduleName
  * @param baseDirectory
  */
 public PreCompiler(String host,
                    int    port,
                    String webmoduleName,
                       String baseDirectory)
    {
  this.host          = host;
  this.port          = port;
  this.webmoduleName = webmoduleName;
  this.baseDirectory = baseDirectory;
 
  if(logger.isInfoEnabled()) {
   logger.info("############# constructor initiated!! #############");
   logger.info("target server      : " + host);
   logger.info("given port         : " + port);
   logger.info("webmodule(context) : " + webmoduleName);
   logger.info("context directory(absolute path) :" + baseDirectory);
  }
 }
 
 /**
  * 빌드를 시작하는 메서드
  *
  * @throws Exception
  */
 public void build() throws Exception {
  File file = new File(baseDirectory);
  if(!file.isAbsolute() || !file.isDirectory()) {
   throw new RuntimeException("절대경로가 아니거나 디렉토리가 아닙니다");
  }
  traceJSPs(file);

  if(logger.isInfoEnabled()) {
   logger.info("#### total " + compiledCount +
                        " file precompiled ####");
  }
 }

 /**
  * baseDirectory 이하에서 jsp확장자나 jspf를 가진 파일을 검색하고
  * 검색이 된경우 프리컴파일을 실행
  * 만약 디렉토리를 만나면 하위디렉토리를 모두 검색할때까지 재귀호출
  *
  * @param file
  * @throws Exception
  */
 private void traceJSPs(File file) throws Exception  {
  if(file.isFile()) {
   if(file.getName().endsWith(".jsp") ||
      file.getName().endsWith(".jspf")
     ) {
    precompile(file);
   }
  } else if(file.isDirectory()) {
   File[] sub = file.listFiles();
   for(int i=0; i<sub.length; i++) {
    traceJSPs(sub[i]);
   }
  }
 }
 
 /**
  * 타겟서버에 접속하여 프리컴파일 시도
  * urlStream등은 받지 않음
  * MalformedURLException이 발생하는 경우
  * log4j를 이용한 error comment(precompile은 계속 시도)
  *
  * @param file
  * @throws Exception
  */
 private void precompile(File file) throws Exception {
  if(logger.isDebugEnabled()) {
   logger.debug("found JSP : " + file.getAbsolutePath());
  }
  try {
   URL target = new URL("http", host, port, makeURLString(file));
   target.getContent();
  } catch (MalformedURLException e) {
   logger.error("error encounted : can't create obj ");
  }
  compiledCount++;
 }
 
 /**
  * URL object를 위한 URLString의 템플릿을 제공하는 메서드
  *
  * @param file
  * @return
  */
 private String makeURLString(File file) {
  StringBuffer buff = new StringBuffer();

  buff.append("/");
  if(!webmoduleName.equals("ROOT") || !webmoduleName.equals("/")) {
   buff.append(webmoduleName);
  }
  int matchPoint = 0;
  String fileName = file.getAbsolutePath().replace('\\', '/');
  matchPoint = fileName.indexOf(webmoduleName) + webmoduleName.length();
  buff.append(fileName.substring(matchPoint));
  buff.append(queryString);
 
  if(logger.isDebugEnabled()) {
   logger.debug("[" + fileName.indexOf(webmoduleName) +
                " / " + webmoduleName.length() + "]" + buff);
  }
  return buff.toString();
 }
 

 public static void main(String[] args) throws Exception {
  /*
   [usage] :
  String host  = "
www.jazzvm.net";
  String port = "80";
  String webmodulename = "jazzweb";
  String baseDirectory = "D:\\AntPublishHome\\upload";
  String baseDirectory = "E:\\eclipse_projects\\integrator";
  */
  String url           = "";
  int    port          = 0;
  String webmodulename = "";
  String baseDirectory = "";

  if(args.length != 4) {
   System.out.println("[usage] : java dns(or IP) port contextName " +
                      "contextDir(absolute dir)");
   System.out.println("example : java
www.jazzvm.net 80 jazzvm " +
                      "/usr/web/tomcat/webapps/jazzvm");
   try {
    throw new Exception("error encounted : Invalid argument");
   } catch(Exception e) {
    System.exit(0);
   }
  }
  url  = args[0];
  try {
   port = Integer.parseInt(args[1]);
  } catch(NumberFormatException e) {
   System.out.println("port number(2nd arg) must be int type value!!");
  }
  webmodulename = args[2];
  baseDirectory = args[3];

 
  PreCompiler instance =
    new PreCompiler(url, port, webmodulename, baseDirectory);
  instance.build();
 }
}

log4j.properties

log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p (%F:%L) - %m%n

 

 

출처

http://okjsp.pe.kr/bbs?act=VIEW&bbs=bbs4&seq=34227&pg=0&keyfield=subject&keyword=&pact=&password=

반응형