Site Search :
Standard Enterprise XML Methodology Pattern Setting Tunning Other
Article Contributors
GuestBook
Javapattern Maven
XSourceGen Dev
JetSpeed Test
JLook Image
jLook Family Site


Making the Http WebServer(Java로 web server를 구현하는 방법)
 
Network, Stream I/O, Multi-Thread Programming 이해
Server Application 구조, Thread Pooling, Logging, Dynamic Configuration 기능 이해 ( 2003/02/28 ) 330
Written by specular - 전홍성
1 of 3
 
<font face='굴림'>※ Web Server 만들기  
Networking, Multi-thread programming을 이해하기 위해 Java로 WebServer를 구현한 
program을 소개하고자 합니다.
Server application을 구현하기 위해 Network, Stream I/O, Thread알고 있어야하고, 
그리고, Server의 동작 구조에 대해 이해를 하고 있어야 합니다. 여기서 소개하고자 
하는 WebServer에서는 Dynamic configuration기능, Logging, Thread Pooling의 기능을 
구현해 놓았고, Server programming에 대한 이해와 함께 이런 기능을 응용하는
방법을 이해하면 될것 같습니다.  

1. Dynamic Configuration  
     Dynamic configuration이라는 의미는 configuration의 값들의 hard coding을 피해서
     runtime에 결정하자는 뜻입니다. 이런 기능의 예로는 Applet의 param tag, Servlet의 
     InitParameter, EJB의 Environment Entry등이 있습니다. 
     즉 변경될 가능성이 있는 값들을 hard coding하게 되면, 값이 변경될때 마다 
     재 compile해야 한다는 번거러움이 있죠. 그래서, configuration file에 name/value값들을 
     설정하고, programming할때 name의 값에 해당하는 value값을 읽어 변수에 저장해 
     사용하게 되면, 값이 변경될때 만다 재 compile하는 번거러움은 없어지는 것이죠.
     그리고, runtime에 값의 변경을 지금 실행 중인 프로세스에 반영할수도 있겠죠.
  web server에서는 httpd.conf file에 web server의 configuration을 다음과 같이 
  설정해 놓았습니다. 
	## HttpWebServer Configuration file : httpd.conf
	DocumentRoot=d:
	handler=5
	timeout=10000
	port=8080
	verbose=true
	buffer.size=2048
	mime.file=mime.txt
			 ▶ httpd.conf 

 
     httpd.conf file의 내용을 읽어 program에서 이 값들을 access하기 위한 객체를 
     정의 해야 하는데, 이것을 정의할때 java.util.Properties 객체를 이용하면 되겠죠.
     Properties객체의 이용은 article "Properties class and properties file Example"을 참고하세요.
     그리고, 아래에 PropManager는 httpd.conf의 configuration을 읽어 Properties객체에 
     저장하는 것과 runtime에 configuration에 대한 service를 해 주는 객체입니다. 
     load() method에서 httpd.conf file의 내용을 읽어 Properties객체에 저장을 하는데, 
     PROPERTIES_FILE의 httpd.conf file은 CLASSPATH경로 위치에서 찾게 됩니다. 
package com.specular.util;

import java.util.*;
import java.io.*;

/*
*	PropManager.java : Configuration Properties management. 
*	2001.05.15
* 	written by Jeon HongSeong
*/
public class PropManager {
//Singleton
private static PropManager instance;
private Properties prop; 
private static String PROPERTIES_FILE="httpd.conf";

static 
{
	instance=new PropManager();
}

private PropManager() 
{
	prop = new Properties();
	try {
		load();
	} catch(IOException e) {
		e.printStackTrace();
	} 
}

public static PropManager getInstance() 
{
	if(instance==null) {
		instance=new PropManager();
	}
	return instance;
}

public synchronized void load() 
	throws IOException 
{
	InputStreamReader isr=null;
	BufferedReader br=null;
	ClassLoader cl = getClass().getClassLoader();
	InputStream in= cl.getResourceAsStream(PROPERTIES_FILE);
	if(in==null) {
		throw new FileNotFoundException("The configuration file is not found.");
	}
	
	try {
		isr = new InputStreamReader(in);
		br = new BufferedReader(isr);
		String line=null;
		while(true) {
			line=br.readLine();
			if(line==null) break;
			if(line.startsWith("#")) continue;
			parseLine(line);
		}			
	} catch(IOException e) { 
		throw e;
	} finally {
		try {
			if(br!=null) br.close();
			if(isr!=null) isr.close();
			if(in!=null) in.close();
		} catch(IOException e) { e.printStackTrace(); }	
	}
}

public synchronized void store(String header) 
	throws IOException 
{
	FileOutputStream fo = null;
	OutputStreamWriter osw = null;
	BufferedWriter awriter = null;
	try {
		fo = new FileOutputStream(PROPERTIES_FILE);
		osw = new OutputStreamWriter(fo);
		awriter = new BufferedWriter(osw);
		if (header != null)	
			writeln(awriter, "# " + header);
		writeln(awriter, "# " + new Date().toString());
	
		Enumeration e = prop.keys();
		while(e.hasMoreElements()) {
			String key = (String)e.nextElement();
			String val = (String)prop.get(key);
			writeln(awriter, key + "=" + val);
		}
		awriter.flush();
	} catch(IOException e) {
		throw e;	
	} finally {
		try {
			if(awriter!=null) awriter.close();
			if(osw!=null) osw.close();
			if(fo!=null) fo.close();
		} catch(IOException e) { e.printStackTrace(); }
	}
}

public String getProperty(String name) 
{
	return prop.getProperty(name);
}

public String getProperty(String name,String defaultValue) 
{
	return prop.getProperty(name,defaultValue);	
}

public void setProperty(String name,String value) 
{
	prop.setProperty(name,value);	
}

public Enumeration propertyNames() 
{
	return prop.propertyNames();	
}

public void list() {
	System.out.println("--"+PROPERTIES_FILE+" configuration --");
	prop.list(System.out);
	System.out.println("------------------------");
}

private void parseLine(String line) 
{
	if(line.indexOf("=") != -1 ) {
		int idx=line.indexOf("=");
		prop.put(line.substring(0,idx),line.substring(idx+1));
	}
}

private void writeln(BufferedWriter bw, String s) 
	throws IOException 
{
		bw.write(s);
		bw.newLine();
}
}
▶ PropManager.java 
//PropManager Test Application
import com.specular.util.*;
import java.io.*;

public class PropMgrEx {
public static void main(String args[]) 
	throws IOException 
{
	PropManager pMgr = PropManager.getInstance("httpd.conf");
	pMgr.list();
	System.out.println(pMgr.getProperty("DocumentRoot"));
}
}
▶ PropMgrEx.java  
 
   

  다음은 web server에서 이용하게 될 MIME Type에 대한 data를 이용하기 위해 
  구현한 코드를 보겠습니다. MIME Type도 configuration과 유사하게 이용될수 있지만, 
  되게 read only의 성격의 data로 볼수가 있습니다. 그래서 PropManager를 이용하기 보다는
read only의 성격을 갖고있는 class를 따로 정의 했죠.
먼저 mime.txt file의 내용입니다. 
	# MIME TYPE : mime.txt file
	.uu=application/octet-stream
	.exe=application/octet-stream
	.ps=application/postscript
	.zip=application/zip
	.sh=application/x-shar
	.tar=application/x-tar
	.snd=audio/basic
	.au=audio/basic
	.wav=audio/x-wav
	.gif=image/gif
	.jpg=image/jpeg
	.jpeg=image/jpeg
	.htm=text/html
	.html=text/html
	.text=text/plain
	.c=text/plain
	.cc=text/plain
	.c++=text/plain
	.h=text/plain
	.pl=text/plain
	.txt=text/plain
	.java=text/plain
 ▶ mime.txt 

  mime.txt file의 내용을 loading해서 runtime에 MIME TYPE에 대해 service해 주는 class입니다. 
package com.specular.http;

import java.io.*;
import java.util.*;
import com.specular.util.*;

/*
 *	MimeDB.java : MIME TYPE data loading,service 
 *	2001.05.15
 * 	written by Jeon HongSeong
 */
public class MimeDB implements Serializable {
private static Properties db;

static 
{
	db = new Properties();
	String mimeFile=PropManager.getInstance().getProperty("mime.file","mime.txt");
	ClassLoader cl = MimeDB.class.getClassLoader();
	InputStream is= cl.getResourceAsStream(mimeFile);
	
	try {
		if(is==null) 
			throw new FileNotFoundException("The configuration file is not found.");
		
		db.load(is);
	} catch(IOException e) {
		LogManager.log(e,"MIME Configuration file is not loaded.");
	} finally {
		try {
			if(is!=null) is.close();
		} catch(IOException e) { e.printStackTrace(); }
	}
}

private MimeDB() {}

public static String get(String name) 
{
	return db.getProperty(name,"unknown/unknown");
}

private static void set(String name,String value) 
{
	if(db!=null) db.setProperty(name,value);
} 

public static void list() {
	System.out.println("-- mime.txt configuration --");
	db.list(System.out);
	System.out.println("-------------------------");	
}
}
 ▶ MimeDB.java 

  위의 PropManager는 configuration peroperties를 dynamic하게 반영하는 것은 구현되어 
  있지 않은데, 이것은 Thread를 이용해 configuration file의 modified time을 주기적으로
  check해서 loading하게끔 구현하면, 되겠죠.   
  
2001.05.17 written by Jeon HongSeong 
 
1 2 3
References
 
This article Source
webserver.jar
Copyright ⓒ 2003 www.javapattern.info & www.jlook.com, an jLOOK co.,LTD