Page History
...
Javaクラスは様々なリソースのパスに基づき実行されます。仮想ホストのスクリプト要素はJavaクラスと特定のリソース・パスを関連付ける際に利用されます。trace属性はHTTPトランザクションのJSMトレースを可能にします。
httpd.xml
...
| Code Block |
|---|
カスタムの Java クラスを書いて、HTTP サーバーからの HTTP 要求を処理するには、Java クラスで com.lansa.jsm.JSMHTTPService のインターフェースが実装されていなければなりません。
JSMHTTPService interface
...
| Code Block |
|---|
public interface JSMHTTPService { public void doRequest ( JSMTrace trace, JSMHTTPVirtual virtual, JSMHTTPContext context, JSMHTTPTransport transport, JSMHTTPRequest request ) ; } |
JSMHTTPVirtual public methods
| Code Block |
|---|
String getHost () |
...
boolean isActive () ; |
...
File getDocumentRoot () |
...
File getDocumentIndex () |
...
File getFile ( String path ) |
...
String getContentType ( File file |
...
) void logException ( JSMHTTPTransport transport, Throwable t |
...
) void logError ( JSMHTTPTransport transport, JSMHTTPRequest request, String message ) |
JSMHTTPContext public methods
| Code Block |
|---|
HashMap getServiceParameters () |
...
JSMHTTPHost[] getServiceHosts () |
JSMHTTPHost public methods
| Code Block |
|---|
String getName () |
...
HashMap getParameters () |
JSMHTTPTransport public methodsmethods
| Code Block |
|---|
int getId () |
...
Socket getSocket () |
...
boolean isSecure () |
...
String getClientAddress () |
...
InetAddress getInetAddress () |
...
InputStream getInputStream () |
...
OutputStream getOutputStream () |
...
void consumeInputStream ( JSMHTTPRequest request ) |
...
byte[] readInputStream ( int length ) |
...
void sendNotFound ( String message ) |
...
void sendForbidden ( String message ) |
...
void sendNotImplemented ( String message ) |
JSMHTTPRequest public methods
| Code Block |
|---|
String getHead () |
...
String getMethod () |
...
String getVersion () |
...
String getResourceRaw () |
...
String getResourcePath () |
...
Properties getProperties () |
...
String getProperty ( String key ) |
...
JSMHTTPNameValue[] getQueryNameValues () |
...
String getHost () |
...
long getContentLength () |
...
boolean canAcceptGZIP () |
...
String getUserAgent () |
...
String getUserAgentVersion () |
...
boolean isUserAgent ( String agent ) |
...
boolean isUserAgentIE6 () |
JSMHTTPNameValue public methods
| Code Block |
|---|
String getName () |
...
String getValue () |
次の Java クラスはスタティックなファイル要求を取り扱う JSM HTTP サーバークラスです。
例
| Code Block |
|---|
package com.acme.service ; |
...
...
import java.io.* ; |
...
...
import java.util.Date ; |
...
import java.util.HashMap ; |
...
...
import java.util.zip.GZIPInputStream ; |
...
...
import com.lansa.jsm.* ; |
...
...
public final class Example implements JSMHTTPService |
...
{ |
...
private final static String CRLF = "\r\n" ; |
...
private final static String EMPTY_STRING = "" ; |
...
private final static String ENCODING_UTF8 = "UTF-8" ; |
...
...
private JSMTrace m_trace = null ; |
...
private JSMHTTPRequest m_request = null ; |
...
private JSMHTTPVirtual m_virtual = null ; |
...
private JSMHTTPTransport m_transport = null ; |
...
...
/* |
...
RFC2616 - ハイパーテキスト転送プロトコル - HTTP/1.1 |
...
*/ |
...
...
private HashMap m_serviceParameters = null ; // Not synchronized |
...
...
public Example () |
...
{ |
...
} |
...
...
public final void doRequest ( JSMTrace trace, |
...
JSMHTTPVirtual virtual, |
...
JSMHTTPContext context, |
...
JSMHTTPTransport transport, |
...
JSMHTTPRequest request ) |
...
{ |
...
try |
...
{ |
...
m_trace = trace ; |
...
...
m_virtual = virtual ; |
...
...
m_request = request ; |
...
...
m_transport = transport ; |
...
...
m_serviceParameters = context.getServiceParameters () ; |
...
...
handleRequest () ; |
...
} |
...
catch ( Throwable t ) |
...
{ |
...
/* |
...
例外のログ |
...
*/ |
...
...
m_virtual.logException ( m_transport, t ) ; |
...
...
if ( m_trace == null ) |
...
{ |
...
System.out.println ( "JSMHTTPServiceFile: handle request exception: " + t.getMessage () ) ; |
...
...
t.printStackTrace () ; |
...
} |
...
else |
...
{ |
...
m_trace.print ( t ) ; |
...
} |
...
} |
...
} |
...
...
private final void handleRequest () throws IOException |
...
{ |
...
if ( m_trace != null ) |
...
{ |
...
m_trace.println ( "Handle request for resource path: ", m_request.getResourcePath () ) ; |
...
} |
...
...
/* |
...
GET と HEAD メソッドに対し、要求コンテンツは期待されない |
...
POST が使われた可能性もある |
...
ソケット入力ストリームのコンテンツはすべて使う必要あり |
...
これにより、ブラウザは切り替えて HTTP 応答を読み込むことができる |
...
*/ |
...
...
m_transport.consumeInputStream ( m_request.getContentLength () ) ; |
...
...
/* |
...
メソッド確認 |
...
*/ |
...
...
if ( !isAllowedMethod ( m_request.getMethod () ) ) |
...
{ |
...
m_virtual.logError ( m_transport, m_request, "Method is not implemented" ) ; |
...
m_transport.sendNotImplemented ( m_request.getMethod () ) ; |
...
return ; |
...
} |
...
...
/* |
...
ファイル取得 |
...
*/ |
...
...
String path = m_request.getResourcePath () ; |
...
...
File file = m_virtual.getFile ( path ) ; |
...
...
if ( file == null ) |
...
{ |
...
if ( m_trace != null ) |
...
{ |
...
m_trace.println ( "File not found" ) ; |
...
} |
...
...
m_virtual.logError ( m_transport, m_request, "File not found" ) ; |
...
m_transport.sendNotFound ( path ) ; |
...
return ; |
...
} |
...
...
/* |
...
ファイルを検知 |
...
*/ |
...
...
if ( file.isDirectory () ) |
...
{ |
...
if ( m_trace != null ) |
...
{ |
...
m_trace.println ( "File is a directory: ", file.getAbsolutePath () ) ; |
...
} |
...
...
m_virtual.logError ( m_transport, m_request, "File is a directory" ) ; |
...
...
m_transport.sendNotFound ( path ) ; |
...
...
return ; |
...
} |
...
...
if ( m_request.getMethod().equals ( "HEAD" ) ) |
...
{ |
...
/* |
...
HEAD ファイル |
...
*/ |
...
...
sendHEAD ( file ) ; |
...
...
return ; |
...
} |
...
...
/* |
...
GET ファイル |
...
*/ |
...
...
sendFile ( file ) ; |
...
...
/* |
...
ワン・ショット・ディレクトリ・ファイルの削除 |
...
*/ |
...
...
if ( file.getParentFile().getName().equals ( "one-shot" ) ) |
...
{ |
...
if ( !file.delete () ) |
...
{ |
...
if ( m_trace != null ) |
...
{ |
...
m_trace.println ( "Cannot delete one-shot file: ", file.getAbsolutePath () ) ; |
...
} |
...
...
m_virtual.logError ( m_transport, m_request, "Cannot delete one-shot file" ) ; |
...
} |
...
} |
...
...
} |
...
...
private final void sendHEAD ( File sendFile ) |
...
{ |
...
/* |
...
コンテンツが送信されない場合を除き、HEAD 応答は GET 応答と同じ |
...
ファイルのコンテンツ長は含まれるが、コンテンツは無し |
...
*/ |
...
...
try |
...
{ |
...
if ( m_trace != null ) |
...
{ |
...
m_trace.println ( "Send HEAD response: ", sendFile.getCanonicalPath () ) ; |
...
} |
...
...
/* |
...
プロトコルの作成 |
...
RFC2616 - HTTP/1.1 |
...
サーバーが応答送信の直後に接続を閉じる選択をした場合 |
...
close トークンを含む接続ヘッダーを送信する必要がある |
...
*/ |
...
...
long contentLength = sendFile.length () ; |
...
...
boolean isCompressed = JSMHTTPHelper.isCompressed ( sendFile ) ; |
...
...
if ( isCompressed && !canAcceptCompressed () ) |
...
{ |
...
isCompressed = false ; |
...
...
contentLength = JSMHTTPHelper.getUncompressedContentLength ( sendFile ) ; |
...
} |
...
...
String contentType = m_virtual.getContentType ( sendFile ) ; |
...
...
StringBuffer response = new StringBuffer ( 512 ) ; |
...
...
response.append ( "HTTP/1.1 200 OK" ) ; |
...
response.append ( CRLF ) ; |
...
...
response.append ( "Date: " ) ; |
...
response.append ( JSMDateTime.getFormattedHTTPDate ( new Date () ) ) ; |
...
response.append ( CRLF ) ; |
...
...
response.append ( "Content-Type: " ) ; |
...
response.append ( contentType ) ; |
...
response.append ( CRLF ) ; |
...
...
response.append ( "Content-Length: " ) ; |
...
response.append ( Long.toString ( contentLength ) ) ; |
...
response.append ( CRLF ) ; |
...
...
if ( isCompressed ) |
...
{ |
...
response.append ( "Content-Encoding: gzip" ) ; |
...
response.append ( CRLF ) ; |
...
} |
...
...
/* |
...
応答日時は作業するキャッシングのために必須 |
...
*/ |
...
...
int cacheAge = getCacheAge ( contentType, sendFile ) ; |
...
if ( cacheAge <= 0 ) |
...
{ |
...
response.append ( "Cache-Control: max-age=0, s-maxage=0, must-revalidate, proxy-revalidate, no-cache" ) ; |
...
response.append ( CRLF ) ; |
...
} |
...
...
if ( JSMHTTPHelper.isTextPlain ( contentType ) ) |
...
{ |
...
/* |
...
IE8 のコンテンツ・スニッフィング行為を停止 |
...
*/ |
...
...
response.append ( "X-Content-Type-Options: nosniff" ) ; |
...
response.append ( CRLF ) ; |
...
} |
...
...
response.append ( "Connection: close" ) ; |
...
response.append ( CRLF ) ; |
...
...
response.append ( CRLF ) ; |
...
...
byte[] protocol = response.toString().getBytes ( ENCODING_UTF8 ) ; |
...
...
if ( m_trace != null ) |
...
{ |
...
File file = m_trace.createTraceFile ( "HTTP_PROTOCOL_RESPONSE.TXT" ) ; |
...
...
JSMHTTPHelper.outputToFile ( file, protocol ) ; |
...
} |
...
...
/* |
...
応答の送信 |
...
クライアント・ソケットが閉じている場合、パイプ破壊の例外が発生 |
...
*/ |
...
...
OutputStream outputStream = m_transport.getOutputStream () ; |
...
...
outputStream.write ( protocol ) ; |
...
...
outputStream.flush () ; |
...
} |
...
catch ( IOException e ) |
...
{ |
...
/* |
...
ユーザーは、すべてのコンテンツが送信される前にブラウザを閉じることができる |
...
*/ |
...
...
if ( m_trace != null ) |
...
{ |
...
m_trace.println ( "Error sending HEAD response" ) ; |
...
} |
...
...
m_virtual.logError ( m_transport, m_request, "Error sending HEAD response" ) ; |
...
} |
...
} |
...
...
private final void sendFile ( File sendFile ) |
...
{ |
...
InputStream inputStream = null ; |
...
...
try |
...
{ |
...
if ( m_trace != null ) |
...
{ |
...
m_trace.println ( "Send file response: ", sendFile.getCanonicalPath () ) ; |
...
} |
...
...
/* |
...
プロトコルの作成 |
...
RFC2616 - HTTP/1.1 |
...
サーバーが応答送信の直後に接続を閉じる選択をした場合 |
...
close トークンを含む接続ヘッダーを送信する必要がある |
...
*/ |
...
...
boolean sendChunked = false ; |
...
...
boolean uncompressContent = false ; |
...
...
long contentLength = sendFile.length () ; |
...
...
boolean isCompressed = JSMHTTPHelper.isCompressed ( sendFile ) ; |
...
...
if ( isCompressed && !canAcceptCompressed () ) |
...
{ |
...
isCompressed = false ; |
...
...
uncompressContent = true ; |
...
...
contentLength = JSMHTTPHelper.getUncompressedContentLength ( sendFile ) ; |
...
} |
...
...
String contentType = m_virtual.getContentType ( sendFile ) ; |
...
...
StringBuffer response = new StringBuffer ( 512 ) ; |
...
...
response.append ( "HTTP/1.1 200 OK" ) ; |
...
response.append ( CRLF ) ; |
...
...
response.append ( "Date: " ) ; |
...
response.append ( JSMDateTime.getFormattedHTTPDate ( new Date () ) ) ; |
...
response.append ( CRLF ) ; |
...
...
response.append ( "Content-Type: " ) ; |
...
response.append ( contentType ) ; |
...
response.append ( CRLF ) ; |
...
...
if ( sendChunked ) |
...
{ |
...
response.append ( "Transfer-Encoding: chunked" ) ; |
...
response.append ( CRLF ) ; |
...
} |
...
else |
...
{ |
...
response.append ( "Content-Length: " ) ; |
...
response.append ( Long.toString ( contentLength ) ) ; |
...
response.append ( CRLF ) ; |
...
} |
...
...
if ( isCompressed ) |
...
{ |
...
response.append ( "Content-Encoding: gzip" ) ; |
...
response.append ( CRLF ) ; |
...
} |
...
...
/* |
...
応答日時は作業するキャッシングのために必須 |
...
*/ |
...
...
int cacheAge = getCacheAge ( contentType, sendFile ) ; |
...
if ( cacheAge <= 0 ) |
...
{ |
...
response.append ( "Cache-Control: max-age=0, s-maxage=0, must-revalidate, proxy-revalidate, no-cache" ) ; |
...
response.append ( CRLF ) ; |
...
} |
...
else |
...
{ |
...
response.append ( "Cache-Control: " ) ; |
...
response.append ( "max-age=" ) ; |
...
response.append ( Integer.toString ( cacheAge ) ) ; |
...
response.append ( ", s-maxage=" ) ; |
...
response.append ( Integer.toString ( cacheAge ) ) ; |
...
response.append ( CRLF ) ; |
...
} |
...
...
if ( JSMHTTPHelper.isTextPlain ( contentType ) ) |
...
{ |
...
/* |
...
IE8 のコンテンツ・スニッフィング行為を停止 |
...
*/ |
...
...
response.append ( "X-Content-Type-Options: nosniff" ) ; |
...
response.append ( CRLF ) ; |
...
} |
...
...
response.append ( "Connection: close" ) ; |
...
response.append ( CRLF ) ; |
...
...
response.append ( CRLF ) ; |
...
...
byte[] protocol = response.toString().getBytes ( ENCODING_UTF8 ) ; |
...
...
if ( m_trace != null ) |
...
{ |
...
File file = m_trace.createTraceFile ( "HTTP_PROTOCOL_RESPONSE.TXT" ) ; |
...
...
JSMHTTPHelper.outputToFile ( file, protocol ) ; |
...
} |
...
...
/* |
...
応答の送信 |
...
クライアント・ソケットが閉じている場合、パイプ破壊の例外が発生 |
...
*/ |
...
...
OutputStream outputStream = m_transport.getOutputStream () ; |
...
...
outputStream.write ( protocol ) ; |
...
...
/* |
...
ファイル・コンテンツ送信 |
...
*/ |
...
...
if ( uncompressContent ) |
...
{ |
...
if ( m_trace != null ) |
...
{ |
...
m_trace.println ( "Uncompress content" ) ; |
...
} |
...
...
inputStream = new GZIPInputStream ( new FileInputStream ( sendFile ), 16384 ) ; |
...
} |
...
else |
...
{ |
...
inputStream = new FileInputStream ( sendFile ) ; |
...
} |
...
...
if ( sendChunked ) |
...
{ |
...
JSMHTTPHelper.sendChunked ( inputStream, outputStream ) ; |
...
} |
...
else |
...
{ |
...
JSMHTTPHelper.sendStream ( inputStream, outputStream ) ; |
...
} |
...
inputStream.close () ; |
...
outputStream.flush () ; |
...
} |
...
catch ( IOException e ) |
...
{ |
...
/* |
...
ユーザーは、すべてのコンテンツが送信される前にブラウザを閉じることができる |
...
*/ |
...
...
if ( inputStream != null ) |
...
{ |
...
try |
...
{ |
...
inputStream.close () ; |
...
} |
...
catch ( Exception e2 ) |
...
{ |
...
} |
...
} |
...
...
if ( m_trace != null ) |
...
{ |
...
m_trace.println ( "Error sending file response" ) ; |
...
} |
...
m_virtual.logError ( m_transport, m_request, "Error sending file response" ) ; |
...
} |
...
} |
...
...
private final |
...
boolean isAllowedMethod ( String method ) |
...
{ |
...
/* |
...
標準 HTTP メソッド |
...
...
GET |
...
PUT |
...
POST |
...
HEAD |
...
TRACE |
...
DELETE |
...
OPTIONS |
...
CONNECT |
...
*/ |
...
...
if ( method.equals ( "GET" ) ) |
...
{ |
...
return true ; |
...
} |
...
...
if ( method.equals ( "HEAD" ) ) |
...
{ |
...
return true ; |
...
} |
...
...
return false ; |
...
} |
...
...
private final boolean canAcceptCompressed () |
...
{ |
...
if ( m_request.canAcceptGZIP () ) |
...
{ |
...
return true ; |
...
} |
...
...
return false ; |
...
} |
...
...
private final int getCacheAge ( String contentType, File sendFile ) |
...
{ |
...
int cacheAge = getCacheAge () ; |
...
...
if ( JSMHTTPHelper.isImage ( contentType ) ) |
...
{ |
...
return getCacheAgeImage () ; |
...
} |
...
...
if ( JSMHTTPHelper.isPDF ( contentType ) ) |
...
{ |
...
return getCacheAgePDF () ; |
...
} |
...
return cacheAge ; |
...
} |
...
...
private final int getCacheAge () |
...
{ |
...
return getServiceParameterInteger ( "CACHE.MAXAGE" ) ; |
...
} |
...
private final int getCacheAgePDF () |
...
{ |
...
/* |
...
キャッシュが 0 の場合、IE は pdf コンテンツを Adobe に渡さない |
...
...
サンプル・ブラウザ URI /axes/dbmhelp.pdf |
...
*/ |
...
...
return getServiceParameterInteger ( "CACHE.MAXAGE.PDF" ) ; |
...
} |
...
...
private final int getCacheAgeImage () |
...
{ |
...
/* |
...
YUI/IE イメージ・キャッシング |
...
IE はイメージに対する要求を頻繁に行っている |
...
ブラウザにイメージをキャッシュするよう伝える、デフォルトはキャッシュ無し |
...
*/ |
...
...
return getServiceParameterInteger ( "CACHE.MAXAGE.IMAGE" ) ; |
...
} |
...
...
private final int getServiceParameterInteger ( String property ) |
...
{ |
...
String value = (String)m_serviceParameters.get ( property ) ; |
...
...
if ( value == null ) |
...
{ |
...
return 0 ; |
...
} |
...
...
if ( value.equals ( EMPTY_STRING ) ) |
...
{ |
...
return 0 ; |
...
} |
...
...
return Integer.parseInt ( value ) ; |
...
} |
...
} |