星期五, 6月 16, 2006

MyEclipse - JSF Spring Hibernate備忘記

這次主要使用MyEclpse對Spring Hibernate JSF作簡單的備忘過程,


首先自行安裝 jdk, tomcat, mysql, eclipse.
http://java.sun.com/j2se/1.5.0/download.jsp
http://jakarta.apache.org/site/downloads/downloads_tomcat-5.html
http://dev.mysql.com/downloads/mysql/5.0.html
http://www.eclipse.org/downloads/


在Mysql建立資料庫及表格:
DB user: root password: rootpassword
create database mysurvey;
create table survey(
id int(10) not null auto_increment,
name varchar(50) not null,
email varchar(30) not null,
result varchar(1) not null,
primary key(id)
);


安裝MyEclipse:
http://www.myeclipseide.com/ContentExpress-display-ceid-10.html


安裝Eclipse Property File GUI Editor:
http://sourceforge.net/project/showfiles.php?group_id=117908


建立Web project:
File -> New -> project ->> MyEclipse -> J2EE Project -> Web Project ->> Next
輸入Project Name : MySurvey -> Finish
建主package ( 方便日後便用MyEclipse Database generate Persistant Bean ):
選MySurvey -> src -> New -> Package ->>
Name : com.joeyta.mysurvey -> Finish


然後download mysql jdbc driver 放在 MySurvey -> WebRoot -> WEB-INF -> lib :
http://dev.mysql.com/downloads/connector/j/3.1.html


建立 DB Profile (方便日後Hibernate使用 及 Generate Beans):
Window -> Open Perspective -> other ->> MyEclipse Database Explorer
DB Browser View -> New
Profile name: mysurvey
Driver: MMMySQL Driver
URL: jdbc:mysql://localhost:3306/mysurvey ( 在Mysql裡要先建立mysurvey database )
User name: root
Password: rootpassword
Open on Eclipse startup: unchecked
Prompt for password: unchecked
-> Next
Display the selected schemas: checked
按 Add -> check mysurvey -> Finish


Generates Persistants Beans:
DB Browser View -> mysurvey -> Open Connection
->> Connected to mysurvey ->mysurvey -> TABLE -> survey
->> Create Hibernate Mapping
Java src folder: /MySurvey/src
java package: com.joeyta.mysurvey
Update hibernate configuration with mapping file location
-> Finish
打開 Package Explorer -> MySurvey -> src -> com.joeyta.mysurvey
就會看到兩個java ( AbstractSurvey.java, Survey.java ) 及 1個mapping file ( Survey.hbm.xml ).
並將mapping file update 到 Hibernate.cfg.xml
( 平時玩Hibernate,通常玩的是mapping files -> beans 及 mapping files -> table scrip,很少玩tablescript > beans )


修改 Survey.hbm.xml :
由於generate 出來的Survey.hbm.xml 裡 class 裡多了catalog property,這會影響SQL.
在<generator class="assigned" /> 這個不是我會想要的,由於mysql中的id是auto_increment,故需要改成native
<!--------------------- Survey.hbm.xml ------------------------------------------>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<hibernate-mapping>
<class name="com.joeyta.mysurvey.Survey" table="survey">
<id name="id" type="integer">
<column name="id" />
<generator class="native" />
</id>
<property name="name" type="string">
<column name="name" length="50" not-null="true" />
</property>
<property name="email" type="string">
<column name="email" length="30" not-null="true" />
</property>
<property name="result" type="string">
<column name="result" length="1" not-null="true" />
</property>
</class>
</hibernate-mapping>
<!--------------------- Survey.hbm.xml ------------------------------------------>


加入 JSF Capabilities:
點選 MySurvey -> MyEclipse -> Add JSF Capabilities
JSF Implementation : MyFaces 1.1.1 -> Finish
有點奇怪的是有些library沒有把它加到WEB-INF/lib裡,
尤其是以下這三個,
jsf-api.jar, jsf-impl.jar, standard.jar
可以到MyEclipse library folder找出來或到
http://java.sun.com/javaee/javaserverfaces/


加入 Hibernate Capabilities:
點選 MySurvey -> MyEclipse -> Add Hibernate Capabilities
Hibernate Specification: Hibernate 3
JAR Library Installation: Copy checked Library Jars to project folder and add to build-path
-> Next
Hibernate config file: New
-> Next
Specify database connection details?: checked
DataSource: Use JDBC Driver
DB Profile: mysurvey ( 這裡就是剛剛建立的DB Profile )
-> Next
Create SessionFactory class: unchecked -> Finish


hibernate.cfg.xml :
<!------------------------- hibernate.cfg.xml ------------------------------->
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">


<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>


<session-factory>
<property name="connection.username">root</property>
<property name="connection.url">jdbc:mysql://localhost:3306/mysurvey</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="myeclipse.connection.profile">mysurvey</property>
<property name="connection.password">rootpassword</property>
<property name="connection.driver_class">org.gjt.mm.mysql.Driver</property>
<mapping resource="com/joeyta/mysurvey/Survey.hbm.xml"></mapping>


</session-factory>


</hibernate-configuration>
<!------------------------- hibernate.cfg.xml ------------------------------->


加入 Spring Capabilities:
點選 MySurvey -> MyEclipse -> Add Spring Capabilities
Show MyEclipse Libraries : checked
並選下面這些 libraries:
Spring 1.2 AOP Libraries
Spring 1.2 Core Libraries
Spring 1.2 ORM/DAO/Hibernate3 Libraries
spring 1.2 Web Libraries
Jar Library Installation: Copy checked Library contents to project folder
Library Folder: /WebRoot/WEB-INF/lib
-> Next
Specify new or existing Spring bean configuration file? checked
-> Next
Create Spring LocalSessionFactory that references the hibernate config using a configLocation property? checked
Spring Config: src/applicationContext.xml
SessionFactory id: sessionFactory
-> Finish


建立log4j.properties:
MySurvey -> src -> New -> File
File name: log4j.properties
############# log4j.properties #############
# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=INFO, A1


# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender


# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n


com.mchange.v2.log.MLog=com.mchange.v2.log.log4j.Log4jMLog
log4j.logger.mo.org.cpttm.workflow=INFO
############# log4j.properties #############


建立DAO object :
( 日後利用Spring AOP, JSF就能存取DAO Object,
這裡為求簡便,不打算利用Spring AOP處理Transaction )
/*********** ISurveyDAO.java **********/
package com.joeyta.mysurvey;
public interface ISurveyDAO {
public void insert(Survey survey);
public Survey find(Integer id);
public boolean isExist(String name, String email);
}
/*********** ISurveyDAO.java **********/


/*********** SurveyDAO.java **********/
package com.joeyta.mysurvey;
import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class SurveyDAO extends HibernateDaoSupport implements
ISurveyDAO {


public SurveyDAO() {
super();
}


public void insert(Survey survey) {
getHibernateTemplate().save(survey);
}


public Survey find(Integer id) {
Survey survey = (Survey) getHibernateTemplate()
.get(Survey.class, id);
return survey;
}


public boolean isExist(String name, String email) {
List list = getHibernateTemplate().find(
"from survey s where s.name='"+name+"' or s.email='"+email+"'");
return list.size() > 0;
}

}
/*********** SurveyDAO.java **********/



修改 applicationContext.xml :
<!--------------------- applicationContext.xml ---------------------------->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">


<beans>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation">
<value>/WEB-INF/classes/hibernate.cfg.xml</value>
</property>
</bean>
<bean id="surveyDAO" class="com.joeyta.mysurvey.SurveyDAO">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
</beans>
<!--------------------- applicationContext.xml ---------------------------->


修改 Survey.java :
/********************* Survey ************************************/
package com.joeyta.mysurvey;


import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;


public class Survey extends AbstractSurvey implements java.io.Serializable {
private static final long serialVersionUID = -6603171231966815078L;


public Survey() {
}


public Survey(Integer id, String name, String email, String result) {
super(id, name, email, result);
}


private String locale = "zh_TW";


private String message;


private SurveyDAO surveyDAO;


public String getParameter(String name, String sDefaultValue) {
if (FacesContext.getCurrentInstance().getExternalContext()
.getRequestParameterMap().get(name) != null) {
return (String) FacesContext.getCurrentInstance()
.getExternalContext().getRequestParameterMap().get(name);
}
return sDefaultValue;
}


public String submit() {
if (surveyDAO.isExist(getName(), getEmail())) {
setMessage("每位會員只能參加一次!");
return "failure";
} else {
setMessage("多謝閣下參與!");
this.surveyDAO.insert(this);
return "success";
}
}


public void changeLocale(ValueChangeEvent event) {
if (locale.equals("zh_TW"))
locale = "zh_CN";
else
locale = "zh_TW";
}


public String getLocale() {
return locale;
}


public void setLocale(String locale) {
this.locale = locale;
}


public String getMessage() {
return message;
}


public void setMessage(String message) {
this.message = message;
}


public SurveyDAO getSurveyDAO() {
return surveyDAO;
}


public void setSurveyDAO(SurveyDAO surveyDAO) {
this.surveyDAO = surveyDAO;
}
}


/********************* Survey ************************************/


建立messages.properties:
MySurvey -> src -> New -> File
File name: messages.properties
Right click messages.properties -> Open With -> Property File GUI Editor
( 由於剛剛將了Property File GUI Editor )
打開後按 Add locale
Local: zh_TW -> OK
再按 Add locale
Local: zh_CN -> OK
儲存後就會看到多出兩個檔案 messages_zh_TW.properties 及 messages_zh_CN.properties
其實這三個檔案就會出來國際化訊息使用,即支持繁簡體.
################## messages.properties #####################
titleText=\u6700\u4F73\u54E1\u5DE5\u9078\u8209


zh_TWText=\u7E41\u9AD4


q1=\u6700\u4F73\u54E1\u5DE5(\u6295\u6CE8\u54E1)


q5=\u6700\u4F73\u54E1\u5DE5(\u6295\u6CE8\u54E1)


q4=\u67E5\u8A62\u53CA\u51FA\u5165\u91D1\u6700\u4F73\u54E1\u5DE5


q3=\u5C08\u8CAC\u5C0F\u7D44\u6700\u4F73\u54E1\u5DE5


q2=\u6700\u4F73\u5BA2\u6236\u4E3B\u4EFB


cnText=\u7C21\u9AD4


name=\u59D3\u540D


idcard=\u8EAB\u4EFD\u8B49\u865F\u78BC


slotid=\u4E92\u806F\u901A\u6236\u53E3\u865F\u78BC


tel=\u806F\u7D61\u96FB\u8A71


address=\u5730\u5740


submit=\u9001\u51FA
################## messages.properties #####################


################## messages_zh_TW.properties #####################
titleText=\u6700\u4F73\u54E1\u5DE5\u9078\u8209


zh_TWText=\u7E41\u9AD4


q1=\u6700\u4F73\u54E1\u5DE5(\u6295\u6CE8\u54E1)


q5=\u6700\u4F73\u54E1\u5DE5(\u6295\u6CE8\u54E1)


q4=\u67E5\u8A62\u53CA\u51FA\u5165\u91D1\u6700\u4F73\u54E1\u5DE5


q3=\u5C08\u8CAC\u5C0F\u7D44\u6700\u4F73\u54E1\u5DE5


q2=\u6700\u4F73\u5BA2\u6236\u4E3B\u4EFB


cnText=\u7C21\u9AD4


name=\u59D3\u540D


idcard=\u8EAB\u4EFD\u8B49\u865F\u78BC


slotid=\u4E92\u806F\u901A\u6236\u53E3\u865F\u78BC


tel=\u806F\u7D61\u96FB\u8A71


address=\u5730\u5740


submit=\u9001\u51FA



################## messages_zh_TW.properties #####################


################## messages_zh_CN.properties #####################
titleText=\u6700\u4F73\u5458\u5DE5\u9009\u4E3E


zh_TWText=\u7E41\u4F53


q1=\u6700\u4F73\u5458\u5DE5(\u6295\u6CE8\u5458)


q5=\u6700\u4F73\u5458\u5DE5(\u6295\u6CE8\u5458)


q4=\u67E5\u8BE2\u53CA\u51FA\u5165\u91D1\u6700\u4F73\u5458\u5DE5


q3=\u4E13\u8D23\u5C0F\u7EC4\u6700\u4F73\u5458\u5DE5


q2=\u6700\u4F73\u5BA2\u6237\u4E3B\u4EFB


cnText=\u7B80\u4F53


name=\u59D3\u540D


idcard=\u8EAB\u4EFD\u8BC1\u53F7\u7801


slotid=\u4E92\u8054\u901A\u6237\u53E3\u53F7\u7801


tel=\u8054\u7EDC\u7535\u8BDD


address=\u5730\u5740


submit=\u9001\u51FA
################## messages_zh_CN.properties #####################



建立 survey.jsp :
Mysurvey -> New -> JSP
File Path: /MySurvey/WebRoot/survey
File Name survey.jsp
Template to use: [6] Default JSF template


<%---------------------- survey.jsp ---------------------------%>
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>


<%String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>


<f:view locale="#{survey.locale}">
<f:loadBundle basename="messages" var="msg" />
<html>
<head>
<base href="<%=basePath%>">


<title><h:outputText value="#{msg.titleText}" /></title>


<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>


<body>
<h:outputText value="#{survey.message}" />
<h:form>
<h:selectOneMenu value="#{survey.locale}" immediate="true"
onchange="this.form.submit();" valueChangeListener="#{survey.changeLocale}">
<f:selectItem itemValue="zh_TW" itemLabel="#{msg.zh_TWText}" />
<f:selectItem itemValue="zh_CN" itemLabel="#{msg.cnText}" />
</h:selectOneMenu>
<br />


<TABLE border="1" width="500px">
<TR>
<TD><h:outputText value="#{msg.name}" /></TD>
<TD>
<h:inputText rendered="true" required="true" value="#{survey.name}" />
</TD>
</TR>
<TR>
<TD><h:outputText value="#{msg.email}" /></TD>
<TD>
<h:inputText rendered="true" required="true" value="#{survey.email}" />
</TD>
</TR>
<TR>
<TD><h:outputText value="#{msg.result}" /></TD>
<TD>
<h:inputText rendered="true" required="true" value="#{survey.result}" />
</TD>
</TR>
<TR>
<TD colspan="2">
<h:commandButton value="#{msg.submit}" rendered="true" action="#{survey.submit}" />
</TD>
</TR>
</TABLE>
</h:form>
</body>
</html>
</f:view>
<%---------------------- survey.jsp ---------------------------%>


修改 faces-config.xml
<!----------------------- faces-config.xml ------------------------>
<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
<faces-config>
<application>
<locale-config>
<default-locale>zh_TW</default-locale>
<supported-locale>zh_CN</supported-locale>
</locale-config>
<variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
</application>


<navigation-rule>
<from-view-id>/survey/survey.jsp</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/survey/survey.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>failure</from-outcome>
<to-view-id>/survey/survey.jsp</to-view-id>
</navigation-case>
</navigation-rule>


<managed-bean>
<managed-bean-name>survey</managed-bean-name>
<managed-bean-class>com.joeyta.mysurvey.Survey</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>surveyDAO</property-name>
<value>#{surveyDAO}</value>
</managed-property>
</managed-bean>


</faces-config>
<!----------------------- faces-config.xml ------------------------>


建立tomcat deployment descriptor
D:\Apache Group\Tomcat 5.0\conf\Catalina\localhost\mysurvey.xml
<Context path="/mysurvey" docBase="D:\eclipse\workspace\MySurvey\WebRoot" reloadable="true"></Context>
<!--------
D:\Apache Group\Tomcat 5.0\ 為 tomcat 安裝位置
D:\eclipse\workspace\MySurvey 為 MySurvey eclipse project 位置
----------->


修改 web.xml
<!----------------------- web.xml ------------------------>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<description>JSF Demo</description>
<display-name>JSF Demo</display-name>


<session-config>
<session-timeout>30</session-timeout>
</session-config>


<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>


<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>


<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/faces-config.xml</param-value>
</context-param>
<listener>
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
<filter>
<filter-name>MyFacesExtensionsFilter</filter-name>
<filter-class>org.apache.myfaces.component.html.util.ExtensionsFilter</filter-class>
<init-param>
<param-name>maxFileSize</param-name>
<param-value>20m</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MyFacesExtensionsFilter</filter-name>
<url-pattern>*.faces</url-pattern>
</filter-mapping>
</web-app>
<!----------------------- web.xml ------------------------>


打開tomcat 及 IE:
http://localhost:8083/mysurvey/survey/survey.faces

java security備忘記

在SDI(http://www2.cpttm.org.mo/cyberlab/SDI/2005-001/index.html.en)的開發的過程中.
我覺得比較值得記錄下來的是security的部份,這裡只記下generate keys及一些測試程式.
而原本的PKCS12 storetype , 我把它改成 JKS.
在security方面,Applet 及 Web communication的部份我覺得比較容易,就沒有記下來.


開始備忘記:
首先要安裝jdk.


產生keystore:
[joeyta]$keytool -genkey -alias joeyta -keystore joeyta.ks -keyalg RSA -sigalg SHA1withRSA
( 在 java 裡, 預設的 keystore type 為 JKS )
其他的看需要輸入,但下面這兩個password就要記住
keystore password 輸入 123456
key password 輸入 234567
就會產生 joeyta.ks


顯示keystore list
輸入 keystore password 為 123456
[joeyta]$keytool -list -v -keystore joeyta.ks


產生certificate request file
[joeyta]$keytool -certreq -alias joeyta -keystore joeyta.ks -file joeyta.csr
輸入 keystore password 為 123456
輸入 key password 234567
就會產生 joeyta.ks


安裝openssl,
windows os 在
http://www.openssl.org/related/binaries.html 下載
( windows os 下需設 environment parameter 為 set RANDFILE=rand )
本人使用 Fedora Core release 3 (Heidelberg), 預設已安裝
輸入
[joeyta]$rpm -qa grep openssl
出現:
openssl-0.9.7a-20.2
openssl-devel-0.9.7a-20.2
如果沒有顯示,即沒有安裝,那就可以到
http://search.rpmseek.com/search.html 去找一下
( rpm -ivh
http://rpmpath ]


產生CA自簽的private key
[joeyta]$openssl req -new -keyout cakey.pem -out careq.pem
輸入 PEM pass phrase 為 capassword
其他的看需要輸入.
就會產生 cakey.pem 及 careq.pem


找出openssl設定檔:
[joeyta]$rpm -ql openssl grep cnf
出現:
/usr/share/ssl/openssl.cnf
( windows os 則在安裝目錄下的bin\openssl.cnf , 如 c:\OpenSSL\bin\openssl.cnf )


產生自簽certificate:
[joeyta]$openssl x509 -signkey cakey.pem -req -days 365 -in careq.pem -out cacert.pem -extfile /usr/share/ssl/openssl.cnf -extensions v3_ca
輸入 pass phrase 為 capassword
就會產生 cacert.pem


產生serial file:
[joeyta]$echo 02 > cacert.srl


產生CA sign certificate:
[joeyta]$openssl x509 -CA cacert.pem -CAkey cakey.pem -CAserial cacert.srl -req -in joeyta.csr -out joeyta.cer -days 365
輸入 pass phrase 為 capassword
就會產生 joeyta.cert


Import trusted CA certificate:
( 如果keystore type為 PKCS12 則不能 import trusted certificate, 因為 PKCS12 只能儲存 private keys )
[joeyta]$keytool -import -alias testCA -file cacert.pem -keystore joeyta.ks
keystore password 輸入 123456
Trust this certificate? [no]: 輸入 yes
[joeyta]$keytool -list -v -keystore joeyta.ks


這裡有不錯的文檔供查詢.
http://download.java.net/jdk6/docs/guide/security/jsse/JSSERefGuide.html
http://www.openssl.org/docs/



產生所有需要的 keystore 後, 可以開始進入寫程式了.
本人使用 Eclipse IDE
建立 java project, 命名為 javasecurity
點選 javasecurity > properties > Java Build Path
進入 Source 面版
點選 Add Foler 為 src
進入 Libraries 面版
點選 Add Extenals JARs , 將 junit_3.8.1.jar 加入來 (可在eclipse plugin目錄下找到), 然後按 ok
將剛剛產生的所有 keys 丟進 src 目錄下.


程式:
KeyManager.java // 主程式
KeyManagerTest.java // 測試程式



/* KeyManager.java */
package testca;


import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;


public class KeyManager {
private static String signAlgorithm = "SHA1withRSA";
private static KeyManager keyManager;

public String getSignAlgorithm() {
return signAlgorithm;
}


public PrivateKey loadPrivateKey(InputStream inputStream, String alias,
char[] keyStorePassword, char[] privatePassword)
throws KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException, UnrecoverableKeyException {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(inputStream, keyStorePassword);
return (PrivateKey) ks.getKey(alias, privatePassword);
}


public PublicKey loadPublicKey(InputStream inputStream, String alias,
char[] keyStorePassword) throws KeyStoreException,
NoSuchAlgorithmException, CertificateException, IOException,
UnrecoverableKeyException {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(inputStream, keyStorePassword);
return (PublicKey) ks.getCertificate(alias).getPublicKey();
}


public byte[] sign(byte[] data, PrivateKey privateKey)
throws NoSuchAlgorithmException, InvalidKeyException,
SignatureException {
Signature s = Signature.getInstance(getSignAlgorithm());
s.initSign(privateKey);
s.update(data);
return s.sign();
}


public boolean verify(byte[] data, PublicKey publicKey, byte[] signature)
throws NoSuchAlgorithmException, InvalidKeyException,
SignatureException {
Signature s = Signature.getInstance(getSignAlgorithm());
s.initVerify(publicKey);
s.update(data);
return s.verify(signature);
}


private KeyManager(){

}
public static KeyManager getInstance(){
if (keyManager == null) {
keyManager = new KeyManager();
}
return keyManager;

}

}
/* KeyManager.java */



/* KeyManagerTest.java */
package testca;


import java.io.InputStream;
import java.security.PrivateKey;
import java.security.PublicKey;


import junit.framework.TestCase;


public class KeyManagerTest extends TestCase {
public void testSuccessSignature() throws Exception {
KeyManager keyManager = KeyManager.getInstance();
String data = "this is the plain text";
InputStream inputStream = getClass().getClassLoader()
.getResourceAsStream("joeyta.ks");
PrivateKey privateKey = keyManager.loadPrivateKey(inputStream,
"joeyta", "123456".toCharArray(), "234567".toCharArray());
byte[] signData = keyManager.sign(data.getBytes(), privateKey);
inputStream = getClass().getClassLoader().getResourceAsStream(
"joeyta.ks");
PublicKey publicKey = keyManager.loadPublicKey(inputStream, "joeyta",
"123456".toCharArray());
assertTrue(keyManager.verify(data.getBytes(), publicKey, signData));
}


public void testFailSignature() throws Exception {
KeyManager keyManager = KeyManager.getInstance();
String data = "this is the plain text";
String data2 = "This is the plain fail text";
InputStream inputStream = getClass().getClassLoader()
.getResourceAsStream("joeyta.ks");
PrivateKey privateKey = keyManager.loadPrivateKey(inputStream,
"joeyta", "123456".toCharArray(), "234567".toCharArray());
byte[] signData = keyManager.sign(data.getBytes(), privateKey);
inputStream = getClass().getClassLoader().getResourceAsStream(
"joeyta.ks");
PublicKey publicKey = keyManager.loadPublicKey(inputStream, "joeyta",
"123456".toCharArray());
assertFalse(keyManager.verify(data2.getBytes(), publicKey, signData));
}


}
/* KeyManagerTest.java */


// 我覺得這些程式非常簡單, 所以沒有作詳細的解釋
/* Explanation 1 */
public PrivateKey loadPrivateKey(InputStream inputStream, String alias,
char[] keyStorePassword, char[] privatePassword)
throws KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException, UnrecoverableKeyException { // 此method 返回 private key
KeyStore ks = KeyStore.getInstance("JKS"); // 定義 keystore type 為 JKS
ks.load(inputStream, keyStorePassword); // load keystore, keyStorePassword 為 當初設定的 123456
return (PrivateKey) ks.getKey(alias, privatePassword);
}

public PublicKey loadPublicKey(InputStream inputStream, String alias,
char[] keyStorePassword) throws KeyStoreException,
NoSuchAlgorithmException, CertificateException, IOException,
UnrecoverableKeyException { // 與上面類似, 但返回 public key
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(inputStream, keyStorePassword);
return (PublicKey) ks.getCertificate(alias).getPublicKey();
}
/* Explanation 1 */



/* Explanation 2 */
public byte[] sign(byte[] data, PrivateKey privateKey)
throws NoSuchAlgorithmException, InvalidKeyException,
SignatureException { // 此method 返回sign完的signature
Signature s = Signature.getInstance(getSignAlgorithm());
s.initSign(privateKey);
s.update(data);
return s.sign();
}


public boolean verify(byte[] data, PublicKey publicKey, byte[] signature)
throws NoSuchAlgorithmException, InvalidKeyException,
SignatureException { // 此method 返回是否verify
Signature s = Signature.getInstance(getSignAlgorithm());
s.initVerify(publicKey);
s.update(data);
return s.verify(signature);
}


private KeyManager(){}

public static KeyManager getInstance(){ // 這只是singleton
if (keyManager == null) {
keyManager = new KeyManager();
}
return keyManager;

}
/* Explanation 2 */



/* Explanation 3 */
public void testSuccessSignature() throws Exception {
KeyManager keyManager = KeyManager.getInstance();
String data = "this is the plain text";
InputStream inputStream = getClass().getClassLoader()
.getResourceAsStream("joeyta.ks"); // 返回keystore
PrivateKey privateKey = keyManager.loadPrivateKey(inputStream,
"joeyta", "123456".toCharArray(), "234567".toCharArray()); // 返回private key
byte[] signData = keyManager.sign(data.getBytes(), privateKey); // 返回signature data
inputStream = getClass().getClassLoader().getResourceAsStream(
"joeyta.ks");
PublicKey publicKey = keyManager.loadPublicKey(inputStream, "joeyta", // 返回public key
"123456".toCharArray());
assertTrue(keyManager.verify(data.getBytes(), publicKey, signData)); // 檢查是否verify
}
/* Explanation 3 */

使用 KeyStore Explorer 瀏覽:
運行 KeyStore Explorer jre 必須 1.5 或以上版本.

其功能為:
KeyStore saving.
Key pair export.
Private key export.
CSR signing.
JAR file signing.
MIDlet signing.


下載10天試用期版本 kse-30-manual.zip :
http://www.lazgosoftware.com/kse/downloads.html
按步驟輸入 email, 就會從 email 取得 download path 如下:
http://www.lazgosoftware.com/kse/downloads/44b2f9e3-2fe40575/kse-30-manual.zip


解壓至 D:\kse 下, 執行:
java -jar D:\kse\kse.jar
就會出現如下畫面:


選擇 File -> Open 搜尋 joeyta.ks,
輸入上面已設定的 keystore password:123456
就能以 GUI 的形式修改 keystore 內容.


下面有一些官方的教學:
http://www.lazgosoftware.com/kse/support-start.html

JDeveloper(Struts & JSF)備忘記

Oracle Jdeveloper 10g 是開發 J2EE Struts 或 JSF 的優良IDE,
它以組件為概念, 就像開發桌面應用程式般簡單 ( 像使用Delphi開發 ),
它內置了 embedded OC4J , 使開發及測試環境更加輕鬆.
在開發階段不用煩惱怎樣配置容器, 使初學者更快進入狀況.

Jdeveloper 10g的新能在下面連結裡官方有詳細說明:
http://www.oracle.com/technology/products/jdev/collateral/1013newfeatures.htm#New%20look%20and%20feel

開始備忘記:
雖然整個備忘過程需要配合大量圖片說明, 但由於數量太多,
故這裡只簡單地用文字表達, 以求備忘過程更簡潔.

安裝JDK


安裝 Oracle 10g requirement:
Windows NT Server 4.0, Windows XP Profectional, Windows 2000 sp1, Windows Server 2003
RAM 512MB 以上, 本人覺得 512MB 太慢了, 最好是 1 GB, Virtual memory 為 RAM 的2倍
Hard disk 1.5 GB 以上


下載 Oracle 10g: http://download.oracle.com/otn/nt/oracle10g/10201/10201_database_win32.zip
Administrator登入後解壓後直接安裝, Oracle Universal Install 會指導整個過程
Global database name 為 odb.mydomain.com , SID 為 ORCL
設定所有帳戶使用同一個password 為 abc123
安裝完成後,
使用 http://odb.mydomain.com:5500/em 進入 Enterprise Manager
使用 http://odb.mydomain.com:5560/isqlplus 進入 iSQL*Plus



安裝 JDeveloper:
下載 http://download.oracle.com/otn/java/jdeveloper/1013/jdevstudio1013.zip
不用安裝直接將它解壓至C:\jdevstudio1013 並點選目錄下的 jdeveloper.exe 執行
第一次執行時 會出現Would you like to migrate from a previous version of JDeveloper?選擇否.


在 Application Navigator panel 選擇 Applications 右鍵 選 new Application,
輸入 AppTest 後按確定, project 為 Project1,
點選 Project1 按 new 選 Simple File > Java Class
Name 為 HelloWorld 點選 Generate Main Method


更改程式為:
package dew;


public class HelloWorld {
public HelloWorld() {
System.out.println("Hello World");
}


public static void main(String[] args) {
HelloWorld helloWorld = new HelloWorld();
}
}


按 F11 或 Run 就會看到輸出 Hello World



JDeveloper 是 Oracle的產品,對資料庫有很好的整合.
選擇 Connections Navigator > Database > New Database Connection > Next
Connection Name 為 ORCLConn, Connection Type 為 Oracle (JDBC)
[ 這裡亦可選擇其它的資料庫Driver ],按 Next
Username 為 sys , Password 為 abc123, Role 為 sysdba , 按 Next
Driver 選 thin
Host Name 為 odb.mydomain.com
JDBC Port 為 1521
SID 為 ORCL
按Next 後, 再按 Test Connnection 會 出現 Success!, 然後按完成,


創建用戶:
點選 ORCLConn > new > Database Objects > User
User Name 為 tester
password 為 tester
Confirm Password 為 tester
Default Tablespace 為 USERS
Temporary Tablespace 為 TEMP
按 確定


變更資料庫連接用戶:
點選 ORCLConn > properties > Authentication
Username 改為 tester
password 改為 tester
Role 改為 Normal
刪除 Role
點選 Test panel 並按 "Test Connnection" 會出現 Success!


建立表格:
展開 ORCLConn 後可看到 TESTER 的 Schema 及裡面的Objects
按 Tables > New Table > Next
Schema 選 TESTER
Name 為 DEPARTMENT > Next
按 "+" , Name 填 DEPARTMENT_ID , Type 選 NUMBER, size 填 10
再按 "+", Name 填 DEPARTMENT, Type 選 VARCHAR2, size 填 100
再按 "+", Name 填 DESCRIPTION, Type 選 VARCHAR2, size 填 200
按 Next, 選 DEPARTMENT_ID 為 primary key , 一直按 Next 及完成
展開 Tables 就會看到 DEPARTMENT TABLE


再建立表格 EMPLOYEE
Name 為 EMPLOYEE > Next
按 "+" , Name 填 EMPLOYEE_ID , Type 選 NUMBER, size 填 10
再按 "+", Name 填 NAME, Type 選 VARCHAR2, size 填 50
再按 "+", Name 填 EMAIL, Type 選 VARCHAR2, size 填 300
再按 "+", Name 填 DEPARTMENT_ID, Type 選 NUMBER, size 填 10
按 Next , 選 EMPLOYEE_ID 為 primary key ,
按 Next > Next , 現在設定 Foreign Keys, 點選 Add
Refereneced Table 選 Department, Local Column 選 DEPARTMENT_ID,
Referenced Column on DEPARTMENT 選 DEPARTMENT_ID, 然後按完成


展開 Tables ,點擊 DEPARTMENT 及在 Editor下方選 Data
按 "+", DEPARTMENT_ID 為 1, DEPARTMANT 為 IT, DESCRIPTION 為 INFORMATION TECHNOLOGY
再按 "+", DEPARTMENT_ID 為 2, DEPARTMANT 為 HR, DESCRIPTION 為 HUMAN RESOURCE
再按 "+", DEPARTMENT_ID 為 3, DEPARTMANT 為 MKT, DESCRIPTION 為 MARKETING
然後選 Commit Changes, 資料就會插入資料庫


點擊 EMPLOYEE 及選 Editor下方選 Data
按 "+", ID 為 1, NAME 為 joeyta, EMAIL 為 joeyta@joeyta.com, DEPARTMENT_ID 為 1
再按 "+", ID 為 2, NAME 為 peter, EMAIL 為 peter@joeyta.com, DEPARTMENT_ID 為 1
再按 "+", ID 為 3, NAME 為 mary, EMAIL 為 mary@joeyta.com, DEPARTMENT_ID 為 1
再按 "+", ID 為 4, NAME 為 luis, EMAIL 為 luis@joeyta.com, DEPARTMENT_ID 為 2
再按 "+", ID 為 5, NAME 為 ann, EMAIL 為 ann@joeyta.com, DEPARTMENT_ID 為 2
再按 "+", ID 為 6, NAME 為 suzana, EMAIL 為 suzana@joeyta.com, DEPARTMENT_ID 為 3
然後選 Commit Changes, 資料就會插入資料庫


建立Stored Procedure:
點選 Connections Navigator > Database > ORCLConn > TESTER
右建選 Procedures > new PL/SQL Procedure, Object Name 輸入 SP_GEN_EMPLOYEE 按確定
內容為:
PROCEDURE SP_GEN_EMPLOYEE (iDepartment_id IN NUMBER)
IS
cursor emp_cur IS
select * from employee
where department_id = iDepartment_id
order by employee_id;
emp_rec emp_cur%ROWTYPE;
BEGIN
open emp_cur;
loop
fetch emp_cur into emp_rec;
exit when emp_cur%notfound;
DBMS_OUTPUT.PUT_LINE(emp_rec.employee_id chr(9)
emp_rec.name chr(9)
emp_rec.department_id);
end loop;
close emp_cur;


END;


點選 Procedures > SP_GEN_EMPLOYEE > RUN , 輸出結果為
JDeveloper 會產生一個 anonymous的 PL/SQL Block 作測試
把內容更改為:
DECLARE
IDEPARTMENT_ID NUMBER;
BEGIN
IDEPARTMENT_ID := 1;


SP_GEN_EMPLOYEE(
IDEPARTMENT_ID => IDEPARTMENT_ID
);
END;


按確定後輸出為:
Connecting to the database ORCLConn.
1 joeyta 1
2 peter 1
3 mary 1
Process exited.
Disconnecting from the database ORCLConn.



安裝及啟動OC4J:
JDeveloper 裡已內含 OC4J (Oracle Container for J2EE)
為 Standalone OC4J 及 Embedded OC4J Server ( Embedded OC4J 為開發使用 )
進入 C:\jdevstudio1013\jdev\bin
執行 C:\jdevstudio1013\jdev\bin>start_oc4j
OC4J 的預設 Administrator 為 oc4jadmin
第一次執行時會要求輸入password, 輸入 password 為 admin
設定後,現在關閉OC4J可執行
C:\jdevstudio1013\jdev\bin>stop_oc4j oc4jadmin admin
再次執行 C:\jdevstudio1013\jdev\bin>start_oc4j
在IE裡輸入 http://localhost:8888/ , 按右邊的 Launch 連結 進入Applecation Server Control
使用者名稱為 oc4jadmin , password 為 admin , 登入後可對 OC4J 進行管理


JDeveloper設定Application Server:
點選 Connections Navigator > Application Server > New Application Server Connection > Next
Connection Name 輸入 AppServerConnection, Connection Type 選 Standalone OC4J 10g 10.1.3
( 這裡亦可以選其他的Application server )
按 Next, Username 為 oc4jadmin, Password 為 admin
按 Next, Host Name 為 localhost, 按 Next 及 按Test Connection 成功後會出現 Success!
最後按 完成



建立 ADF Model (Application Development Framework):
點選 "Application Navigator" panel > New Application
Application Name 為 AppProject
Application Template 選 Java Application [Swing, ADF BC], 按確定
展開AppProject > Model > New > ADF Business Components > Business Components from Tables
Connection 選 ORCLConn , 按確定 後 出現另一個wizard, 然後按 Next
設定 new entities objects and associations:
在 Available 的方塊裡 按 Ctrl 點選 Department 及 Employee 再按 ">" , 再按 Next
設定 new view objects and view links:
創建 Object 及 View 都是
在 Available 的方塊裡 按 Ctrl 點選 Department 及 Employee 再按 ">" , 再按 Next
填入 Name 為 DeptEmpModule 及 按 Next 及 選擇 Business Components Diagram 及 再按 Next > 完成
展開 Application Navigator > AppProject > Model > Application Sources
裡面包含了 Oracle ADF 相關的檔案及設定檔
( Entity Object, Association, View Object, View Link, Application Module )
測試module 及 管理資料:
展開AppProject > Model > Application Sources > dew.model > DeptEmpModule > test
就可以打開 ADF BC 的 Oracle Business Componets Browser 去測試一下數據


建立UI:
點選AppProject > View 選 New > ADF Swing 選 Form 按確定 及 Next 需要進行 7個Step
step 1:
選擇 Mater-Detail Tables 及 Form
step 2(Master 及 Detail Template): 使用預設值
step 3(Define data model): click New
會出現 Client Data Model Definition Wizard
使用預設值, 按 Next > Next > 完成
step 4, step 5 ,step 6, step 7: 使用預設值 並按 完成
就會建立 ADF Swing Client , 選 View 按Make 及 Run
就會出現 Master-Detail 的 Swing Clint 供 瀏覽,新增,修改,刪除數據



建立 JSP Web Application Project:
選 AppProject > New Project > Projects > Web Project ,
按確定後出現Create Web Project Wizard, 按 Next
step 1: Project Name 為 ViewController
step 2: 選Servlet 2.4/JSP 2.0 (J2EE 1.4)
step 3: 點選 "Add JSP Page"
Next > 完成 就會出現 Create JSP Wizard
step 1: File Name 為 DeptEmp.jsp
step 2, step 3, step 4 使用預設值 > 完成
展開 ViewController > Web Content 就可以看到DeptEmp.jsp
進入 DeptEmp.jsp Edit page, 可隨意輸入文字及改變style
選擇 View > Components palette ,選擇 CSS > JDeveloper
拖到編輯區就會把預設的JDeveloper css 加到 DeptEmp.jsp
選擇 View > Data Control palette , 展開 DeptEmpModuleDataControl
將 DepartmentView1 拖到編輯區 選 Forms > Read-Only Form
再次將 DepartmentView1 拖到編輯區Form裡, 選 Forms > Navigation Buttons
然後點選 DeptEmp.jsp 按 Make 及 Run
JDeveloper 會啟動內置的Embbeded OC4J 及 自動開啟IE
( Embbeded OC4J 是JDeveloper 內置的lighweight container, 用作測試 )
http://192.168.15.21:8988/AppProject-ViewController-context-root/DeptEmp.jsp
可以對數據進行瀏覽


到這裡為此, 感覺使用JDeveloper進行開發Client side, 就像Delphi開發一樣簡單
主要兩種方法:
(1)
Business Service (Connect DB)
ADF Data Controls Model 或 ADF Bindings Model
Contoller (Struts)
ADF UIX 或 JSP Web Client
(2)
Business Service (Connect DB)
ADF Data Controls Model 或 ADF Bindings Model
Swing model Java Client



JDeveloper建主Struts Web Projects:
建立表格:
Connection Navigator > Connections > Database > ORCLConn
展開 ORCLConn 後可看到 TESTER 的 Schema 及裡面的Objects
按 Tables > New Table > Next
step 1: Schema 選 TESTER, Name 為 USERS > Next
step 2:
按 "+" , Name 填 USER_ID , Type 選 VARCHAR2, size 填 10
再按 "+", Name 填 PASSWORD, Type 選 VARCHAR2, size 填 20
再按 "+", Name 填 TEL, Type 選 VARCHAR2, size 填 30
step 3:
選 USER_ID 為 primary key
step 4, step 5, step 6, step 7, step 8 按 Next 及 完成
展開 Tables 就會看到 USERS TABLE


增加 USERS 表數據:
點 ORCLConn 選 SQL WorkSheet , 輸入
insert into users(user_id, password, tel)
values('joeyta', 'joeyta', '99999999');
按左上角 Execute SQL Statement, 在 USERS table 裡就看到新增的數據


建主 Struts Web Project:
Applications > New Application
Application Name 為 LoginWS, Application Template 選 Web Application[JSP, Strues, ADF BC]
展開LoginWS , 可看到 Model 及 ViewController project
點 ViewController 選 Open Struts Page Flow
選 View > Components Palette > Struts Page Flow > Page
把 Page 拖到Struts-config.xml 編輯區, 就會產生 /untitled1.jsp , 將它更改成 /index.jsp
在 Components Palette 拖一個 Action 至編輯區中
就會產生一個 /action1 , 將它更改成 /LoginAction
然後繼續拉兩個 Page 到 編輯區, 改名為 /success.jsp 及 /failure.jsp


Double click /index.jsp 打開其作編輯
選 View > Components Palette > CSS , 把JDeveloper拖到編輯區
然後將 Components Palette 裡的 CSS 切換成 Strut HTML
選 Action 為 /LoginAction.do 按確定
將 Strut HTML 切換成 HTML Common, 然後將Table拖到編輯區
在 Table 第一個column 輸入 "帳號" 及 "密碼"
然後將 Components Palette 裡的 HTML Common 切換成 Strut HTML
拖選 Text Field 至編輯區中, Property 輸入 user_id 後按確定
拖選 Password Field 至編輯區中, Property 輸入 passwd 後按確定
拖選 Submit Button 至編輯區中


Double click /welcome.jsp 進入編輯
輸入 登入成功


Double click /failure.jsp 進入編輯
輸入 登入失敗


切換至 Struts-config.xml 編輯區
點選空白地方按右鍵,選Diagram > Refresh Diagram from All Pages
就會看到 /index.jsp 及 /LoginAction 之間有一條虛線連結一起
然後 點選 Struts Page Flow > Forward
把 /loginAction 連至 /success.jsp 及 /failure.jsp
然後 Double click /loginAction 並按 確定
LoginAction.java 的程式碼就會顯示
切換至 Struts-config.xml 編輯區
然後 點選 Struts Page Flow > Page Link
把 /failure.jsp 與 /index.jsp 連在一起


點選 view > Structure > struts-config > Insert inside struts-config > form-beans
點 form-beans 選 Insert inside form-beans > form-bean
name 為 login , type 選擇 org.apache.struts.action.DynaActionForm
點選 form-bean - login > Insert inside form-beans > form-property
name 為 user_id , type 為 java.lang.String
再點選 form-bean - login > Insert inside form-beans > form-property
name 為 passwd , type 為 java.lang.String


點選 Applications > LoginWS > ViewController > Web Content > WEB-INF > struts-config.xml > properties
選 Action Mappings > Form Bean > name 選 login , 按確定


ADD Oracle JDBC library:
點選 View Controller > Project Properties > Libraries > Add Library > Oracle JDBC > 確定


修改 LoginAction.java 為
public class LoginAction extends Action {
/**This is the main action called from the Struts framework.
* @param mapping The ActionMapping used to select this instance.
* @param form The optional ActionForm bean for this request.
* @param request The HTTP Request we are processing.
* @param response The HTTP Response we are processing.
*/
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws IOException,
ServletException {


String user_id = (String)((DynaActionForm)form).get("user_id");
String passwd = (String)((DynaActionForm)form).get("passwd");
int numColumns = 0;
Connection conn;
try {
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
} catch (SQLException e) {
System.out.print("Driver Error");
}
try {
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl",
"tester", "tester");
Statement stmt = conn.createStatement();
ResultSet rs =
stmt.executeQuery("select count(1) counter from users where user_id = '" +
user_id + "' and password = '" + passwd +
"'");
ResultSetMetaData rmeta = rs.getMetaData();
numColumns = rmeta.getColumnCount();
System.out.println(numColumns);
conn.close();
if (numColumns == 1) {
return mapping.findForward("success");
} else {
return mapping.findForward("failure");
}
} catch (SQLException ce) {
System.out.print(ce);
}
return mapping.findForward("success");
}
}


進入 struts-config.xml 的編輯區 選 /index.jsp > run
就會啟動 Embedded OC4J 並彈出 IE browser, 就可以對登入頁面作測試


JDeveloper建主JSF Web Projects:
在最新版本的JDeveloper已內置了JSF,不需要對JSF做任何設定.
現在直接來建立一個Hello World的Project
File -> New -> Application -> Application
Application Name : FirstJSF
Application Template: Web Application[JSF, ADF BC]


點選FirstJSF -> ViewController -> Web Content 右鍵按New
選 JSP -> JSP
step 1:
File Name: HelloWorld.jsp
step 2: Next
step 3: Choose "JSF Core 1.0" and "JSF HTML 1.0"
from Available Libraries to Selected Libraries.
step 4: Finish


在FirstJSF -> ViewController -> Web Content下會出現HelloWorld.jsp
點選 View -> Component Palette -> JSF Core
把 View 拉到 HelloWorld.jsp裡
點選 View -> Component Palette -> JSF HTML
把 Output Text 拉到 HelloWorld.jsp
然後點選 HelloWorld.jsp 裡的Output Text 按右鍵選 Properties
Value 輸入 Hello World 然後按確定
Right click Helloworld.jsp -> Run
Embedded OC4J 就會自動啟動 並彈出 browser.
在browser裡 就會看到 Hello World.


以下有更多官方的教學:
http://www.oracle.com/technology/obe/obe1013jdev/index.htm

Ruby on Rails備忘記

Ruby on Rails 為開源Web框架,
其主要目的是使開發過程更優美簡單, 更具效率,
開發員能使用個人喜好的配置習慣, 使開發變得更漂亮簡單.

Instant Rails 為一站式安裝 Rails 運行的解決方案, 包含 Ruby, Rails, Apache 及 MySQL,
其預配置一切就緒, 不需要安裝, 只要簡單地解壓到目錄裡就能運行.

開始備忘記:
進入網站 http://instantrails.rubyforge.org/wiki/wiki.pl?Instant_Rails
選擇[Download] 下載 Instant Rails 1.3
http://instantrails.rubyforge.org/wiki/wiki.pl?Getting_Started 裡有安裝的詳細內容
解壓至C:\InstantRails , 點選InstantRails.exe
會自動檢查 apache, mysql, phpmyadmin (Rails裡亦包含這些)


http://localhost/ 進入網站
http://localhost/mysql/ 進入phpmyadmin管理mysql server
(mysql default user為root, 沒有password)


點選 Configure > Windows Hosts file
加入
127.0.0.1 www.mycookbook.com
127.0.0.1 typo


點選 Rails Applications > Manage Rails Applications...
然後選擇 cookbookc 並按下 "Start SCGI" button.
輸入 http:\\www.mycookbook.com\ 進入網站


在玩Rails前,先來認識一下Ruby:
在windows os下, 進入 cd\InstantRails\ruby\bin
執行:ruby -v
查看ruby版本
執行:irb
進入Ruby shell可進行邏輯運算
執行:irb --simple-prompt
然後輸入 print('Joeyta') 就可看到輸出


建主c:\joeyta.rb, 內容為:
print ("My name is:") # 括孤可有可無
puts "Joeyta" # puts的輸出自動輸行
print "What is your name?\n" # double quote可解釋escape字元
puts 'Peter Chan\n' # single quote直接輸出所有字元
print '1+1 ='; puts 1+1 # 傳回 1+1=2
puts "abc"=="abc" # 傳回true
a = "ab"; b = "ab"
puts "ab".eql?"ab" # 傳回true,判斷實際值
puts "ab".equal?"ab" # 傳回false,判斷參考位置
puts 10 > 50 # 傳回false
puts "abcd".index('c') # 傳回2


name = "joeyta";
printf("名: %s\n", name) # printf可作格式化
printf("是否joeyta? %s\n", (name == 'joeyta' ? '是' : '否'))


number=gets.to_i # gets取後輸入,to_i轉成數字,給變數number
puts number


if number == 1 # if表達式
puts '輸入為1'
elsif number == 2
puts '輸入為2'
else
puts '輸入不為1,2'
end


unless number == 1 # number不為1時為true
puts '輸出不為1'
else
puts '輸出為1'
end


case number # case 表達式
when 1
puts 'case 1'
when 2
puts 'case 2'
else
puts 'case 1,2'
end


for i in 0..2
print i,"\n"
end


for element in [0.2, 4, 'joeyta']
print "#{element}\t(#{element.class})\n"
end


(0..2).each {i puts i}
(9..12).each do i puts i end
2.times {puts "joeyta"}
3.times do i puts "peter" end


j = 1000
begin
j -= 1
puts j
if j==997
break
end
end while j>=995 # 亦可使用until


(1..5).each do num
print num
if num == 4
break # 亦可使用redo繼續, next下一個, retry重試迴路
end
end



values = [2, 4, 6, 8, 10]
values.length.times do index
print values[index], " "
end


ary = Array.new(3).fill { "foo" }
ary[0].replace "bar"
p ary


執行
c:\InstantRails\ruby\bin>ruby c:\joeyta.rb


玩完Ruby後,現在來玩一下Rails:
c:\InstantRails\ruby\bin>rails C:\InstantRails\rails_apps\mybook
就會在C:\InstantRails\rails_apps\ 目,建主mybook的application及相關的檔案


執行
ruby C:\InstantRails\rails_apps\mybook\script\server
或到Instant Rails > I > Rails Application > Manage Rails Applications
點選mybook 及 按 "Start with WEBrick"
就會啟動網站
輸入http://127.0.0.1:3000/ 進入網站


執行ruby C:\InstantRails\rails_apps\mybook\script\generate controller MyTest
編輯C:\InstantRails\rails_apps\mybook\app\controllers\my_test_controller.rb 為
class MyTestController < ApplicationController
def index
render_text "Hello World"
end
end


輸入 http://127.0.0.1:3000/My_Test/ 就能看到 Hello World


繼續編輯 C:\InstantRails\rails_apps\mybook\app\controllers\my_test_controller.rb
class MyTestController < ApplicationController
def index
render_text "Hello World"
end
def hello
render_text "Hello Rails"
end
end


輸入 http://127.0.0.1:3000/My_Test/hello 就看到 Hello Rails



建立資料庫:
進入 http://localhost/mysql/


執行:
create database mybook;
create table books(
id int(11) auto_increment primary key,
title varchar(100),
description text,
buydate date)


修改 C:\InstantRails\rails_apps\mybook\config\database.yml
(YAML配置檔,詳情可參考 http://www.yaml.org/http://www.ruby-doc.org/core/classes/YAML.html)
development:
adapter: mysql
database: mybook
username: root
password:
host: localhost
test:
adapter: mysql
database: mybook
username: root
password:
host: localhost
production:
adapter: mysql
database: mybook
username: root
password:
host: localhost

執行
ruby C:\InstantRails\rails_apps\mybook\script\generate model book
就會在C:\InstantRails\rails_apps\mybook\app\models 下產生 book.rb
Rails智能地把Book mapping 至 mysql 的books table.
(創建model book就會將Book映射至英文眾數的book talbe,即books table)



執行
ruby C:\InstantRails\rails_apps\mybook\script\generate controller book
編輯 C:\InstantRails\rails_apps\mybook\app\controllers\book_controller.rb
class BookController < ApplicationController
scaffold:book # scaffold:book 生成CRUD代碼
end


輸入 http://127.0.0.1:3000/book/new
不可思意地竟然產生了UI 讓用戶新增 修改 刪除 數據到mysql books table.


編輯 C:\InstantRails\rails_apps\mybook\app\controllers\book_controller.rb
class BookController < ApplicationController
scaffold:book
def list
end
當輸入 http://127.0.0.1:3000/book/new 會出現缺少template的錯誤頁面


新增
C:\InstantRails\rails_apps\mybook\app\views\book\list.rhtml 內容為
<html>
<head>
<title>All books</title>
</head>
<body>
<h1>Online Mybook - All books</h1>
<table border="1">
<tr>
<td width="80%"><p align="center"><i><b>book</b></i></td>
<td width="20%"><p align="center"><i><b>Date</b></i></td>
</tr>
<% @books.each do book %>
<tr>
<td><%= link_to book.title, :action => "show", :id => book.id %></td>
<td><%= book.buydate %></td>
</tr>
<% end %>
</table>
<p><%= link_to "Create new book", :action => "new" %></p>
</body>
</html>


修改 C:\InstantRails\rails_apps\mybook\app\controllers\book_controller.rb
class BookController < ApplicationController
scaffold:book
def list
@books = Book.find_all
end
end


輸入 http://127.0.0.1:3000/book/list 就會出現自定的list template



進入 http://localhost/mysql/
use mybook;
create table categories(
id int(11) auto_increment primary key,
name varchar(50)
);
alter table books add category_id int(11) not null after description;
INSERT INTO `categories` VALUES (1, '小說');
INSERT INTO `categories` VALUES (2, '科幻');
INSERT INTO `categories` VALUES (3, '漫畫');
INSERT INTO `books` VALUES (1, '天海', '天海一閣', 1, '2006-04-28');
INSERT INTO `books` VALUES (2, '好書', '好書一本', 2, '2006-04-29');



執行
ruby C:\InstantRails\rails_apps\mybook\script\generate model category
ruby C:\InstantRails\rails_apps\mybook\script\generate controller category


修改 C:\InstantRails\rails_apps\mybook\app\model\book.rb
class Book < ActiveRecord::Base
belongs_to :category
end


修改 C:\InstantRails\rails_apps\mybook\app\model\category.rb
class Category < ActiveRecord::Base
has_many :books
end


修改 C:\InstantRails\rails_apps\mybook\app\controllers\book_controller.rb
class BookController < ApplicationController
scaffold:book
def list
@books = Book.find_all
end
def edit
@book = Book.find(@params["id"])
@categories = Category.find_all
end
end



新增C:\InstantRails\rails_apps\mybook\app\views\book\list.rhtml 內容為
<html>
<head>
<title>Edit book</title></head>
<body>
<h1>Edit book</h1>
<form action="../update" method="POST">
<input id="book_id" name="book[id]" size="30" type="hidden" value="<%= @book.id %>" />
<p><b>Title</b><br>
<input id="book_title" name="book[title]" size="30" type="text" value="<%= @book.title %>" /> </p>
<p><b>Description</b><br>
<input id="book_description" name="book[description]" size="30" type="text" value="<%= @book.description %>" /> </p>
<p><b>Category:</b><br>
<select name="book[category_id]">
<% @categories.each do category %>
<option value="<%= category.id %>" <%= ' selected' if category.id == @book.category.id %>> <%= category.name %>
</option>
<% end %>
</select></p>
<input type="submit" value="Update" />
</form>
<a href="/book/show/<%= @book.id %>"> Show </a> <a href="/book/list"> Back </a>
</body>
</html>


AJAX(JSON)備忘記

在上一編"Ajax備忘記"中,使用了Array及JAVA裡的getter,setter概念來存取property.
這在管理上有點不方便,現在把它改成使用JSON來實作.


下面主要包含3個文檔:
news.xml // xml data file
news.js // logical file
news.html // configuration file


<!-----------news.xml-------------->
<?xml version="1.0" ?>
<ROWSET>
<NEWS>
<NEWS_ITEM num="1">
<ID>TEST 1</ID>
<TITLE>TITLE TEST 1</TITLE>
<CONTENT>CONTENT TEST 1</CONTENT>
</NEWS_ITEM>
<NEWS_ITEM num="2">
<ID>TEST 2</ID>
<TITLE>TITLE TEST 2</TITLE>
<CONTENT>CONTENT TEST 2</CONTENT>
</NEWS_ITEM>
</NEWS>
</ROWSET>
<!-----------news.xml-------------->


<!-----------news.js-------------->
var xmlHttp;
var debug_log = "Start mailto:Debug@@@@@@@/n";

function createXMLHttpRequest() {
if (window.ActiveXObject && !window.XMLHttpRequest) {
window.XMLHttpRequest = function() {
var MSXML = ["Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","Msxml2.XMLHTTP.3.0","Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
for (var i = 0; i < MSXML.length; i++) {
try {
return new ActiveXObject(MSXML[i]);
} catch (e) {
debug_log += "#XMLHTTP TYPE:"+MSXML[i]+"#\n";
}
}
return null;
};
}
xmlHttp = new XMLHttpRequest();
}

function startRequest() {
createXMLHttpRequest();
xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.open("GET", xmlUrl, true);
//xmlHttp.setRequestHeader("If-Modified-Since", xmlHttp.lastModified);
xmlHttp.send(null);
}

function handleStateChange() {
if(xmlHttp.readyState != 4){
//var iTimeoutId = setTimeout(function(){Logger("#Loading#\n");}, 3000);
//clearTimeout(iTimeoutId);
}


if(xmlHttp.readyState == 4) {
if(xmlHttp.status == 200) {
loadXmlData();
is_load = true;
} else {
debug_log += "#http status:"+xmlHttp.status+"#\n";
}
ShowContent();
//Logger(debug_log);
} else {
debug_log += "#readyState:"+xmlHttp.readyState+"#\n";

}
}


function isNewsProperty(property){
var oNews = new News();
for(var i in oNews.items){
if(i == property) return true;
}
return false;
}

function loadXmlData(){
var xmlDoc = xmlHttp.responseXML;
var news_allItem = xmlDoc.getElementsByTagName(news_item_tag_name);
debug_log += "#XML TEXT:"+xmlHttp.responseText+"#\n";
for(var i = 0; i < news_allItem.length; i++){
var oNews = new News();
var item_list = news_allItem[i].childNodes;
for(var k = 0; k < item_list.length; k++){
if(isNewsProperty(item_list[k].tagName)){
oNews.items[item_list[k].tagName] = item_list[k].childNodes[0].nodeValue;
}
}
news_item_list.push(oNews);
}
}

function Logger(msg){
if(document.getElementById("log") == null){
var oDiv = document.createElement("div");
//var oAtt = document.createAttribute("id","log");
//oDiv.setAttributeNode(oAtt);
oDiv.setAttribute("id","log");
document.body.appendChild(oDiv);
}
document.getElementById("log").innerHTML += msg;
}

window.onload = function(){
startRequest();
}
<!-----------news.js-------------->


<!-----------news.html-------------->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Parsing XML Responses with the W3C DOM</title>
<script type="text/javascript">
var xmlUrl = "news.xml";
var news_item_tag_name = "NEWS_ITEM";
function News(){
this.items={
'ID' : '',
'TITLE' : '',
'CONTENT' : ''
};
}
var news_item_list = new Array();
var is_load = false;
</script>


<script type="text/javascript">
function getContent(){
var content = "<table border='1'>";
if(news_item_list.length == 0){
content += "<tr><td>No data</td></tr></table>";
return content;
}


content += "<tr><th>ID</th><th>TITLE</th><th>CONTENT</th></tr>"
for(var i = 0; i < news_item_list.length; i++){
content += "<tr>";
content += "<td>" + news_item_list[i].items.ID + "</td>";
content += "<td>" + news_item_list[i].items.TITLE + "</td>";
content += "<td>" + news_item_list[i].items.CONTENT + "</td>";
content += "<tr>";
}
content += "</table>";
return content;
}


function ShowContent(){
if(document.getElementById("content") == null){
var oDiv = document.createElement("div");
oDiv.setAttribute("id","content");
document.body.appendChild(oDiv);
}
document.getElementById("content").innerHTML = getContent();
}
</script>


<script type="text/javascript" src="news.js"></script>


</head>
<body>
</body>
</html>
<!-----------news.html-------------->



<!-------------Explanation 1------------------------>
<script type="text/javascript">
var xmlUrl = "news.xml"; // xml的相對位置, 亦可輸入url的absolute位置.但不能跨Domain,跨Domain需實作proxy
var news_item_tag_name = "NEWS_ITEM"; // 想要截取的tag name
function News(){
this.items={ // 這裡使用了JSON( JavaScript Object Notation)
'ID' : '',
'TITLE' : '',
'CONTENT' : ''
};
}
var news_item_list = new Array(); // 把所有的NEWS ITEM放進ARRAY裡
var is_load = false; // 是否已把XML資料LOAD完,用作PRE Lodding,這裡沒實作

</script>
<!-------------Explanation 1------------------------>


<!-------------Explanation 2------------------------>
var xmlHttp; // XMLHttpRequest的instance放的變量.
var debug_log = "Start Debug@@@@@@@\n"; // 記錄debug message的變量

function createXMLHttpRequest() {
if (window.ActiveXObject && !window.XMLHttpRequest) {
window.XMLHttpRequest = function() {
// 這裡override window的XMLHttpRequest object,後面使用就不用管它是甚麼Browser( ie or firefox )
var MSXML = ["Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","Msxml2.XMLHTTP.3.0","Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
// 為了相容IE6前的所有BROWSER版本, 所以用FOR LOOP去LOOKUP每個版本ActiveXObject的XMLHTTP.當找到就直接RETURN
for (var i = 0; i < MSXML.length; i++) {
try {
return new ActiveXObject(MSXML[i]);
} catch (e) {
debug_log += "#XMLHTTP TYPE:"+MSXML[i]+"#\n";
}
}
return null;
};
}
xmlHttp = new XMLHttpRequest();
// 由於上面已override window裡的XMLHttpRequest object,所以這裡直接使用
}
<!-------------Explanation 2------------------------>



<!-------------Explanation 3------------------------>
function startRequest() {
createXMLHttpRequest();
xmlHttp.onreadystatechange = handleStateChange;
// onreadystatechange為event handler, 所以當readyState 等於 0,1,2,3,4時,便會call handleStateChange()這個function
// readyState = 0 為uninitialized, 1 為loading, 2 為loaded, 3為interactive, 4為complete
xmlHttp.open("GET", xmlUrl, true);
//xmlUrl就會xml的位置, 為了簡單,這裡只使用靜態的xml檔案,
// 最後的true表示現在是異歩, false為同步,
// 如果設定為同步,在下面送出send時就會等待回應結果,
xmlHttp.send(null);
}

function handleStateChange() {
if(xmlHttp.readyState != 4){
//var iTimeoutId = setTimeout(function(){Logger("#Loading#\n");}, 3000);
//clearTimeout(iTimeoutId);
}


if(xmlHttp.readyState == 4) { // 上面己對readState的各種狀態說明了, 這裡當readyState等於4即已把xml載入完成
if(xmlHttp.status == 200) {
// xmlHttp.status為http status, 200為成功,404為not found
loadXmlData();
// call 自建的 loadXmlData() function. 把xml裡的NEWS_ITEM放進news_item_list ARRAY裡
is_load = true;
} else {
debug_log += "#http status:"+xmlHttp.status+"#\n";
}
ShowContent();
// 當所有NEWS_ITEM載入news_item_list, call 自建的ShowContent() function顯示html
//Logger(debug_log);
} else {
debug_log += "#readyState:"+xmlHttp.readyState+"#\n";

}
}
<!-------------Explanation 3------------------------>



<!-------------Explanation 4------------------------>
function isNewsProperty(property){ // 這個function是判斷xml裡的property有沒有對應News裡的items.
var oNews = new News();
for(var i in oNews.items){ // 把所有items裡的properties逐個取出來
if(i == property) return true;
}
return false;
}

function loadXmlData(){
var xmlDoc = xmlHttp.responseXML; // 把xml document給xmlDoc變量
var news_allItem = xmlDoc.getElementsByTagName(news_item_tag_name);
// 此變量news_item_tag_name 為 "NEWS_ITEM", 所以此句LOOKUP所有NEWS_ITEM node.
debug_log += "#XML TEXT:"+xmlHttp.responseText+"#\n";
for(var i = 0; i < news_allItem.length; i++){
var oNews = new News();
var item_list = news_allItem[i].childNodes; // 這裡將所有NEWS_ITEM裡的node放到item_list變量
for(var k = 0; k < item_list.length; k++){
if(isNewsProperty(item_list[k].tagName)){
oNews.items[item_list[k].tagName] = item_list[k].childNodes[0].nodeValue;
}
}

news_item_list.push(oNews);
// Array裡的push function可看作Array為stack, push data在Array的最後
}
}

function Logger(msg){ // Logger用作debug
if(document.getElementById("log") == null){
var oDiv = document.createElement("div"); // 這裡建立div element.
oDiv.setAttribute("id","log");
// 設定div裡id property為"log"
document.body.appendChild(oDiv);
// 把div element加入html 的body裡, body tag 必須先在html裡建立
}
document.getElementById("log").innerHTML += msg;
}

window.onload = function(){
// 當load完整份html,然後呼叫startRequest()去load xml及顯示html content
startRequest();
}
<!-------------Explanation 4------------------------>


<!-------------Explanation 5------------------------>
<script type="text/javascript">
function getContent(){
// 此function可看到html的template,把資料送給ShowContent() function顯示
var content = "<table border='1'>";
if(news_item_list.length == 0){
content += "<tr><td>No data</td></tr></table>";
return content;
}


content += "<tr><th>ID</th><th>TITLE</th><th>CONTENT</th></tr>"
for(var i = 0; i < news_item_list.length; i++){
content += "<tr>";
content += "<td>" + news_item_list[i].items.ID + "</td>";
content += "<td>" + news_item_list[i].items.TITLE + "</td>";
content += "<td>" + news_item_list[i].items.CONTENT + "</td>";
content += "<tr>";
}
content += "</table>";
return content;
}


function ShowContent(){ // 當readState為4時,load完xml後,呼叫此function顯示data
if(document.getElementById("content") == null){
var oDiv = document.createElement("div");
oDiv.setAttribute("id","content");
document.body.appendChild(oDiv);
}
document.getElementById("content").innerHTML = getContent();
}
</script>
<!-------------Explanation 5------------------------>

AJAX(PROTOTYPE)備忘記

由於公司網站瀏覽數量非常驚人, 更新資料亦非常頻繁.
就形成前門火災, 後門進賊 的困景, 這雖然錢能解決以上問題, 但實在不是長期的辦法.
雖然 flash 搭配 xml 是一個很好的解決方案, 但當頁面設計變更時,
開發員往往需要花上很多時間處理 flash 裡 Layout 的重整, 那是件非常累人的事情.
所以使用 Ajax 技術能有效解決以上問題.
對於門戶網站的前門, 能減少不小頻寬, 這可節省不少金錢.
對於後門, 能更快地更新到網站上, 尤其是對於實時性很注重的網站.


話說回來, 在1年多前,
一位同事在別的網站抄了別人的ajax代碼再更改,非常凌亂,實在是不好維護,
而且不能處理 firefox, 後來我也鮮少去看.
由於此原因, 故想自己從頭開始編寫一次, 並將整個過程記錄下來, 以供日後參考.


編寫Ajax(Asyncronous Javascript and XML), 必需對Javascript與XML DOM有一定的了解.
由於本人屬新手級, 所以必須參考多本藉作, 以更快掌握Ajax技術.
對於javascript及DOM,本人參考了Professional JavaScript for Web Developers.
而Ajax的書藉, 則參考了Foundations of Ajax及Ajax Hacks, 當然還有網上的豐富資源.

這個例子只是簡單的測試, 沒有對xml資料作動態變更, 這使得整個備忘記更加簡潔.
處理 XML 的變更亦是一件輕而易舉的事情, 使用 Jdom 是不錯的選擇.

開始備忘記:
下面主要包含3個文檔:
news.xml // xml data file
news.js // logical file
news.html // configuration file


<!-----------news.xml-------------->
<?xml version="1.0" ?>
<ROWSET>
<NEWS>
<NEWS_ITEM num="1">
<ID>TEST 1</ID>
<TITLE>TITLE TEST 1</TITLE>
<CONTENT>CONTENT TEST 1</CONTENT>
</NEWS_ITEM>
<NEWS_ITEM num="2">
<ID>TEST 2</ID>
<TITLE>TITLE TEST 2</TITLE>
<CONTENT>CONTENT TEST 2</CONTENT>
</NEWS_ITEM>
</NEWS>
</ROWSET>
<!-----------news.xml-------------->


<!-----------news.js-------------->
Array.prototype.indexOf = function (vItem) {
for (var i=0; i < this.length; i++) {
if (vItem == this[i]) {
return i;
}
}
return -1;
}

function getParameter( sParameterName, sDefaultValue ) {
var sQueryString = window.location.search.substring(1).toLowerCase();
var parameters = new Array();
parameters = sQueryString.split('&');
for(var i = 0; i < parameters.length; i++) {
if (parameters[i].indexOf(sParameterName.toLowerCase())>=0) {
var sParameterValue = new Array();
sParameterValue = parameters[i].split('=');
return sParameterValue[1];
}
}
return sDefaultValue;
}


var xmlHttp;
var debug_log = "Start
Debug@@@@@@@\n";

function News() {
this.items = new Array();
}
News.prototype.set = function(key,value){
this.items[news_item_att.indexOf(key)] = value;
}
News.prototype.get = function(key){
return this.items[news_item_att.indexOf(key)];
}


function createXMLHttpRequest() {
if (window.ActiveXObject && !window.XMLHttpRequest) {
window.XMLHttpRequest = function() {
var MSXML = ["Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","Msxml2.XMLHTTP.3.0","Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
for (var i = 0; i < MSXML.length; i++) {
try {
return new ActiveXObject(MSXML[i]);
} catch (e) {
debug_log += "#XMLHTTP TYPE:"+MSXML[i]+"#\n";
}
}
return null;
};
}
xmlHttp = new XMLHttpRequest();
}

function startRequest() {
createXMLHttpRequest();
xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.open("GET", xmlUrl, true);
//xmlHttp.setRequestHeader("If-Modified-Since", xmlHttp.lastModified);
xmlHttp.send(null);
}

function handleStateChange() {
if(xmlHttp.readyState != 4){
//var iTimeoutId = setTimeout(function(){Logger("#Loading#\n");}, 3000);
//clearTimeout(iTimeoutId);
}


if(xmlHttp.readyState == 4) {
if(xmlHttp.status == 200) {
loadXmlData();
is_load = true;
} else {
debug_log += "#http status:"+xmlHttp.status+"#\n";
}
ShowContent();
//Logger(debug_log);
} else {
debug_log += "#readyState:"+xmlHttp.readyState+"#\n";

}
}

function loadXmlData(){
var xmlDoc = xmlHttp.responseXML;
var news_allItem = xmlDoc.getElementsByTagName(news_item_tag_name);
debug_log += "#XML TEXT:"+xmlHttp.responseText+"#\n";
for(var i = 0; i < news_allItem.length; i++){
var oNews = new News();
var item_list = news_allItem[i].childNodes;
for(var k = 0; k < item_list.length; k++){
for(var j = 0; j < news_item_att.length; j++){
if(item_list[k].tagName == news_item_att[j]){
oNews.set(news_item_att[j],item_list[k].childNodes[0].nodeValue);
}
}
}
news_item_list.push(oNews);
}
}

function Logger(msg){
if(document.getElementById("log") == null){
var oDiv = document.createElement("div");
//var oAtt = document.createAttribute("id","log");
//oDiv.setAttributeNode(oAtt);
oDiv.setAttribute("id","log");
document.body.appendChild(oDiv);
}
document.getElementById("log").innerHTML += msg;
}

window.onload = function(){
startRequest();
}
<!-----------news.js-------------->



<!-----------news.html-------------->
<html xmlns="
http://www.w3.org/1999/xhtml">
<head>
<title>Parsing XML Responses with the W3C DOM</title>
<script type="text/javascript">
var xmlUrl = "news.xml";
var news_item_tag_name = "NEWS_ITEM";
var news_item_att = ["ID","TITLE","CONTENT"];
var news_item_list = new Array();
var is_load = false;
</script>


<script type="text/javascript">
function getContent(){
var content = "<table border='1'>";
if(news_item_list.length == 0){
content += "<tr><td>No data</td></tr></table>";
return content;
}


content += "<tr><th>ID</th><th>TITLE</th><th>CONTENT</th></tr>"
for(var i = 0; i < news_item_list.length; i++){
content += "<tr>";
content += "<td>" + news_item_list[i].get("ID") + "</td>";
content += "<td>" + news_item_list[i].get("TITLE") + "</td>";
content += "<td>" + news_item_list[i].get("CONTENT") + "</td>";
content += "<tr>";
}
content += "</table>";
return content;
}


function ShowContent(){
if(document.getElementById("content") == null){
var oDiv = document.createElement("div");
oDiv.setAttribute("id","content");
document.body.appendChild(oDiv);
}
document.getElementById("content").innerHTML = getContent();
}
</script>


<script type="text/javascript" src="news.js"></script>


</head>
<body>
</body>
</html>
<!-----------news.html-------------->



<!-------------Explanation 1------------------------>
<script type="text/javascript">
var xmlUrl = "news.xml";
// xml的相對位置, 亦可輸入url的absolute位置.但不能跨Domain,跨Domain需實作proxy
var news_item_tag_name = "NEWS_ITEM"; // 想要截取的tag name
var news_item_att = ["ID","TITLE","CONTENT"]; // xml裡NEWS_ITEM下的所有NODES
var news_item_list = new Array(); // 把所有的NEWS ITEM放進ARRAY裡
var is_load = false; // 是否已把XML資料LOAD完,用作PRE Lodding,這裡沒實作
</script>
<!-------------Explanation 1------------------------>



<!-------------Explanation 2------------------------>
Array.prototype.indexOf = function (vItem) {
for (var i=0; i < this.length; i++) {
if (vItem == this[i]) {
return i;
}
}
return -1;
}
// 由於Array裡沒有像indexOf的function, 故自建indexOf function. 而在javascript裡,
// function可看作object,而每個object裡都包含prototype這個屬性.
// 當object被new時, prototype所關聯的function就會關聯到object裡. 即變成object的fucntion.


var xmlHttp; // XMLHttpRequest的instance放的變量.
var debug_log = "Start
Debug@@@@@@@\n"; // 記錄debug message的變量

function News() { // News item物件,將對應XML資料的NEWS_ITEM
this.items = new Array();
}
News.prototype.set = function(key,value){
// news_item_att看作所有News物件的properties, 這裡利用array的特性自定義setter
this.items[news_item_att.indexOf(key)] = value;
}
News.prototype.get = function(key){ // News裡properties的getter
return this.items[news_item_att.indexOf(key)];
}
<!-------------Explanation 2------------------------>



<!-------------Explanation 3------------------------>
function createXMLHttpRequest() {
if (window.ActiveXObject && !window.XMLHttpRequest) {
window.XMLHttpRequest = function() {
// 這裡override window的XMLHttpRequest object,後面使用就不用管它是甚麼Browser( ie or firefox )
var MSXML = ["Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","Msxml2.XMLHTTP.3.0","Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
// 為了相容IE6前的所有BROWSER版本, 所以用FOR LOOP去LOOKUP每個版本ActiveXObject的XMLHTTP.當找到就直接RETURN
for (var i = 0; i < MSXML.length; i++) {
try {
return new ActiveXObject(MSXML[i]);
} catch (e) {
debug_log += "#XMLHTTP TYPE:"+MSXML[i]+"#\n";
}
}
return null;
};
}
xmlHttp = new XMLHttpRequest();
// 由於上面已override window裡的XMLHttpRequest object,所以這裡直接使用
}
<!-------------Explanation 3------------------------>



<!-------------Explanation 4------------------------>
function startRequest() {
createXMLHttpRequest();
xmlHttp.onreadystatechange = handleStateChange;
// onreadystatechange為event handler, 所以當readyState 等於 0,1,2,3,4時,便會call handleStateChange()這個function
// readyState = 0 為uninitialized, 1 為loading, 2 為loaded, 3為interactive, 4為complete
xmlHttp.open("GET", xmlUrl, true);
//xmlHttp.setRequestHeader("If-Modified-Since", xmlHttp.lastModified);
xmlHttp.send(null);
}

function handleStateChange() {
if(xmlHttp.readyState != 4){
//var iTimeoutId = setTimeout(function(){Logger("#Loading#\n");}, 3000);
//clearTimeout(iTimeoutId);
}


if(xmlHttp.readyState == 4) { // 上面己對readState的各種狀態說明了, 這裡當readyState等於4即已把xml載入完成
if(xmlHttp.status == 200) {
// xmlHttp.status為http status, 200為成功,404為not found
loadXmlData();
// call 自建的 loadXmlData() function. 把xml裡的NEWS_ITEM放進news_item_list ARRAY裡
is_load = true;
} else {
debug_log += "#http status:"+xmlHttp.status+"#\n";
}
ShowContent();
// 當所有NEWS_ITEM載入news_item_list, call 自建的ShowContent() function顯示html
//Logger(debug_log);
} else {
debug_log += "#readyState:"+xmlHttp.readyState+"#\n";

}
}
<!-------------Explanation 4------------------------>



<!-------------Explanation 5------------------------>
function loadXmlData(){
var xmlDoc = xmlHttp.responseXML;
// 把xml document給xmlDoc變量
var news_allItem = xmlDoc.getElementsByTagName(news_item_tag_name);
// 此變量news_item_tag_name 為 "NEWS_ITEM", 所以此句LOOKUP所有NEWS_ITEM node.
debug_log += "#XML TEXT:"+xmlHttp.responseText+"#\n";
for(var i = 0; i < news_allItem.length; i++){
var oNews = new News();
var item_list = news_allItem[i].childNodes;
// 這裡將所有NEWS_ITEM裡的node放到item_list變量
for(var k = 0; k < item_list.length; k++){
for(var j = 0; j < news_item_att.length; j++){
// news_item_att Array儲存NEWS_ITEM裡的所有node的tag name
if(item_list[k].tagName == news_item_att[j]){
oNews.set(news_item_att[j],item_list[k].childNodes[0].nodeValue);
// 將所有properties以setter放進oNews object裡
}
}
}
news_item_list.push(oNews);
// Array裡的push function可看作Array為stack, push data在Array的最後
}
}

function Logger(msg){ // Logger用作debug
if(document.getElementById("log") == null){
var oDiv = document.createElement("div"); // 這裡建立div element.
oDiv.setAttribute("id","log");
// 設定div裡id property為"log"
document.body.appendChild(oDiv);
// 把div element加入html 的body裡, body tag 必須先在html裡建立
}
document.getElementById("log").innerHTML += msg;
}

window.onload = function(){
// 當load完整份html,然後呼叫startRequest()去load xml及顯示html content
startRequest();
}
<!-------------Explanation 5------------------------>


<!-------------Explanation 6------------------------>
<script type="text/javascript">
function getContent(){
// 此function可看到html的template,把資料送給ShowContent() function顯示
var content = "<table border='1'>";
if(news_item_list.length == 0){
content += "<tr><td>No data</td></tr></table>";
return content;
}


content += "<tr><th>ID</th><th>TITLE</th><th>CONTENT</th></tr>"
for(var i = 0; i < news_item_list.length; i++){
content += "<tr>";
content += "<td>" + news_item_list[i].get("ID") + "</td>";
content += "<td>" + news_item_list[i].get("TITLE") + "</td>";
content += "<td>" + news_item_list[i].get("CONTENT") + "</td>";
content += "<tr>";
}
content += "</table>";
return content;
}


function ShowContent(){ // 當readState為4時,load完xml後,呼叫此function顯示data
if(document.getElementById("content") == null){
var oDiv = document.createElement("div");
oDiv.setAttribute("id","content");
document.body.appendChild(oDiv);
}
document.getElementById("content").innerHTML = getContent();
}
</script>
<!-------------Explanation 6------------------------>


不要誤會這編是介紹關於 Prototype Javascript Framework,
由於官方的文檔很少,
因此研究 Prototype 的最好方法是直接參看主要的源文檔 prototype.js ,
以下有一編相關的網上教學, 由於寫得不錯, 就不再重寫一編:
https://compdoc2cn.dev.java.net/prototype/html/prototype.js.cn.html
官方網站為:
http://prototype.conio.net/