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 ) 332
Written by specular - ÀüÈ«¼º
3 of 3
 
<font face='±¼¸²'>4. HttpHandler and HandlerPool(Thread Pooling)  
     HttpWebServer¿¡¼­ client connectionÈÄ Handler thread¸¦ startÇÏÁö ¾Ê°í,
     HandlerPoolÀÇ execute(Socket sck) method¸¦ callÇßÁÒ. HandlerPool °´Ã¼´Â 
     HttpHandler¶ó´Â Thread°´Ã¼ÀÇ poolingÀÌ ±¸ÇöµÇ¾î ÀÖ½À´Ï´Ù.
  

  Thread pooling¿¡ ´ëÇÑ Çʿ伺À» À§Çؼ­ ¾ð±ÞÇß°í, ±¸Çö ¹æ¹ýÀº
  HandlerPoolÀÇ sourceº¸¸é µÇ°ÚÁÒ. 
package com.specular.http;

import java.util.*;
import java.net.*;
import com.specular.util.PropManager;
import com.specular.util.LogManager;

/*
*	HandlerPool.java - Thread Pooling
*	2001.05.17
* 	written by Jeon HongSeong
*/
public class HandlerPool 
{
private Vector pool;
//	Singleton
private static HandlerPool instance;
private ThreadGroup tGroup;

private static int HANDLER=5;
	
public HandlerPool() 
{
	pool = new Vector(1,3);
	tGroup = new ThreadGroup("HttWebServerHandler");
	init();
}

public static HandlerPool getInstance()
{
	//	Double checked Locking
	if(instance==null) {
		synchronized(HandlerPool.class) {
			if(instance==null) {
				instance=new HandlerPool();
			}
		}
	}
	return instance;
}

// 	initialize the pooling
private void init() {
	PropManager propMgr=PropManager.getInstance();
	try {
		HANDLER=Integer.parseInt(propMgr.getProperty("handler","5"));
	} catch(NumberFormatException e) {
		LogManager.log(e,"at the HandlerPool.init() method."+
			propMgr.getProperty("handler"));
		LogManager.log("The 'HANDLER' property is setted by 5.");	
	}
	
	for(int i=0;i<HANDLER;i++) 
	{
		HttpHandler newHandler=new HttpHandler();
		new Thread(tGroup,newHandler,"handler"+i).start();
		pool.addElement(newHandler);
	}
	LogManager.log("HandlerPool is initialized. - "+pool.size());
	//tGroup.list();
}

//	process the client request( notify a thread. )
public synchronized void execute(Socket sck) {
	if(pool.isEmpty()) {
		HttpHandler newHandler=new HttpHandler();
		newHandler.setSocket(sck);
		new Thread(tGroup,newHandler,"New Handler").start();
	} else {
		HttpHandler handler=(HttpHandler)pool.remove(pool.size()-1);
		handler.setSocket(sck);
	}	
}

public synchronized void release(HttpHandler handler) 
{
	if(pool.size()>=HANDLER) {
		LogManager.log("Too many threads, exit this thread.["+
			Thread.currentThread().getName()+"]");
		handler.stop();	
	} else {
		pool.addElement(handler);
	}
	//tGroup.list();
}	
}
¢º HandlerPool.java



À§ÀÇ ThreadPoolÀÇ source¸¸ ºÁ¼­´Â ½ÇÀç poolingÀÇ ¿ø¸®¸¦ ÀÌÇØ ÇÏÁö´Â ¸øÇÏ°ÚÁÒ. 
½ÇÁ¦ threadÀÇ wait, notify´Â HttpHandlerÀÇ setSocket(), run() method¿¡ ±¸ÇöµÇ¾î ÀÖ½À´Ï´Ù.
±×·¡¼­, client request½Ã¿¡ setSocket() method¿¡¼­ notify()¸¦ ½ÃÅ°°í, notifyµÈ thread´Â 
ÀϾ request¸¦ ó¸®ÇÏ°í, ´Ù½Ã wait() ÇÏ°Ô µÇ´Â °ÍÀÔ´Ï´Ù. 
±×¸®°í, HttpHandler´Â processReqeust() method¸¦ ÅëÇØ ½ÇÀç HttpRequest¸¦ ó¸®ÇÕ´Ï´Ù. 
package com.specular.http;

import java.io.*;
import java.net.*;
import com.specular.util.LogManager;
import com.specular.util.PropManager;

/*
*	HttpHandler.java - ClientÀÇ reqeust¸¦ ó¸®ÇÏ´Â thread
*	2001.05.17
* 	written by Jeon HongSeong
*/
public class HttpHandler implements Runnable,HttpConstants 
{
private Socket sck;
private static int BUFF_SIZE=1024*2;
private byte[] buff;

private static boolean SHUTTDOWN;
private static final byte[] EOL={(byte)'\r',(byte)'\n'};
private static final byte GET=(byte)0;
private static final byte HEAD=(byte)1;
private static final String[] methodStr={"GET","HEAD"};

public HttpHandler() 
{
	String buffer=PropManager.getInstance().getProperty("buffer.size","2048");
	try {
		BUFF_SIZE=Integer.parseInt(buffer);	
	} catch(NumberFormatException e) {
		LogManager.log(e,"at the HttpHandler constructor.");
		LogManager.log("The Response buffer.size is setted 2048K");
	}
	buff = new byte[BUFF_SIZE];			
}

// 	client request½Ã callµÊ. ==> notify a thread
public synchronized void setSocket(Socket sck) 
{
	this.sck = sck;
	notify();	
}
	
public synchronized void run() 
{
	SHUTTDOWN=true;
	while(SHUTTDOWN) 
	{
		if(sck==null) 
		{
			try {
				wait();
			} catch(InterruptedException e) {
				LogManager.log(e,"at the HttpHandler.run() method.");
				continue;	
			}					
		}
		try {
			processRequest();
		} catch(IOException e) {
			LogManager.log(e,"at the HttpHandler.run() method.");
			LogManager.log("For processing the request, It is occured a Exception.");
		}
		sck=null;
		//	clear the buffer
		for(int i=0;i<BUFF_SIZE;i++)	buff[i]=0;
	
		HandlerPool pool = HandlerPool.getInstance(); 
		pool.release(this);
	}
		
}

protected void processRequest() 
	throws IOException 
{
	InputStream in = sck.getInputStream();
	OutputStream out = sck.getOutputStream();
	BufferedOutputStream bos = new BufferedOutputStream(out);
	PrintStream ps = new PrintStream(bos);
	PropManager propMgr = PropManager.getInstance();
	//get timeout
	String timeout = propMgr.getProperty("timeout","5000");
	
	sck.setSoTimeout(Integer.parseInt(timeout));
	sck.setTcpNoDelay(true);
			
	int reqLength=0,r=0;
	try {
		/* 	HTTP GET/HEAD¸¸ Áö¿ø,
		 *	request message ù ¶óÀÎÀ» read.
		 */
outer:		while(reqLength<BUFF_SIZE) 
	{
		r = in.read(buff,reqLength,BUFF_SIZE-reqLength);
		if(r==-1) return;
		
		int i=reqLength;
		reqLength+=r;
		for(;i<reqLength;i++) 
		{
			if(buff[i]==(byte)'\n'||buff[i]==(byte)'\r')
				break outer;	
		}					
	}
	
	byte reqMethod;
	int index=0;
	if(buff[0]==(byte)'G'&&
	   buff[1]==(byte)'E'&&
	   buff[2]==(byte)'T'&&
	   buff[3]==(byte)' ') {
		reqMethod = GET;
		index=4;   	
	} else if(buff[0]=='H'&&
			  buff[1]=='E'&&
			  buff[2]=='A'&&
			  buff[3]=='D'&&
			  buff[4]==' ') {
		reqMethod=HEAD;
		index=5;			  	
	} else {
		//send 405 : Bad method type
		String[] msg = {"unsupported method type:",new String(buff,0,5)};
		sendHeader(ps,HTTP_BAD_METHOD,msg[0]);
		sendError(ps,HTTP_BAD_METHOD,msg);
		return;	
	}
	
	/*	get request file name
	 */
	int i=0;
	for(i=index;i<reqLength;i++) {
		if(buff[i]==(byte)' ') {
			break;	
		}
	}
	
	String fname = new String(buff,index,i-index);
	fname=fname.replace('/',File.separatorChar);
	if(fname.startsWith(File.separator)) {
		fname=fname.substring(1);	
	}
	String docRoot = 
	  propMgr.getProperty("DocumentRoot",System.getProperty("user.dir"));
	File target = new File(docRoot,fname);
	
	//target resource : DocumentRootÀÏ °æ¿ì
	if(target.isDirectory()) {
		File welcome = new File(target,"index.html");
		if(welcome.exists()) {
			target=welcome;	
		}	
	} 
	if( !target.exists() && !fname.equals("") ) {
		// 404 : NotFound 
		sendHeader(ps,HTTP_NOT_FOUND,"");
		return;
	}
	LogManager.log("From "+sck.getInetAddress().getHostAddress()+": "+
			methodStr[reqMethod]+" "+target.getAbsolutePath());
	
	switch(reqMethod) 
	{
	case GET : if( target.isDirectory() ) 
		   {
			   sendHeader(ps, HTTP_OK,"OK",target);
			   sendDirList(ps,target);
		   } else if(target.isFile()) {
			   sendHeader(ps, HTTP_OK,"OK",target);
			   sendFile(ps,target);
		   }
		break;
	case HEAD : 
		
		break;
	}
	
	LogManager.log("From "+sck.getInetAddress().getHostAddress()+": "+
			methodStr[reqMethod]+" "+target.getAbsolutePath()+"-->"+HTTP_OK);
} finally {
	if(ps!=null) {
		ps.flush();
		ps.close();	
	}
	if(bos!=null) bos.close();
	if(in!=null) in.close();
	if(out!=null) out.close();
	if(sck!=null) sck.close();
}
	
}

protected void sendHeader(PrintStream ps,int resCode,String msg) 
	throws IOException 
{		
	ps.print("HTTP/1.0 "+resCode+" "+msg);
	ps.write(EOL);
	ps.print("Date: "+new java.util.Date());
	ps.write(EOL);
	ps.print("Server: HttpWebServer 1.0");
	ps.write(EOL);
}

protected void sendHeader(PrintStream ps, int resCode, String msg, File target) 
	throws IOException 
{
	sendHeader(ps,resCode,msg);
	if(target.isDirectory()) {
		ps.print("Content-type: text/html");
		ps.write(EOL);	
	} else {
		ps.print("Content-length: "+target.length());
		ps.write(EOL);
		ps.print("Last Modified: "+new java.util.Date(target.lastModified()));
		ps.write(EOL);
		String fName=target.getName();
		int idx=fName.lastIndexOf('.');
		String mime=null;
		if(idx>0) mime = (String)MimeDB.get(fName.substring(idx));
		if(mime==null) mime="unknown/unknown";
		ps.print("Content-type: "+mime);
		ps.write(EOL);
	}	
}

protected void sendError(PrintStream ps, int resCode, String[] msg) 
	throws IOException 
{
	ps.print("Content-type: text/html");
	ps.write(EOL);
	ps.write(EOL);
	ps.print(resCode + " : "+msg[0]);
	for(int i=1;i<msg.length;i++) {
		ps.write(EOL);
		ps.print(msg[i]);	
	}
}

protected void sendFile(PrintStream ps, File target) 
	throws IOException 
{		
	ps.write(EOL);
	InputStream is = new FileInputStream(target.getAbsolutePath());

	try {
		int n;
		while ((n = is.read(buff)) > 0) {
			ps.write(buff, 0, n);
		}
	} finally {
		is.close();
	}
}
protected void sendDirList(PrintStream ps,File dir) 
	throws IOException 
{
	PropManager propMgr = PropManager.getInstance();
	String docRoot = propMgr.getProperty("DocumentRoot",System.getProperty("user.dir"));
	String path = dir.getPath();
	String viewPath=path.substring(docRoot.length()).replace(File.separatorChar,'/');

	ps.println("<TITLE>Directory listing</TITLE><P>\n");
	ps.println("<body>");
	ps.println("<A HREF=\"..\">Parent Directory</A><BR> \n");
	ps.println("<TABLE BORDER=\"0\">");
	ps.println("<TR><TD><u>File Name</u></TD>");
	ps.println("<TD><u>Size</u></TD>");
	ps.println("<TD><u>Last Modified</u></TD></TR>");
	File[] list = dir.listFiles();
	for (int i = 0; list != null && i < list.length; i++) {
	ps.println("<TR><TD width=40%>");
	if (list[i].isDirectory()) {
		ps.println("<li><A HREF=\""+viewPath+"/"+list[i].getName()+"/\">");
		ps.println("<font size=-1>"+list[i].getName()+
				"/</font></A><BR>");
	} else {
		ps.println("<li><A HREF=\""+viewPath+"/"+list[i].getName()+"\">");
		ps.println("<font size=-1>"+list[i].getName()+
				"</font></A><BR>");
	}
	ps.println("</TD><TD width=15%><font size=-1>"+
				list[i].length()+"</font></TD>");
	ps.println("<TD width=45%><font size=-1>"+
				new java.util.Date(list[i].lastModified()));
	ps.println("</font></TD></TR>");
	}
	ps.println("</TABLE>");
ps.println("<P><HR><BR><I>" + (new java.util.Date()) + 
				"</I></body>");
}

public void stop() 
{
	SHUTTDOWN=false;	
}	
}
¢º HttpHandler.java
¢º HttpConstants.java




ÀÌ·¸°Ô Çؼ­ Web Server¿¡ ´ëÇÑ ÇÁ·Î±×·¥À» ´Ù ¼Ò°³¸¦ Ç߳׿ä.
Network Server programming ÀÇ ÀÌÇØ¿¡ µµ¿òÀÌ µÇ¾ú´ÂÁö ¸ð¸£°Ú³×¿ä.

¢º Source Code


2001.05.18 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