Javaデモ/Servlet「チャット:SPA(Single Page Application)」:「ファイル入出力、ファイル・ロック、AJAX、タイマー」:「RandomAccessFile、FileChannel、Charset、ByteBuffer」(Ver.5)
開発環境はEclipseです。
Eclipseにおいてサーバー・サイド用Javaプロジェクトを作る場合は「動的Web」でプロジェクトを作る必要が有ります。
「動的Web」プロジェクト作成時には、必ず「web.xmlデプロイメント記述子の生成」チェックボックスをチェックしてして下さい(下記参照)。
Webアプリ向け開発手順
http://www.javaroad.jp/opensource/js_eclipse6.htm
現在、文字エンコードは多数ありますが、「UTF-8、Shift_JIS」の2つがインターネットでのデファクト・スタンダードですが、Windowsの場合はShift_JISとは微妙に違うMS932(CP932)がデファクト・スタンダードになります(なのでWindowsにおいてShift_JISと言う場合は、基本的にMS932(CP932)を意味します)。
デモなのでイロイロ手抜きをしています。
レスポンスの遅延を想定して、タイマー処理はsetInterval()では無く、setTimeout()にする、(レスポンスの遅延を想定して)レスポンスが来てからsetTimeout()を設定する。
つまりsetTimeout()を設定する場所はどこでも良いと言う訳では無い。
サーバー・サイドの場合、多数のユーザーが同時にアクセスした場合を考慮する必要があります。
具体的にはサーバー・サイドの場合は、ファイル出力時にファイルをロックしないとデータ破壊される危険性があります。
また ファイルの書き換え(ファイル入力してファイル出力する)は、ファイル入出力を一体としてロックしないとデータ破壊される危険性があります、つまり ファイル入力とファイル出力でファイル・ロックを2つに分けるとファイル・データが破壊される危険性があります。
下記デモでもファイル入力とファイル出力でファイル・ロックは1つだけだと言うことに注意して下さい。
なお (データの削除などで)ファイル・サイズが前より小さくなる場合は「rewind(~)、ftruncate(~,0)」で一旦ファイル・サイズをゼロにする必要があります。
【example\MyServlet.java】
package example;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class MyServlet
*/
@WebServlet("/MyServlet")
public class MyServlet extends HttpServlet {
private static final long serialVersionUID = 1;
int iDebugSW = 0;
String sEncode = "UTF-8";
String sASC_LF = String.valueOf((char) 0x0A); // Line Feed.
String sASC_CR = String.valueOf((char) 0x0D); // Carriage Return.
String sIndention = sASC_CR;
String sUE_Indention; // = URLEncoder.encode(sIndention,sEncode);
String sReg_Indention = "(\\x0D\\x0A|\\x0D|\\x0A)";
// このデモでは改行コード(sIndention)は「x0D」しか使ってないですが、
// OSによって改行コードが違うので、正規表現で文字列を分割する。
String sDelimiter = "<>"; // "\t"; //
// $Delimiterは実際は"\t"タブ文字などが推奨されますが、
// デバッグ用表示には"<>"の方が分かりやすいでしょう。
String sUE_Delimiter; // = URLEncoder.encode(sDelimiter,sEncode);
String sFDirectory = "C:/pleiades/";
String sFName_Article = "Article.txt";
String sFName_Template = "Template.html";
String sFPathName_Article = sFDirectory + sFName_Article;
String sFPathName_Template = sFDirectory + sFName_Template;
String sFFirst_Print = "@Print_";
String sFExt_Print = ".txt";
Charset oCharset = Charset.forName(sEncode);
int iArticleMax = 100;
boolean Article_Add; // = false; // true; //
// int iLastSerial; // = -1;
String sPT_Name;
String sPT_Submit;
String sPT_LineMarker;
String sPT_Comment;
/**
* @see HttpServlet#HttpServlet()
*/
public MyServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(
HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
sUE_Delimiter = URLEncoder.encode(sDelimiter, sEncode);
sUE_Indention = URLEncoder.encode(sIndention, sEncode);
Article_Add = false; // true; //
request.setCharacterEncoding(sEncode);
sPT_Name = request.getParameter("Name");
if (sPT_Name == null)
sPT_Name = "";
sPT_Submit = request.getParameter("Submit");
if (sPT_Submit == null)
sPT_Submit = "";
sPT_LineMarker = request.getParameter("LineMarker");
if (sPT_LineMarker == null)
sPT_LineMarker = "";
sPT_Comment = request.getParameter("Comment");
if (sPT_Comment == null)
sPT_Comment = "";
System.out.printf("sPT_Name.compareTo(''): %d, %n", sPT_Name.compareTo(""));
System.out.printf(
"sPT_Comment.compareTo(''): %d, %n",
sPT_Comment.compareTo(""));
if (0 < sPT_Name.compareTo("") & 0 < sPT_Comment.compareTo("")) {
System.out.println(
"if ( 0<sPT_Name.compareTo('') & 0<sPT_Comment.compareTo(''))");
Article_Add = true; // false; //
}
String sResponse = "";
if (!sPT_Submit.equals("AJAX")) {
sResponse = File_Template();
}
if (sPT_Submit.equals("AJAX")) {
System.out.println("if (sPT_Submit.equals('AJAX'))");
sResponse = File_Article(sPT_LineMarker);
System.out.printf("sResponse: %s, %n", sResponse);
}
response.setContentType("text/html; charset = " + sEncode);
PrintWriter writer = response.getWriter();
writer.print(sResponse);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(
HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
String File_Template() {
String line = null;
String sTemplate = "";
try ( // これはtry-with-resources文なので、
// 自動クローズがサポートされるから、明示的なクローズは必要ない。
BufferedReader oReader = new BufferedReader(
new InputStreamReader(new FileInputStream(sFPathName_Template), sEncode));
//
) {
line = null;
while ((line = oReader.readLine()) != null) {
// dlsResponse.add(line);
line = line //
.replace("<&$DebugSW>", String.valueOf(iDebugSW)) //
.replace("<&$UE_Delimiter>", sUE_Delimiter) //
.replace("<&$UE_Indention>", sUE_Indention) //
.replace("<&$ArticleMax>", String.valueOf(iArticleMax)); //
sTemplate += line + sIndention;
}
} catch (IOException ex) {
ex.printStackTrace();
}
System.out.println(sFPathName_Template);
return sTemplate;
}
String File_Article(String sMarker) {
LocalDateTime oTimeStamp = LocalDateTime.now();
DateTimeFormatter oTDFormatter = DateTimeFormatter.ofPattern(
"yyyy/MM/dd HH:mm:ss");
List<String> dlsArticle = new ArrayList<String>();
String sBuffer, sNewBuffer;
sBuffer = "";
sNewBuffer = "";
String sSummary = "";
try ( // これはtry-with-resources文なので、
// 自動クローズがサポートされるから、明示的なクローズは必要ない。
RandomAccessFile oRW_Article = new RandomAccessFile(
sFPathName_Article,
"rw");
// 読み書き可能な File
FileChannel oRWC_Article = oRW_Article.getChannel(); //
// 読み書き可能な FileChannel
//
) {
int serial = -1;
String line = null;
oRWC_Article.lock();
ByteBuffer oByteBuffer;
oByteBuffer = ByteBuffer.allocate((int) oRWC_Article.size());
// oRWChannel.size()が約2GB(2,147,483,647Byte)を超えたらダメ。
oRWC_Article.read(oByteBuffer);
oByteBuffer.rewind();
sBuffer = oCharset.decode(oByteBuffer).toString();
if (!(null == sBuffer || "".equals(sBuffer))) {
dlsArticle = new ArrayList(Arrays.asList(sBuffer.split(sReg_Indention)));
}
if (0 < dlsArticle.size()) {
String[] col = dlsArticle.get(0).split(sDelimiter);
serial = Integer.parseInt(col[0]);
}
if (Article_Add) {
line = oTimeStamp.format(oTDFormatter) +
sDelimiter +
sPT_Name +
sDelimiter +
sPT_Comment;
if (serial < 0) {
serial = 0;
} else {
serial++;
serial = serial & 0xFFFFFFFF;
}
line = serial + sDelimiter + line;
dlsArticle.add(0, line);
}
// iLastSerial = serial;
boolean summary = true; // false; //
System.out.printf("sMarker: %s, %n", sMarker);
int iMarker = "".equals(sMarker) ? -1 : Integer.parseInt(sMarker);
System.out.printf("iMarker: %d, %n", iMarker);
for (int i = 0; i < Math.min(dlsArticle.size(), iArticleMax); i++) {
line = dlsArticle.get(i);
System.out.printf("line: %s, %n", line);
String[] col = line.split(sDelimiter);
System.out.printf("col[0]: %s, %n", col[0]);
serial = Integer.parseInt(col[0]);
System.out.printf("serial: %s, %n", serial);
if (iMarker == serial) {
System.out.println("if(iMarker==serial)");
summary = false; // true; //
}
if (summary) {
sSummary += line + sIndention;
}
line = (i != 0 ? sIndention : "") + line;
sNewBuffer += line;
}
System.out.printf("sSummary: %s, %n", sSummary);
if (Article_Add) {
oByteBuffer = oCharset.encode(sNewBuffer);
oByteBuffer.rewind();
oRWC_Article.truncate(0);
// 一旦 ファイル・サイズをゼロにしている。
oRWC_Article.write(oByteBuffer);
}
} catch (IOException ex) {
ex.printStackTrace();
}
return sSummary;
}
}
【\pleiades\Template.html】※ファイルの設置場所に注意して下さい(pleiades内)。
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
</head>
<style>
p,
section {
border: thin solid #000;
min-height: 1.5em;
}
#idDebug,
#idDebugControl {
display: none;
}
</style>
<body>
<form action="" method="POST">
<input type="text" id="idName" name="Name" value="" placeholder="Name"><br>
<input type="text" id="idComment" name="Comment" value="" placeholder="Comment">
<button type="button" name="Submit" onclick="sSubmit='Article_Add';">Submit</button>
</form>
<main id="idMain">
<div id="idDebugControl">
<button type="button" onclick="AJAXComm();">AJAXComm</button><br>
</div>
<section id="idDebug">
</section>
<section id="idMessage">
</section>
<section id="idArticle">
</section>
</main>
</body>
<script>
nDebugSW = Number("<&$DebugSW>");
sIndention = decodeURI("<&$UE_Indention>");
sDelimiter = decodeURI("<&$UE_Delimiter>");
nArticleMax = Number("<&$ArticleMax>");
nTimeout = 500;
sSubmit = "";
LineMarker = "";
wDebug = document.getElementById("idDebug");
wDebugControl = document.getElementById("idDebugControl");
wMessage = document.getElementById("idMessage");
wName = document.getElementById("idName");
wComment = document.getElementById("idComment");
wArticle = document.getElementById("idArticle");
ConsoleOut(`nDebugSW: ${nDebugSW}`);
ConsoleOut(`sDelimiter: ${sDelimiter}`);
if (nDebugSW) {
wDebug.style.display = "block";
wDebugControl.style.display = "block";
}
function AJAXComm() {
ConsoleOut();
ConsoleOut("function AJAXComm()");
var sAJAX_Charset = "UTF-8";
var sAJAX_Server = "";
var sQueryParameter = `Submit=AJAX&LineMarker=${LineMarker}&`;
sQueryParameter += `Name=${encodeURI(wName.value)}&`;
ConsoleOut(`sSubmit: ${sSubmit}, `);
var err = false; // true; //
if (sSubmit == 'Article_Add') {
ConsoleOut("if (sSubmit == 'Article_Add')");
sSubmit = "";
ConsoleOut(`wName.value: ${wName.value}, `);
ConsoleOut(`wComment.value: ${wComment.value}, `);
wMessage.innerText = "";
if (!wName.value) {
err = true; // false; //
wMessage.innerText += "★Nameが入力されていません\n";
}
if (!wComment.value) {
err = true; // false; //
wMessage.innerText += "★Commentが入力されていません\n";
}
sQueryParameter += `Comment=${encodeURI(wComment.value)}&`;
wComment.value = "";
}
ConsoleOut(`sQueryParameter: ${sQueryParameter}, `);
var sAJAX_Parameter = "";
var oAJAX = new XMLHttpRequest();
oAJAX.onload = function () {
ConsoleOut();
ConsoleOut("oAJAX.onload");
if (this.readyState === 4 && this.status === 200) {
ConsoleOut("this.readyState: " + this.readyState);
ConsoleOut("this.status: " + this.status);
var res = oAJAX.responseText;
ConsoleOut(`res: ${res}, `);
wDebug.innerText = res;
if (res) {
var a1sArticle = res.split(sIndention);
ConsoleOut("a1sArticle");
ConsoleOut(a1sArticle);
var col = null;
for (var i = a1sArticle.length - 1; 0 <= i; i--) {
var line = a1sArticle[i];
if (" " <= line) {
var wP = document.createElement('p');
wP.innerText = line;
wArticle.prepend(wP);
col = line.split(sDelimiter);
}
}
/*
var wFirst = wArticle.firstElementChild;
if (wFirst) {
col = wFirst.innerText.split(sDelimiter);
}
*/
if (col) {
LineMarker = Number(col[0]);
}
ConsoleOut(`LineMarker: ${LineMarker}, `);
while (nArticleMax < wArticle.childElementCount) {
wArticle.lastElementChild.remove();
}
}
}
if (!nDebugSW) {
setTimeout(AJAXComm(), nTimeout);
}
}
// oAJAX.overrideMimeType('text/plain; charset=' + sAJAX_Charset);
oAJAX.open('POST', sAJAX_Server);
oAJAX.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
oAJAX.send(sQueryParameter);
ConsoleOut(`oAJAX.send(sQueryParameter)`);
ConsoleOut(`sQueryParameter: ${sQueryParameter}, `);
}
if (!nDebugSW) {
setTimeout(AJAXComm(), nTimeout);
}
function ConsoleOut(msg) {
if (nDebugSW) {
msg = msg ? msg : "";
console.log(msg);
}
}
</script>
</html>
コメント
コメントを投稿