品質は後付けできる例
1つのブラウザで、複数タブを使った場合、サーバー側では、同一セッションになる。
ということは、
・はじめ、ユーザー名xmldtpでログインしておき、
その後、新たなタブでウィリアムとログインしても、
同じセッションが使われる
・はじめ、ユーザー名xmldtpでログインしておき、
その後、新たなタブでxmldtp(と同名で)ログインした場合、
(1)先にログインしているからエラー
(2)後でログインしたものを優先し、セッションを作り直す
のどちらもできる。ただし、(2)でセッションを作り直した後に、
はじめ操作していたタブでアクセスすると(2)で新しいセッション
になっているのでおかしくなる可能性がある(場合によっては落ちる)
■お題
ここで、
![]()
xmldtpでログインすると、
![]()
のように、アクセス回数とセッションIDを返すプログラムで、
何回もxmldtpでアクセスして
![]()
のようになった状態で、
![]()
のように、新たなIDでログインすると
![]()
のように初期化され
![]()
と戻すと
![]()
のように前の値を持ってくることを考える
※上記のセッションIDを見ると、みんな同じ。つまり、値は変わっているが、
同一セッションを使いまわしている
■概要
Tomcat7で指定URLのみクッキーを無効に
http://questionbox.jp.msn.com/qa7881678.html
の「ANo.3」に書かれているとおり。つまり、
・Filterに、ハッシュマップをstaticで用意する(=全体で1個)、
→そのハッシュマップには、
キーはログイン名、値は保存したい値を持っておく
・FilterのdoFilterで、
パラメータから、ログイン名をチェック
上記ハッシュマップに値が入っていたら、その値をセッションに入れる
→ここで、ログイン名ごとに、セッションの値が入れ替わる
・セッションのリスナーで
セッションの値が書き換わったら、上記ハッシュマップの値も書き換え
・セッションが破棄されれたら、上記ハッシュマップのログイン名のデータも破棄
ということをする。
■お題に関係ない部分
お題に関係ない、はじめの画面は、こんな感じのJSP(index.jsp)
<html> <head> <title>MySession</title> </head> <body> <form method="post" action="kekka.jsp"> login name<input type="text" name="loginName"/> <input type="submit"/> </form> </body>
ここで、ログイン名を入れているので、上記フィルタにより、
noの値がログイン名ごとに入れ替えられ
結果表示のJSP(kekka.jsp)
<%
int ino;
String no = (String)session.getAttribute("no");
if ( no == null )
{
ino=0;
}
else
{
ino = Integer.parseInt(no);
ino++;
}
session.setAttribute("no",String.valueOf(ino));
%>
<%=ino%>
<%=session.getId()%>
にわたる。ここでnoに1足すと、リスナーが呼ばれる。
ここまでは、あまり今回のお題に関係ない
■セッションの値を入れ替える部分:フィルタ
フィルタの値を入れ替えるJavaプログラムMyFilter1.javaはこんなかんじ。
package mytomcat1;
import java.io.IOException;
import java.util.HashMap;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public class MyFilter1 implements Filter {
static HashMap<String,String> svdata = new HashMap<String,String>();
@Override
public void destroy() {
// TODO 自動生成されたメソッド・スタブ
}
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
// http://www.javaroad.jp/servletjsp/sj_servlet10.htm
HttpServletRequest myreq = (HttpServletRequest)req;
HttpSession session = myreq.getSession();
String loginName = myreq.getParameter("loginName");
// セーブ領域から、データ復元
if ( loginName != null)
{
String no = svdata.get(loginName);
if ( no == null)
{
no = "0";
}
session.setAttribute("loginName",loginName);
session.setAttribute("no", no);
}
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO 自動生成されたメソッド・スタブ
}
}
■値が書き換わったとき:リスナー
セッションの値が書き換わったら、セーブしてあるデータも書き換える
MyFilterListener1.javaはこんなかんじ
package mytomcat1;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class MyFilterListener1 implements HttpSessionListener,
HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent arg0) {
}
@Override
public void attributeRemoved(HttpSessionBindingEvent arg0) {
}
@Override
public void attributeReplaced(HttpSessionBindingEvent arg0) {
// http://www.javaroad.jp/servletjsp/sj_servlet9.htm
// セッションが変更されたら、保存しているデータも変更
HttpSession session =arg0.getSession();
String loginName = (String)session.getAttribute("loginName");
String no = (String)session.getAttribute("no");
if ( loginName != null)
{
if ( no == null)
{
no = "0";
}
MyFilter1.svdata.put(loginName,no);
}
}
@Override
public void sessionCreated(HttpSessionEvent arg0) {
}
@Override
public void sessionDestroyed(HttpSessionEvent arg0) {
// セッションが削除されたら、保存しているデータも削除
HttpSession session = arg0.getSession();
String loginName = (String)session.getAttribute("loginName");
if ( loginName != null)
{
MyFilter1.svdata.remove(loginName);
}
}
}
■web.xml
フィルターとリスナーをweb.xmlに登録する。こんな感じ
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Test Servlet</display-name>
<description>Test Servlet</description>
<filter>
<filter-name>MyFilter1</filter-name>
<filter-class>mytomcat1.MyFilter1</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>mytomcat1.MyFilterListener1</listener-class>
</listener>
<servlet>
<servlet-name>MyServlet1</servlet-name>
<servlet-class>MyServlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet1</servlet-name>
<url-pattern>/MyServlet1</url-pattern>
</servlet-mapping>
</web-app>
■つけたし
(A)以上でできるのだが、これを実行すると
![]()
のように、違うブラウザでも、ログイン名が同じだと、同じセッションデータ
を使ってしまう。これはこれでいいかもしれないが、いけない場合、
ログイン名のほかにセッションIDを足したものをキーとすればよい
(B)後がちにするには、保存しているデータに、ログイン時間を持ち、
サーバーアクセスする際には、ログイン名とログイン時間を常に
パラメータで渡す。ログイン名は一致するが、ログイン時間が一致
しない場合は、後からログインされたものとみなして、
すぐに終了処理をするなり、セッションにその旨の値を入れる
(C)ユーザーAがログインしたら、B,Cはキャンセルという場合、
ユーザーAがログインしたら、ハッシュマップのB,Cデータを
消せば、B,Cがアクセスしたとき、ログインデータをなくせる
(D)これは、排他制御はしていない。したがって、同時アクセスされるとおかしくなる。
排他制御をするなら、ハッシュマップの(上記のソースだとsvdataの)ログイン名と
値を直接変更し、その際排他制御をし・・・とかいうと、セッションそのものを
自作しているのと、なんらかわらない(^^;)
■いいたいこと
で、いいたいことは、このやりかたでなく、
ユーザーごとにログインしたいとか、(A),(B),(C)のバグ修正
をするとしても、基本的に1を足すというロジック部分は変わっていない
(変わっているのはセッション部分で、主にフィルターとリスナーの中)
つまり、ロジック部分を分離すれば、あとは入出力部分で品質は変えられる。
1つのブラウザで、複数タブを使った場合、サーバー側では、同一セッションになる。
ということは、
・はじめ、ユーザー名xmldtpでログインしておき、
その後、新たなタブでウィリアムとログインしても、
同じセッションが使われる
・はじめ、ユーザー名xmldtpでログインしておき、
その後、新たなタブでxmldtp(と同名で)ログインした場合、
(1)先にログインしているからエラー
(2)後でログインしたものを優先し、セッションを作り直す
のどちらもできる。ただし、(2)でセッションを作り直した後に、
はじめ操作していたタブでアクセスすると(2)で新しいセッション
になっているのでおかしくなる可能性がある(場合によっては落ちる)
■お題
ここで、

xmldtpでログインすると、

のように、アクセス回数とセッションIDを返すプログラムで、
何回もxmldtpでアクセスして

のようになった状態で、

のように、新たなIDでログインすると

のように初期化され

と戻すと

のように前の値を持ってくることを考える
※上記のセッションIDを見ると、みんな同じ。つまり、値は変わっているが、
同一セッションを使いまわしている
■概要
Tomcat7で指定URLのみクッキーを無効に
http://questionbox.jp.msn.com/qa7881678.html
の「ANo.3」に書かれているとおり。つまり、
・Filterに、ハッシュマップをstaticで用意する(=全体で1個)、
→そのハッシュマップには、
キーはログイン名、値は保存したい値を持っておく
・FilterのdoFilterで、
パラメータから、ログイン名をチェック
上記ハッシュマップに値が入っていたら、その値をセッションに入れる
→ここで、ログイン名ごとに、セッションの値が入れ替わる
・セッションのリスナーで
セッションの値が書き換わったら、上記ハッシュマップの値も書き換え
・セッションが破棄されれたら、上記ハッシュマップのログイン名のデータも破棄
ということをする。
■お題に関係ない部分
お題に関係ない、はじめの画面は、こんな感じのJSP(index.jsp)
<html> <head> <title>MySession</title> </head> <body> <form method="post" action="kekka.jsp"> login name<input type="text" name="loginName"/> <input type="submit"/> </form> </body>
ここで、ログイン名を入れているので、上記フィルタにより、
noの値がログイン名ごとに入れ替えられ
結果表示のJSP(kekka.jsp)
<%
int ino;
String no = (String)session.getAttribute("no");
if ( no == null )
{
ino=0;
}
else
{
ino = Integer.parseInt(no);
ino++;
}
session.setAttribute("no",String.valueOf(ino));
%>
<%=ino%>
<%=session.getId()%>
にわたる。ここでnoに1足すと、リスナーが呼ばれる。
ここまでは、あまり今回のお題に関係ない
■セッションの値を入れ替える部分:フィルタ
フィルタの値を入れ替えるJavaプログラムMyFilter1.javaはこんなかんじ。
package mytomcat1;
import java.io.IOException;
import java.util.HashMap;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public class MyFilter1 implements Filter {
static HashMap<String,String> svdata = new HashMap<String,String>();
@Override
public void destroy() {
// TODO 自動生成されたメソッド・スタブ
}
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
// http://www.javaroad.jp/servletjsp/sj_servlet10.htm
HttpServletRequest myreq = (HttpServletRequest)req;
HttpSession session = myreq.getSession();
String loginName = myreq.getParameter("loginName");
// セーブ領域から、データ復元
if ( loginName != null)
{
String no = svdata.get(loginName);
if ( no == null)
{
no = "0";
}
session.setAttribute("loginName",loginName);
session.setAttribute("no", no);
}
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO 自動生成されたメソッド・スタブ
}
}
■値が書き換わったとき:リスナー
セッションの値が書き換わったら、セーブしてあるデータも書き換える
MyFilterListener1.javaはこんなかんじ
package mytomcat1;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class MyFilterListener1 implements HttpSessionListener,
HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent arg0) {
}
@Override
public void attributeRemoved(HttpSessionBindingEvent arg0) {
}
@Override
public void attributeReplaced(HttpSessionBindingEvent arg0) {
// http://www.javaroad.jp/servletjsp/sj_servlet9.htm
// セッションが変更されたら、保存しているデータも変更
HttpSession session =arg0.getSession();
String loginName = (String)session.getAttribute("loginName");
String no = (String)session.getAttribute("no");
if ( loginName != null)
{
if ( no == null)
{
no = "0";
}
MyFilter1.svdata.put(loginName,no);
}
}
@Override
public void sessionCreated(HttpSessionEvent arg0) {
}
@Override
public void sessionDestroyed(HttpSessionEvent arg0) {
// セッションが削除されたら、保存しているデータも削除
HttpSession session = arg0.getSession();
String loginName = (String)session.getAttribute("loginName");
if ( loginName != null)
{
MyFilter1.svdata.remove(loginName);
}
}
}
■web.xml
フィルターとリスナーをweb.xmlに登録する。こんな感じ
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Test Servlet</display-name>
<description>Test Servlet</description>
<filter>
<filter-name>MyFilter1</filter-name>
<filter-class>mytomcat1.MyFilter1</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>mytomcat1.MyFilterListener1</listener-class>
</listener>
<servlet>
<servlet-name>MyServlet1</servlet-name>
<servlet-class>MyServlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet1</servlet-name>
<url-pattern>/MyServlet1</url-pattern>
</servlet-mapping>
</web-app>
■つけたし
(A)以上でできるのだが、これを実行すると

のように、違うブラウザでも、ログイン名が同じだと、同じセッションデータ
を使ってしまう。これはこれでいいかもしれないが、いけない場合、
ログイン名のほかにセッションIDを足したものをキーとすればよい
(B)後がちにするには、保存しているデータに、ログイン時間を持ち、
サーバーアクセスする際には、ログイン名とログイン時間を常に
パラメータで渡す。ログイン名は一致するが、ログイン時間が一致
しない場合は、後からログインされたものとみなして、
すぐに終了処理をするなり、セッションにその旨の値を入れる
(C)ユーザーAがログインしたら、B,Cはキャンセルという場合、
ユーザーAがログインしたら、ハッシュマップのB,Cデータを
消せば、B,Cがアクセスしたとき、ログインデータをなくせる
(D)これは、排他制御はしていない。したがって、同時アクセスされるとおかしくなる。
排他制御をするなら、ハッシュマップの(上記のソースだとsvdataの)ログイン名と
値を直接変更し、その際排他制御をし・・・とかいうと、セッションそのものを
自作しているのと、なんらかわらない(^^;)
■いいたいこと
で、いいたいことは、このやりかたでなく、
ユーザーごとにログインしたいとか、(A),(B),(C)のバグ修正
をするとしても、基本的に1を足すというロジック部分は変わっていない
(変わっているのはセッション部分で、主にフィルターとリスナーの中)
つまり、ロジック部分を分離すれば、あとは入出力部分で品質は変えられる。