#
package com.tianhe.frm.chat;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Scanner;
/**
*
*<br>
* 客戶端的實現效果 <br>
*<br>
* 1.登錄服務器,如果服務器端口號和IP號輸入的字符都是"0"則,客戶端連接到默認的服務器 <br>
*<br>
* 2.輸入聊天昵稱 <br>
*<br>
* 3.輸入"-quit"會自動退出聊天 <br>
*<br>
* 4.輸入"-getList"會得到在線用戶的名稱 <br>
*<br>
* 5.輸入"-to <用戶名稱> <聊天信息>"會把信息發送到指定的用戶處,他人看不到 <br>
*<br>
* 6.輸入"-help"會得到客戶端相應操作幫助 <br>
*<br>
* 6.直接輸入內容則會將內容發送到所有在線的用戶處 <br>
*<br>
*<br>
*<br>
*<br>
* 客戶端的類與方法 <br>
*<br>
* 1.建立連接方法:connectServer(String ip,int serverPort) <br>
*<br>
* 2.斷開連接方法:disconnectServer() <br>
*<br>
* 2.發送消息方法:sendMes(String mes) <br>
*<br>
* 3.接受消息方法:getMes() <br>
*<br>
* 4.接受一次消息:getMesOnce() <br>
*<br>
* 5.獲得用戶列表:getList(String mes) <br>
*<br>
* 6.測試連接方法:testConnect(String ip,int serverPort) <br>
*<br>
* 7.檢查用戶名 :checkNickName(String nickName) <br>
*<br>
* 8.獲得幫助列表:helpList(); <br>
*<br>
* 9.各項內容的輸入在main方法中進行 <br>
*<br>
*<br>
*<br>
*<br>
* 重點注意的地方 <br>
*<br>
* 由于客戶端無法知道服務器何時轉發數據,所以客戶端需要一直不停地監聽消息,為了不影響其他的方法,如發送數據,這里用到多線程,即為接受數據單獨建立一個線程,以防止影響其他方法的正常工作。
*
*/
public class ChatClient implements Runnable
{
private String serverIp = "localhost";
private int serverPort = 9999;
private InetAddress serverAddr = null;
// 客戶端
private DatagramSocket ds = null;
// 發送的數據報
private DatagramPacket dp = null;
// 用來測試是否能夠連接成功或者用戶名是否正確
// 在testConnect和checkNickName中會用到.
// 并穿插在sendMes方法中
private boolean test = true;
/**
* 建立連接
*
* @param ip 服務器IP號
* @param serverPort 服務器Port
*/
public boolean initServer(InetAddress serverAddr, int serverPort)
{
this.serverAddr = serverAddr;
this.serverPort = serverPort;
try
{
ds = new DatagramSocket();
}
catch (SocketException s)
{
System.out.println("The Connection of server is error.");
return false;
}
return true;
}
/**
* 斷開連接
*/
public void disconnectServer()
{
// 當客戶端斷開連接之前,客戶端會向服務器端發送一個信息,
// 以便告訴服務器該客戶下線
this.sendMes("-quit");
if (ds != null)
ds.close();
}
/**
* 發送消息
*
* @param mes
*/
public void sendMes(String mes)
{
byte[] buf = mes.getBytes();
dp = new DatagramPacket(buf, buf.length, serverAddr, serverPort);
// 當建立發送時,測試正常
test = true;
try
{
ds.send(dp);
}
catch (NullPointerException n)
{
// 如果無法得到要發送的消息,測試終止
test = false;
System.out.println("The nullity of the address of connection");
}
catch (IOException s)
{
// 如果無法發送消息至服務器,測試終止
test = false;
System.out.println("There is a problem to send message.");
}
finally
{
}
}
/**
* 得到數據
*/
public void getMes()
{
byte[] buf = new byte[1024];
DatagramPacket getDatas = new DatagramPacket(buf, buf.length);
String mes = null;
try
{
while (true)
{
ds.receive(getDatas);
mes = new String(buf, 0, getDatas.getLength());
// 如果服務器發送的消息中的頭片段有-getList
// 表明客戶端獲得在線用戶列表,調用getList方法解析
if (mes.indexOf("-getList") == 0)
this.getList(mes);
else
System.out.println(mes);
}
}
catch (IOException i)
{
System.out.println("Fail in receving message");
}
}
/**
* 得到一次數據
*
*/
public String getMesOnce()
{
byte[] buf = new byte[1024];
DatagramPacket getDatas = new DatagramPacket(buf, buf.length);
String mes = null;
try
{
ds.receive(getDatas);
mes = new String(buf, 0, getDatas.getLength());
}
catch (Exception e)
{
System.out.println("!-Can not receive the message!");
}
finally
{
}
return mes;
}
/**
* 顯示在線用戶列表
*
* @param mes
*/
private void getList(String mes)
{
String[] list = mes.split(",");
System.out.println("在線用戶:\n-------------");
for (int i = 1; i < list.length; i++)
{
System.out.println(" * " + list[i]);
}
System.out.println("-------------");
}
/**
* 判斷用戶名輸入是否正確
*/
private boolean checkNickName(String nickName)
{
test = true;
String mes = null;
// 判斷昵稱是否符合約定
for (int i = 0; i < nickName.length(); i++)
{
char temp = nickName.charAt(i);
// 如果不符合,則終止測試
if (!Character.isLetterOrDigit(temp))
{
test = false;
break;
}
}
// 如果通過約定,則試圖向服務器發送昵稱,進行判斷
if (test)
{
this.sendMes("-nick " + nickName);
mes = this.getMesOnce();
// 如果服務器返回"-nick"標識
// 說明測試昵稱失敗,終止測試
if (mes.startsWith("-nick"))
{
test = false;
}
}
System.out.println(mes);
return test;
}
/**
* 得到幫助列表
*
*/
public void helpList()
{
System.out.println("重要說明:如果已經成功連接,只要輸入內容,所有在線用戶都能看到您的發言。" + "\n" + "-help" + " 獲取客戶端相應操作的幫助" + "\n"
+ "-test" + " 測試與服務器的連接,如果已經服務器連接,請不要選用此項" + "\n" + "-getList" + " 得到在線用戶的列表" + "\n"
+ "-to <name> <message>" + "\n" + " 將消息發送到特定的在線用戶處" + "\n" + "-quit"
+ " 斷開連接,退出聊天室" + "\n");
}
/**
* 線程部分
*/
public void run()
{
getMes();
}
/**
* 各項內容的操作器
*/
public static void main(String[] args)
{
ChatClient cc = new ChatClient();
// serverAddr && serverPort
String serverIp = "localhost";
int serverPort = 9999;
InetAddress serverAddr = null;
if (args != null && args.length > 0)
{
int i = 0;
while (i < args.length)
{
// serverAddr
if (args[i].equalsIgnoreCase("-ServerAddr"))
{
i = i + 1;
serverIp = args[i];
continue;
}
// serverPort
int ti = 9999;
if (args[i].equalsIgnoreCase("-ServerPort"))
{
i = i + 1;
ti = Integer.parseInt(args[i]);
if (ti > 0)
{
serverPort = ti;
}
i = i + 1;
continue;
}
}
}
try
{
serverAddr = InetAddress.getByName(serverIp);
}
catch (UnknownHostException e)
{
e.printStackTrace();
System.exit(-1);
}
System.out.println("歡迎登錄Lexiaofei聊天室!");
Scanner input = new Scanner(System.in);
// 連接服務器
cc.initServer(serverAddr, serverPort);
// 輸入昵稱
boolean goon = true;
while (goon)
{
// 第三步:輸入昵稱
System.out.println("3.請輸入您登錄后顯示的昵稱(只能輸入中文、英文字母及數字):");
String nick = input.next();
boolean judge = cc.checkNickName(nick);
// 判斷昵稱是否輸入正確
if (judge)
goon = false;
else
System.out.println("!-您輸入的用戶名不符合條件,請重新輸入!");
}
// 開始運行接收數據
new Thread(cc).start();
// 聊天
String mes = null;
System.out.println("!-您成功進入聊天室!");
while (!(mes = input.nextLine()).equalsIgnoreCase("-quit"))
{
// 如果輸入"-test"命令,開始測試
if (mes.trim().equalsIgnoreCase("-getList"))
{
cc.sendMes(mes);
// -nick 設置昵稱
}
else if (mes.equalsIgnoreCase("-help"))
{
cc.helpList();
// 如果輸入的是空字符,則不發送
}
else
{
if (!mes.equals(""))
{
cc.sendMes(mes);
}
}
}
// 退出
input.close();
cc.disconnectServer();
}
/**
* 錯誤提示標記
*
* @param str
*/
public static void errorTip(String str)
{
System.out.println("-----------------\n" + str + "\n-----------------");
}
}
package com.tianhe.frm.chat;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Scanner;
/**
*
*<br>
* 服務器的實現效果: <br>
*<br>
* 1.輸入-help得到所有有關服務器操作的命令 <br>
*<br>
* 2.輸入-run進入服務器各項內容初始化 <br>
*<br>
* 3.輸入-stop斷開服務器 <br>
*<br>
* 4.輸入-quit斷開服務器,并退出操作 <br>
*<br>
* 5.服務器創建成功后,會通過單獨的線程運行監聽客戶端信息(listenLink) <br>
*<br>
* 6.服務器接收到數據后,會將數據移交至數據分析器(analyseMes)處理 <br>
*<br>
* 7.當用戶確定連接并確定昵稱后,服務器將該用戶的地址及姓名存儲在infoMemory中。 <br>
*<br>
*<br>
*<br>
*<br>
* 服務器的類與方法: <br>
*<br>
* 1.測試服務器創建:testServer(String ip,int serverPort) <br>
*<br>
* 2.初始化服務器 :initServer(String ip,int serverPort) <br>
*<br>
* 3.確定IP與Port :fixServerLink(String ip,int serverPort) <br>
*<br>
* 4.信息監聽器 :listenLink() <br>
*<br>
* 5.數據分析器 :analyseMes(String mes) <br>
*<br>
* 6.獲取地址用戶名:getConsumerName(SocketAddress sa) <br>
*<br>
* 7.數據轉發器 :transforMes(String mes) <br>
*<br>
* 8.數據單項轉發器:transforMesSingle(SocketAddress adr,String mes) <br>
*<br>
* 9.停止服務器 :cutServer() <br>
*<br>
* 10.獲得幫助列表 :helpList() <br>
*<br>
* 11.錯誤提示方法 :errorTip(String str) <br>
*<br>
* 12.在主函數中進行相應操作 <br>
*<br>
*<br>
* 重點注意的地方: <br>
*<br>
* 與客戶端相仿,為了順利監聽信息,需要另一個線程運行信息監聽器
*
*/
public class ChatServer implements Runnable
{
private DatagramSocket ds = null;
private DatagramPacket dp = null;
private String serverIp = "localhost";
private int serverPort = 9999;
private InetAddress serverAddr = null;
// 開始運行監聽的變量
private boolean beStart = false;
// 信息存儲器
private Hashtable<String, SocketAddress> userCache = new Hashtable<String, SocketAddress>();
/**
* 服務器初始化
*
* @param ip
* @param serverPort
*/
public boolean initServer(InetAddress serverAddr, int serverPort)
{
this.serverAddr = serverAddr;
this.serverPort = serverPort;
try
{
ds = new DatagramSocket(serverPort, serverAddr);
System.out.println("!-The Server Initialization Success!");
// 可以開始運行變量
beStart = true;
}
catch (SocketException s)
{
// 如果出現異常,則服務器測試不通過
errorTip("!-The Server Initialization Fail!--new DatagramSocket(" + serverPort + ", " + serverAddr + ")");
return false;
}
return true;
}
/**
* 監聽信息
*
*/
public void listenLink()
{
byte[] buf = new byte[1024];
String mes = null;
try
{
dp = new DatagramPacket(buf, buf.length);
System.out.println("!-The Server starts listenning to message.");
while (beStart)
{
ds.receive(dp);
mes = new String(buf, 0, dp.getLength());
// 將獲取的數據傳遞至數據分析器
this.analyseMes(mes);
}
}
catch (IOException e)
{
errorTip("!-The Server Can not receive message");
}
}
/**
* 數據分析器,給予相應處理
*
* @param mes
*/
private void analyseMes(String mes)
{
// 獲取當前客戶端的地址:
SocketAddress adr = dp.getSocketAddress();
// -test:進行服務器與客戶端的連接測試
// 若成功,則將該客戶端發送成功消息
if (mes.trim().equalsIgnoreCase("-test"))
{
transforMesSingle(adr, "-test: !-From Server:Succeed in Testing.");
}
// -quit:接受客戶端退出信息
// 將該用戶的退出信息轉發至所有在線成員
else if (mes.trim().equalsIgnoreCase("-quit"))
{
String name = this.getConsumerName(adr);
System.out.println(name + "http://" + adr + " quit! ");
transforMes("* " + name + "退出聊天室");
userCache.remove(name);
}
// -getList:接受客戶端獲取列表的請求
// 將所有用戶連接為字符串的形式,如:"-getList,用戶1,用戶2...用戶n"
else if (mes.trim().equals("-getList"))
{
StringBuffer list = new StringBuffer();
list.append("-getList,");
Enumeration<String> names = userCache.keys();
while (names.hasMoreElements())
{
list.append(names.nextElement() + ",");
}
transforMesSingle(dp.getSocketAddress(), list.toString());
}
// -to:接受客戶端請求,將信息轉發給相應客戶
// 如果目標客戶不存在,則向請求客戶發送相應消息
else if (mes.indexOf("-to ") != -1 && mes.startsWith("-to "))
{
String main = mes.substring("-to ".length(), mes.length());
String toName = main.substring(0, main.indexOf(" "));
String name = this.getConsumerName(adr);
String con = name + " say to you :" + main.substring(toName.length() + 1, main.length());
if (!userCache.containsKey(toName))
transforMesSingle(adr, "!-The message can not be recevie by whom you send for,please check out.");
else
transforMesSingle(userCache.get(toName), con);
}
// -nick:接受客戶端登錄請求
// 如果輸入的匿名不存在,則登記該用戶與infoMemory
// 如果存在則返回相應提示
else if (mes.indexOf("-nick ") != -1 && mes.startsWith("-nick "))
{
String nickName = mes.substring("-nick ".length(), mes.length());
if (userCache.containsKey(nickName))
transforMesSingle(adr, "-nick: !-The nickname you post is already exist,please try others.");
else
{
userCache.put(nickName, adr);
transforMes("Welcome " + nickName + " to Sunspot Chat!");
System.out.println(nickName + "http://" + adr.toString() + " succeed in lanuching.");
}
}
// 一般消息將會轉發至所有用戶
else
{
String name = this.getConsumerName(adr);
transforMes(name + ": " + mes);
}
}
/**
* 通過地址得到用戶的昵稱 由于Hashtable無法通過Value獲取Key,所有專門寫該方法
*
* @param sa
* @return
*/
private String getConsumerName(SocketAddress sa)
{
Enumeration<String> names = userCache.keys();
String name = null;
while (names.hasMoreElements())
{
String temp = names.nextElement();
SocketAddress adrs = userCache.get(temp);
// 進行比較
if (sa.equals(adrs))
{
name = temp;
break;
}
}
return name;
}
/**
* 向所有的用戶發送數據
*
* @param mes
*/
public void transforMes(String mes)
{
byte[] buf = mes.getBytes();
DatagramPacket sendDatas = null;
Enumeration<SocketAddress> all = userCache.elements();
try
{
while (all.hasMoreElements())
{
sendDatas = new DatagramPacket(buf, buf.length, all.nextElement());
ds.send(sendDatas);
}
}
catch (SocketException s)
{
errorTip("!-The feedback address is error!");
}
catch (IOException i)
{
errorTip("!-Can not send message!");
}
}
/**
* 向單個用戶發送數據
*
* @param adr
* @param mes
*/
public void transforMesSingle(SocketAddress adr, String mes)
{
byte[] buf = mes.getBytes();
try
{
DatagramPacket sendDatas = new DatagramPacket(buf, buf.length, adr);
ds.send(sendDatas);
}
catch (SocketException s)
{
errorTip("!-The feedback address is error!");
}
catch (IOException i)
{
errorTip("!-Can not send message!");
}
}
/**
* 斷開連接
*
*/
public void cutServer()
{
beStart = false;
if (ds != null)
ds.close();
System.out.println("!-The ds is done.");
}
public void helpList()
{
System.out.println("-help" + " 獲取服務器相應操作的幫助" + "\n" + "-run " + " 運行服務器,并同時建立信息監聽" + "\n" + "-stop"
+ " 停止服務器" + "\n" + "-quit" + " 停止服務器,并退出命令" + "\n");
}
/**
* 線程
*/
public void run()
{
listenLink();
}
/**
* 主要操作
*
* @param args
*/
public static void main(String[] args)
{
ChatServer cs = new ChatServer();
// serverAddr && serverPort
String serverIp = "localhost";
int serverPort = 9999;
InetAddress serverAddr = null;
if (args != null && args.length > 0)
{
int i = 0;
while (i < args.length)
{
// serverAddr
if (args[i].equalsIgnoreCase("-ServerAddr"))
{
i = i + 1;
serverIp = args[i];
continue;
}
// serverPort
int ti = 9999;
if (args[i].equalsIgnoreCase("-ServerPort"))
{
i = i + 1;
ti = Integer.parseInt(args[i]);
if (ti > 0)
{
serverPort = ti;
}
i = i + 1;
continue;
}
}
}
try
{
serverAddr = InetAddress.getByName(serverIp);
}
catch (UnknownHostException e)
{
e.printStackTrace();
System.exit(-1);
}
// 建立輸入
System.out.println("Lexiaofei聊天室創建成功!");
Scanner input = new Scanner(System.in);
System.out.println("!-請輸入服務端命令:");
// 開始輸入
String command = null;
// 如果輸入quit將斷開連接,并退出操作
while (!(command = input.next()).equalsIgnoreCase("-quit"))
{
// 獲取幫助
if (command.equalsIgnoreCase("-help"))
{
cs.helpList();
}
// 初始化服務器
if (command.equalsIgnoreCase("-run"))
{
boolean goon = true;
while (goon)
{
// 測試服務器創建,如果成功則同時為信息監聽器建立線程
System.out.println("!-創建服務器并運行...");
if (cs.initServer(serverAddr, serverPort))
{
new Thread(cs).start();
goon = false;
}
else
System.out.println("!-服務器創建失敗,請檢查!");
}
}
// 關閉服務器
if (command.equalsIgnoreCase("-stop"))
{
cs.cutServer();
}
}
input.close();
cs.cutServer();
}
/**
* 錯誤提示
*
* @param str
*/
public static void errorTip(String str)
{
System.out.println("-----------------\n" + str + "\n-----------------");
}
}
<!--weblogic-->
<bean id="jtaTxManager" class="org.springframework.transaction.jta.WebLogicJtaTransactionManager">
<property name="transactionManagerName" value="javax.transaction.TransactionManager" />
</bean>
<!--websphere-->
<bean id="jtaTxManager" class="org.springframework.transaction.jta.WebSphereUowTransactionManager" />
<tx:advice id="txAdvice" transaction-manager="jtaTxManager">
<tx:attributes>
<tx:method name="select*" read-only="true" />
<tx:method name="query*" read-only="true" />
<tx:method name="list*" read-only="true" />
<tx:method name="detail*" read-only="true" />
<tx:method name="insert*" />
<tx:method name="update*" />
<tx:method name="delete*" />
<tx:method name="transfer*" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="transactionPointcut"
expression="execution(public * com.wbd.ngcrm.example.dictitem.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPointcut" />
</aop:config>
<!-- dataSource begin -->
<bean id="dataSource.yy1a" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/yy1a</value>
</property>
</bean>
<bean id="dataSource.yy1b" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/yy1b</value>
</property>
</bean>
<bean id="dataSource.yy2a" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/yy2a</value>
</property>
</bean>
<bean id="dataSource.yy2b" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/yy2b</value>
</property>
</bean>
為了使用WebWork,我們只需要在web.xml配置FilterDispatcher一個過濾器即可,閱讀一下FilterDispatcher的JavaDoc和源碼,我們可以看到它調用了:
finally
{
ActionContextCleanUp.cleanUp(req);
}
在ActionContextCleanUp中,有這樣的代碼:
req.setAttribute(CLEANUP_PRESENT, Boolean.TRUE);
如果FilterDispatcher檢測到這個屬性,就不會清除ActionContext中的內容了,而由ActionContextCleanUp后續的代碼來清除,保證了一系列的Filter訪問正確的ActionContext.
文檔中提到,如果用到SiteMesh的Filter或者其他類似Filter,那么設置順序是:
ActionContextCleanUp filter
SiteMesh filter
FilterDispatcher
所以最后我們的web.xml應該類似這樣:
<filter>
<filter-name>ActionContextCleanUp</filter-name>
<filter-class>com.opensymphony.webwork.dispatcher.ActionContextCleanUp</filter-class>
</filter>
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>com.opensymphony.webwork.sitemesh.FreeMarkerPageFilter</filter-class>
</filter>
<filter>
<filter-name>webwork</filter-name>
<filter-class>com.opensymphony.webwork.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>ActionContextCleanUp</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>webwork</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
問題起因:
今天遇到一個亂碼問題,以前已經配置好了的呀。而且是普遍現象,看來是公共的變更引起的問題。
分析過程:
于是開始進行調試,因為是公共問題,所以就對web.xml最近的變更進行增刪調試,發現如果使用SecurityFilter就報錯,去掉久正常了。
仔細檢查,發現如下現象:
1、在設置字符集之前, 提前調用了request.getParameter()方法,就會出現問題。即使后來再設置字符集,隨后的action接收的數據也會亂碼。
2、對于同一個url,如:*.do,filter-mapping 是有順序的,按照web.xml中的配置順序。
從網上查到的結論:
根據servlet2.3規范filter執行是按照web.xml配置的filter-mapping先后順序進行執行,所以上面的配置會導致遇見*.do的url請求,先進行SecurityFilter的過濾器處理,這時候沒有做編碼處理,已經是亂碼,到下面的filter處理時已經時亂碼,再做編碼處理已經沒有用處。
解決辦法:
錯誤的順序:
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CharacterEncoding</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CharacterEncoding</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
正確的順序:
<filter-mapping>
<filter-name>CharacterEncoding</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CharacterEncoding</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
問題起因:
升級spring2.x的dtd聲明為xsd聲明以后,spring2.x+struts2.x的應用在weblogic81下報錯,日志提示建議使用jdk1.5或升級xerces。
解決過程:
自己寫了一個spring2.x+struts2.x應用在weblogic81下正常運行。說明問題出在應用上。
拷貝出問題的應用的jar包到成功應用的lib目錄,問題仍然存在。說明還有其他隱情。
比對兩個應用,發現出問題的應用使用了weblogic.xml, 其中配置了優先加載web-inf\lib的jar包。
此時建立jrokit/jar/lib/endrosed目錄,放入最新的xerces包,也不生效。
換一個思路,查詢DocumentBuilderFactoryImpl,發現有應用中有4處引用,刪除xerces.jar的時候,發布成功。至此問題解決!!!!!!!
問題總結:
1 要注意保留成功的案例,以便比對。
最近碰到兩個類似的問題了:一個是websphere下的web-inf\template問題,一個就是由于weblogic.xml干預,造成xerces.jar
的問題。
2 要注意觀察實際使用的類的版本
3 升級以后,一定要驗證單個平臺下是否都能正常使用,并增加評審環節,避免影響他人。
1、gbk -> GBK
2、weblogic.xml中上下文要寫為war的應用名字
3、8.1版本中的j2ee組件(filter,listener,servlet)初始化順序與tomcat,websphere不一樣,如果組件之間有依賴,無法實現跨平臺相同部署。
在JSP頁面中的對象,包括用戶創建的對象(例如,JavaBean對象)和JSP的隱含對象,都有一個范圍屬性。
范圍定義了在什么時間內,在哪一個JSP頁面中可以訪問這些對象。
例如,session對象在會話期間內,可以在多個頁面中被訪問。application對象在整個Web應用程序的生命周期中都可以被訪問。
在JSP中,有4種范圍,如下所示。
page范圍
具有page范圍的對象被綁定到javax.servlet.jsp.PageContext對象中。在這個范圍內的對象,只能在創建對象的頁面中訪問。可以調用pageContext這個隱含對象的getAttribute()方法來訪問具有這種范圍類型的對象(pageContext對象還提供了訪問其他范圍對象的getAttribute方法),pageContext對象本身也屬于page范圍。當Servlet類的_jspService()方法執行完畢,屬于page范圍的對象的引用將被丟棄。page范圍內的對象,在客戶端每次請求JSP頁面時創建,在頁面向客戶端發送回響應或請求被轉發(forward)到其他的資源后被刪除。
request范圍
具有request范圍的對象被綁定到javax.servlet.ServletRequest對象中,可以調用request這個隱含對象的getAttribute()方法來訪問具有這種范圍類型的對象。在調用forward()方法轉向的頁面或者調用include()方法包含的頁面中,都可以訪問這個范圍內的對象。要注意的是,因為請求對象對于每一個客戶請求都是不同的,所以對于每一個新的請求,都要重新創建和刪除這個范圍內的對象。
session范圍
具有session范圍的對象被綁定到javax.servlet.http.HttpSession對象中,可以調用session這個隱含對象的getAttribute()方法來訪問具有這種范圍類型的對象。JSP容器為每一次會話,創建一個HttpSession對象,在會話期間,可以訪問session范圍內的對象。
application范圍
具有application范圍的對象被綁定到javax.servlet.ServletContext中,可以調用application這個隱含對象的getAttribute()方法來訪問具有這種范圍類型的對象。在Web應用程序運行期間,所有的頁面都可以訪問在這個范圍內的對象。
訪問控件的主要對象是:document對象, 提供幾個主要方法來訪問對象:
1. document.getElementById
2. document.getElementsByName
3. document.getElementsByTagName
4. document.all
下面談談以上幾個方法的具體用法:
一.document.getElementById
Var obj=document.getElementById("ID")
根據指定的ID屬性值得到對象。返回id屬性值等于ID的第一個對象的引用。假如對應的為一組對象,則返回該組對象中的第一個。
<input name="a" type="text" id="b"/>
<input name="b" type="text" id="a"/>
<input type="button" name="submint1" value="text1" onclick=:"alert(document.getElementById("b"))"/>
<input type="button" name="submint2" value="text2" onclick="alert(document.getElementById("a")))"/>
我在IE中測試了上面代碼,在第一個文本框中輸入1,在第二個文本中輸入2,然后點擊兩個按鈕,大吃一驚。
結果兩個按鈕都返回了第一個文本框的值。
這說明了IE執行document.getElementById(elementName)的時候,返回的是第一個name或者id等于elementName的對象,并不是根據ID來查找的。
相反我在firefox中就不存在這個問題。Firefox執行document.getElementById(elementName)的時候只能查找ID等于elementName對象,如果不存在在返回null.
二.document.getElementsByName
Var obj=document.getElementsByName("Name")
根據Name屬性的值獲取對象集合。返回name等于指定Name對象的集合。注意這里返回的是一個集合,包括只有一個元素的情況也是一個集合。
document.getElementsByName("name")[0]
這樣來獲取某一個元素。注意javascript中的集合取一個值可以用[],也可以用()。
如:
<script>
function prop()
{
var objs=document.getElementsByName("a");
alert(objs(0).value);//或者可以alert(objs[0].value)也正確的。
}
</script>
<input type="text" name="a" id="b" value="this is textbox"/>
<input type="button" value="testing" onclick="prop()"/>
三.Document.getElementsByTagName
Var ojbs=document.getElementsByTagName("Tag")
根據基于指定元素名稱對象的集合。返回Tag屬性等于指定Tag標記的集合。這里也返回的是一個集合。(同上)
四.document.all用法。
document.all是頁面內所有元素的一個集合。例如:
document.all(0)表示頁面的第一個元素。
Document.all("txt")表示頁面上id或name等于txt的所有對象的單個元素和集合元素。
如果頁面上的id或name等于txt只有一個元素(包括name和id情況),那么document.all()的結果就只是一個元素,反之就是獲取一個集合。
(綜合了document.getElementById和document.getElementsByName的各自的特點)。
也可以這樣寫:document.all.txt也是一樣。
例如:
<input name=aaa value=aaa>
<input id=bbb value=bbb>
<script language=Jscript>
alert(document.all.aaa.value) //根據name取value
alert(document.all.bbb.value) //根據id取value
</script>
代碼2:
但是常常name可以相同(如:用checkbox取用戶的多項愛好的情況)
<input name=aaa value=a1>
<input name=aaa value=a2>
<input id=bbb value=bbb>
<script language=Jscript>
alert(document.all.aaa(0).value)//顯示a1
alert(document.all.aaa(1).value)//顯示a2
alert(document.all.bbb(0).value)//這行代碼會失敗
</script>
理論上一個頁面中的ID是互不相同的,如果出現不同的tags而有相同的id的話,document.all.id就會失敗,就象這樣:
<input id=aaa value=a1>
<input id=aaa value=a2>
<script language=Jscript>
alert(document.all.aaa.value)//顯示undefined而不是a1或者a2
</script>
所以說遇到了這種情況的話用下面這種寫法:
<input id=aaa value=aaa1>
<input id=aaa value=aaa2>
<input name=bbb value=bbb>
<input name=bbb value=bbb2>
<input id=ccc value=ccc>
<input name=ddd value=ddd>
<script language=Jscript>
alert(document.all("aaa",0).value)
alert(document.all("aaa",1).value)
alert(document.all("bbb",0).value)
alert(document.all("bbb",1).value)
alert(document.all("ccc",0).value)
alert(document.all("ddd",0).value)
</script>
另外document.all可以判斷瀏覽器的種類是否是IE,
document.all---------針對IE
document.layers------------針對Netscape
這兩個集合.all只在ie里面有效,layers只在nc里面有效
所以就可以通過這個方式來判斷不同的瀏覽器。
最后我來說說getElementById和getElementsByName使用范圍:
Id就像身份證號,是唯一的,name就像姓名一樣可以同名。
一個元素定義了id,引用該元素時直接用id屬性,而name通常用在form中,且必須由document.form.***而來,
也就是說,name 屬性定義的元素在腳本中是document 對象的子對象。
1. name用于form內元素,提交需要.id用于form外元素好用因為DOM能直接取得單一元素
2.id 每頁只能有一個. name可以有多個 name,有些標簽不推薦用它
3. 表單元素(form input textarea select)與框架元素(iframe frame) 用 name這些元素都與表單(框架元素作用于form的target)提交有關,
在表單的接收頁面只接收有name的元素 , 賦ID 的元素通過表單是接收不到值的, 你自己可以驗證一下有一個例外A 可以賦 name 作為錨點, 也可以賦 ID;只能賦ID不能賦name的元素:(除去與表單相關的元素都只能賦ID)
body li table tr td th p div span pre dl dt dd font b 等等。
這里我引出另一個問題,既然有了ID那為什么還要name呢?
最直接答案:ID就像是一個人身份證號,而 name就像是他的名字,ID雖然是唯一的,但name是可以重復的。
具體來說:對于ID來說,它就是Client端HTML元素的Identity 。而Name 其實要復雜的多,因為 Name 有很多種的用途,所以它并不能完全由ID來代替,從而將其取消掉。
參考網站資料如下:具體用途有:
用途1:作為可與服務器交互數據的HTML元素的服務器端的標示,比如input、select、textarea和button等。我們可以在服務器端根據其Name通過
request.getParameter('name')取得元素提交的值。
用途2:HTML元素Input type="radio" 分組,我們知道radio button控件在同一個分組類,check操作是mutex的,同一時間只能選中一個radio,這個分組就是根據相同的Name屬性來實現的。
用途3:建立頁面中的錨點,我們知道<a href="URL" >link </a>是獲得一個頁面超級鏈接,
如果不用href屬性,而改用Name,如:<a name="PageBottom"></a>,我們就獲得了一個頁面錨點。
用途4:作為對象的 Identity ,如 Applet 、 Object 、 Embed 等元素。比如在 Applet 對象實例中,我們將使用其Name來引用該對象。
用途5:在IMG元素和MAP元素之間關聯的時候,如果要定義IMG的熱點區域,需要使用其屬性usemap,使usemap="#name"(被關聯的MAP元素的Name)
用途6:某些特定元素的屬性,如attribute,和param。例如為Object定義參數<PARAM NAME = "appletParameter" VALUE = "value" > .
顯然這些用途都不是能簡單的使用 ID來代替掉的,所以HTML元素的ID和 Name的卻別并不是身份證號碼和姓名這樣的區別,它們更本就是不同作用的東西。
當然HTML元素的Name屬性在頁面中也可以起那么一點ID的作用,因為在DHTML對象樹中,我們可以使用 document.getElementsByName 來
獲取一個包含頁面中所有指定Name元素的對象數組。
在這里順便說一下,要是頁面中有n(n >1) 個HTML元素的ID都相同了怎么辦?在DHTML對象中怎么引用他們呢?
這個時候我們還是可以繼續使用document.getElementById獲取對象,只不過我們只能獲取ID重復的那些對象中在HTML Render時第一個出現的對象。
而這時重復的ID會在引用時自動變成一個數組,ID重復的元素按 Render的順序依次存在于數組中。
getElementById("xxx")返回第一個id屬性為"xxx"或者特定表單元素name為"xxx"的元素
getElementsByName("xxx")返回所有id屬性為"xxx"或者特定表單元素name為"xxx"的元素
這里要說明一下其實getElementById和getElementsByName取元素的范圍是一樣的,區別只是前者只返回第一個元素后者返回所有元素的集合
另外說明一下表單元素 表單元素指的是在<FORM >標簽中數據可以被提交給服務器的標簽,
主要有 <INPUT > <SELECT > <TEXTAREA >三個標簽 這三個標簽只有在name屬性不為空的時候才能將數據提交給服務器 所以這三種標簽多定義了一個name屬性
其實這個name屬性和id屬性是完全一樣的都可以定位元素
如果不是表單元素 就算你加了name屬性getElementsByName也取不到不信你自己試試
問題起因:
今天兩個同事向我反映workspace.jsp頁面性能下降了,開始以為他們機器性能差或者jsp沒有編譯的原因。礙于面子,就坐下來看了一下,這一看不要緊,每次點都比較慢。兩個人的機器配置也不差,以前同樣的應用速度還是能夠忍受的。我立刻感覺到了可能確實存在了問題,一個頁面在tomcat單機下要幾秒才能出來,肯定是有問題了。我們的系統是企業級別的應用,并發客戶量非常的大。要是出了這樣的問題,肯定會當場癱瘓的。
問題分析:
我首先通知了負責這個模塊的同事,檢查最近這個功能的修改紀錄。最后發現原來是增加了一個ocx控件的調用。每次訪問這個頁面都要重新從服務器下載這個控件,導致了頁面加載的性能問題。
解決辦法:
把控件的加載放到了同一個頁面的另一個frame的top.jsp,這樣登陸的時候只需要下載一次緩存這個控件,之后workspace.jsp通過top.jsp所在的frame訪問到該ocx控件。工作區頁面的顯示速度又恢復了原來的樣子。
問題總結:
1、要相信直覺的力量,發現問題及時定位,不能攢到一起,再排地雷
2、要聯系該模塊最近的變更,及時找到問題的根源。
3、要注意收集總結這樣的案例,以備將來的性能改造。