雅典之夏的小站
          知人者智 自知者明 Fighting!!

          JBUILDER的光標定位不準確的最佳解決方案是:進入%JBUILDER_HOME%/bin目錄下,用寫字板編輯jbuilder.config,把下面的配置加進去:
          vmparam -Dprimetime.editor.useVariableWidthFont=true

          posted @ 2005-12-06 11:02 rkind 閱讀(238) | 評論 (0)編輯 收藏

          Ant的簡介:類似make工具,但可以支持多平臺
          Ant的安裝:配置ant的準備工作:ant_home 指Ant的安裝目錄,在path中加入%ant_home%/bin,用于命令行下
                  運 行ant
          Ant的結構:主要是通過對build.xml的配置,
                               Ant內置任務:                                                描述
                                  property                                              設置name/value的屬性        
                                  mkdir                                                   創建目錄
                                  copy                                                     拷貝
                                  delete                                                   刪除
                                  javac                                                    編繹
                                  war                                                      打包
          下面是一個簡單build.xml的示例:
          <project name="bookstore" default="about" basedir=".">

              <target name="init">
                  <tstamp/>
                  <property name="build" value="build" />
                  <property name="src" value="src" />
                  <property environment="myenv" />
                  <property name="servletpath"  value="${myenv.CATALINA_HOME}/common/lib/servlet-api.jar" />
                  <property name="mysqlpath" value="WEB-INF/lib/mysqldriver.jar" />

           <mkdir dir="${build}" />
           <mkdir dir="${build}\WEB-INF" />
           <mkdir dir="${build}\WEB-INF\classes" />

                <copy todir="${build}" >
                  <fileset dir="${basedir}"    >
                     <include name="*.jsp" />
                     <include name="*.bmp" />
                     <include name="WEB-INF/**" />
                     <exclude name="build.xml" />
                  </fileset>
               </copy>
           
             </target>


            <target name="compile" depends="init">       

               <javac srcdir="${src}"
                       destdir="${build}/WEB-INF/classes"
                       classpath="${servletpath}:${mysqlpath}">
               </javac>
            </target>
           
             <target name="bookstorewar" depends="compile">       

              <war warfile="${build}/bookstore.war" webxml="${build}/WEB-INF/web.xml">
           <lib dir="${build}/WEB-INF/lib"/>
           <classes dir="${build}/WEB-INF/classes"/>
           <fileset dir="${build}"/>
              </war>
              </target>

            <target name="about" >
                  <echo>
           This build.xml file contains targets
                 for building bookstore web application
                  </echo> 
             </target>

          </project>

          從示例我們看出來,整個xml是一個project,project下有幾個為init,compile,的target
          運行時首先在這個目錄下打開dos窗口,以這個xml為準,如果你只運行ant那么只會輸出echo中的內容
          因為project的default是about; 如果運行ant complie 它會執行兩個target: init 和complie,因為complie是依靠init
          的。
                用了以后發現ant 原來很簡單,當然現在只是學了個皮毛而已。

          posted @ 2005-11-25 14:53 rkind 閱讀(263) | 評論 (0)編輯 收藏

             RMI,遠程方法調用(Remote Method Invocation)是Enterprise JavaBeans的支柱,是建立分布式Java應用程序的方便途徑。RMI是非常容易使用的,但是它非常的強大。

                 RMI的基礎是接口,RMI構架基于一個重要的原理:定義接口和定義接口的具體實現是分開的。下面我們通過具體的例子,建立一個簡單的遠程計算服務和使用它的客戶程序

          一個正常工作的RMI系統由下面幾個部分組成:

          ●     遠程服務的接口定義

          ●     遠程服務接口的具體實現

          ●     樁(Stub)和框架(Skeleton)文件

          ●     一個運行遠程服務的服務器

          ●     一個RMI命名服務,它允許客戶端去發現這個遠程服務

          ●     類文件的提供者(一個HTTP或者FTP服務器)

          ●     一個需要這個遠程服務的客戶端程序

          下面我們一步一步建立一個簡單的RMI系統。首先在你的機器里建立一個新的文件夾,以便放置我們創建的文件,為了簡單起見,我們只使用一個文件夾存放客戶端和服務端代碼,并且在同一個目錄下運行服務端和客戶端。

          如果所有的RMI文件都已經設計好了,那么你需要下面的幾個步驟去生成你的系統:

          1、  編寫并且編譯接口的Java代碼

          2、  編寫并且編譯接口實現的Java代碼

          3、  從接口實現類中生成樁(Stub)和框架(Skeleton)類文件

          4、  編寫遠程服務的主運行程序

          5、  編寫RMI的客戶端程序

          6、  安裝并且運行RMI系統


          1、  接口

          第一步就是建立和編譯服務接口的Java代碼。這個接口定義了所有的提供遠程服務的功能,下面是源程序:

          //Calculator.java
          //define the interface
          import java.rmi.Remote;

          public interface Calculator extends Remote
          {
              public long add(long a, long b)
                  throws java.rmi.RemoteException;

              public long sub(long a, long b)
                  throws java.rmi.RemoteException;

              public long mul(long a, long b)
                  throws java.rmi.RemoteException;

              public long div(long a, long b)
                  throws java.rmi.RemoteException;
          }
          注意,這個接口繼承自Remote,每一個定義的方法都必須拋出一個RemoteException異常對象。

          建立這個文件,把它存放在剛才的目錄下,并且編譯。

          >javac Calculator.java

          2、  接口的具體實現

          下一步,我們就要寫遠程服務的具體實現,這是一個CalculatorImpl類文件:

          //CalculatorImpl.java
          //Implementation
          import java.rmi.server.UnicastRemoteObject

          public class CalculatorImpl extends UnicastRemoteObject implements Calculator
          {

              // 這個實現必須有一個顯式的構造函數,并且要拋出一個RemoteException異常
              public CalculatorImpl()
                  throws java.rmi.RemoteException {
                  super();
              }

              public long add(long a, long b)
                  throws java.rmi.RemoteException {
                  return a + b;
              }

              public long sub(long a, long b)
                  throws java.rmi.RemoteException {
                  return a - b;
              }

              public long mul(long a, long b)
                  throws java.rmi.RemoteException {
                  return a * b;
              }

              public long div(long a, long b)
                  throws java.rmi.RemoteException {
                  return a / b;
              }
          }
                 同樣的,把這個文件保存在你的目錄里然后編譯他。

                 這個實現類使用了UnicastRemoteObject去聯接RMI系統。在我們的例子中,我們是直接的從UnicastRemoteObject這個類上繼承的,事實上并不一定要這樣做,如果一個類不是從UnicastRmeoteObject上繼承,那必須使用它的exportObject()方法去聯接到RMI。

                 如果一個類繼承自UnicastRemoteObject,那么它必須提供一個構造函數并且聲明拋出一個RemoteException對象。當這個構造函數調用了super(),它久激活UnicastRemoteObject中的代碼完成RMI的連接和遠程對象的初始化。



          3、  樁(Stubs)和框架(Skeletons)

          下一步就是要使用RMI編譯器rmic來生成樁和框架文件,這個編譯運行在遠程服務實現類文件上。

          >rmic CalculatorImpl

          在你的目錄下運行上面的命令,成功執行完上面的命令你可以發現一個Calculator_stub.class文件,如果你是使用的Java2SDK,那么你還可以發現Calculator_Skel.class文件。



          4、  主機服務器

          遠程RMI服務必須是在一個服務器中運行的。CalculatorServer類是一個非常簡單的服務器。

          //CalculatorServer.java
          import java.rmi.Naming;

          public class CalculatorServer {

             public CalculatorServer() {
               try {
                 Calculator c = new CalculatorImpl();
                 Naming.rebind("rmi://localhost:1099/CalculatorService", c);
               } catch (Exception e) {
                 System.out.println("Trouble: " + e);
               }
             }

             public static void main(String args[]) {
               new CalculatorServer();
             }
          }
                 建立這個服務器程序,然后保存到你的目錄下,并且編譯它。

          5、  客戶端

          客戶端源代碼如下:

          //CalculatorClient.java



          import java.rmi.Naming;
          import java.rmi.RemoteException;
          import java.net.MalformedURLException;
          import java.rmi.NotBoundException;

          public class CalculatorClient {

              public static void main(String[] args) {
                  try {
                      Calculator c = (Calculator)
                                     Naming.lookup(
                           "rmi://localhost
                                  /CalculatorService");
                      System.out.println( c.sub(4, 3) );
                      System.out.println( c.add(4, 5) );
                      System.out.println( c.mul(3, 6) );
                      System.out.println( c.div(9, 3) );
                  }
                  catch (MalformedURLException murle) {
                      System.out.println();
                      System.out.println(
                        "MalformedURLException");
                      System.out.println(murle);
                  }
                  catch (RemoteException re) {
                      System.out.println();
                      System.out.println(
                                  "RemoteException");
                      System.out.println(re);
                  }
                  catch (NotBoundException nbe) {
                      System.out.println();
                      System.out.println(
                                 "NotBoundException");
                      System.out.println(nbe);
                  }
                  catch (
                      java.lang.ArithmeticException
                                                ae) {
                      System.out.println();
                      System.out.println(
                       "java.lang.ArithmeticException");
                      System.out.println(ae);
                  }
              }
          }
                 保存這個客戶端程序到你的目錄下(注意這個目錄是一開始建立那個,所有的我們的文件都在那個目錄下),并且編譯他。



          6、  運行RMI系統

          現在我們建立了所有運行這個簡單RMI系統所需的文件,現在我們終于可以運行這個RMI系統啦!來享受吧。

          我們是在命令控制臺下運行這個系統的,你必須開啟三個控制臺窗口,一個運行服務器,一個運行客戶端,還有一個運行RMIRegistry。

          首先運行注冊程序RMIRegistry,你必須在包含你剛寫的類的那么目錄下運行這個注冊程序。

          >rmiregistry

          好,這個命令成功的話,注冊程序已經開始運行了,不要管他,現在切換到另外一個控制臺,在第二個控制臺里,我們運行服務器CalculatorService,因為RMI的安全機制將在服務端發生作用,所以你必須增加一條安全策略。以下是對應安全策略的例子
          grant {
          permission java.security.AllPermission "", "";
          };
          注意:這是一條最簡單的安全策略,它允許任何人做任何事,對于你的更加關鍵性的應用,你必須指定更加詳細安全策略。
          現在為了運行服務端,你需要除客戶類(CalculatorClient.class)之外的所有的類文件。確認安全策略在policy.txt文件之后,使用如下命令來運行服務器。
          > java -Djava.security.policy=policy.txt CalculatorServer

          這個服務器就開始工作了,把接口的實現加載到內存等待客戶端的聯接。好現在切換到第三個控制臺,啟動我們的客戶端。
          為了在其他的機器運行客戶端程序你需要一個遠程接口(Calculator.class) 和一個stub(CalculatorImpl_Stub.class)。 使用如下命令運行客戶端
          prompt> java -Djava.security.policy=policy.txt CalculatorClient

          如果所有的這些都成功運行,你應該看到下面的輸出:

          1
          9
          18
          3
          如果你看到了上面的輸出,恭喜你,你成功了,你已經成功的創建了一個RMI系統,并且使他正確工作了。即使你運行在同一個計算機上,RMI還是使用了你的網絡堆棧和TCP/IP去進行通訊,并且是運行在三個不同的Java虛擬機上。這已經是一個完整的RMI系統。

          posted @ 2005-11-15 18:46 rkind 閱讀(239) | 評論 (0)編輯 收藏

            在一個有密碼保護的Web應用中,正確處理用戶退出過程并不僅僅只需調用HttpSession的invalidate()方法。現在大部分瀏覽器上都有后退和前進按鈕,允許用戶后退或前進到一個頁面。如果在用戶在退出一個Web應用后按了后退按鈕瀏覽器把緩存中的頁面呈現給用戶,這會使用戶產生疑惑,他們會開始擔心他們的個人數據是否安全。許多Web應用強迫用戶退出時關閉整個瀏覽器,這樣,用戶就無法點擊后退按鈕了。還有一些使用javascript,但在某些客戶端瀏覽器這卻不一定起作用。這些解決方案都很笨拙且不能保證在任一情況下100%有效,同時,它也要求用戶有一定的操作經驗。

            這篇文章以示例闡述了正確解決用戶退出問題的方案。作者Kevin Le首先描述了一個密碼保護Web應用,然后以示例程序解釋問題如何產生并討論解決問題的方案。文章雖然是針對JSP頁面進行闡述,但作者所闡述的概念很容易理解切能夠為其他Web技術所采用。最后作者展示了如何用Jakarta Struts優雅地解決這一問題。

            大部分Web應用不會包含象銀行賬戶或信用卡資料那樣機密的信息,但一旦涉及到敏感數據,我們就需要提供一類密碼保護機制。舉例來說,一個工廠中工人通過Web訪問他們的時間安排、進入他們的訓練課程以及查看他們的薪金等等。此時應用SSL(Secure Socket Layer)有點殺雞用牛刀的感覺,但不可否認,我們又必須為這些應用提供密碼保護,否則,工人(也就是Web應用的使用者)可以窺探到工廠中其他雇員的私人機密信息。

            與上述情形相似的還有位處圖書館、醫院等公共場所的計算機。在這些地方,許多用戶共同使用幾臺計算機,此時保護用戶的個人數據就顯得至關重要。設計良好編寫優秀的應用對用戶專業知識的要求少之又少。

            我們來看一下現實世界中一個完美的Web應用是如何表現的:一個用戶通過瀏覽器訪問一個頁面。Web應用展現一個登陸頁面要求用戶輸入有效的驗證信息。用戶輸入了用戶名和密碼。此時我們假設用戶提供的身份驗證信息是正確的,經過了驗證過程,Web應用允許用戶瀏覽他有權訪問的區域。用戶想退出時,點擊退出按鈕,Web應用要求用戶確認他是否則真的需要退出,如果用戶確定退出,Session結束,Web應用重新定位到登陸頁面。用戶可以放心的離開而不用擔心他的信息會泄露。另一個用戶坐到了同一臺電腦前,他點擊后退按鈕,Web應用不應該出現上一個用戶訪問過的任何一個頁面。事實上,Web應用在第二個用戶提供正確的驗證信息之前應當一直停留在登陸頁面上。
          通過示例程序,文章向您闡述了如何在一個Web應用中實現這一功能。

            JSP示例

            為了更為有效地闡述實現方案,本文將從展示一個示例應用logoutSampleJSP1中碰到的問題開始。這個示例代表了許多沒有正確解決退出過程的Web應用。logoutSampleJSP1包含了下述jsp頁面:login.jsp, home.jsp, secure1.jsp, secure2.jsp, logout.jsp, loginAction.jsp, and logoutAction.jsp。其中頁面home.jsp, secure1.jsp, secure2.jsp, 和logout.jsp是不允許未經認證的用戶訪問的,也就是說,這些頁面包含了重要信息,在用戶登陸之前或者退出之后都不應該出現在瀏覽器中。login.jsp包含了用于用戶輸入用戶名和密碼的form。logout.jsp頁包含了要求用戶確認是否退出的form。loginAction.jsp和logoutAction.jsp作為控制器分別包含了登陸和退出代碼。

            第二個示例應用logoutSampleJSP2展示了如何解決示例logoutSampleJSP1中的問題。然而,第二個應用自身也是有疑問的。在特定的情況下,退出問題還是會出現。

            第三個示例應用logoutSampleJSP3在第二個示例上進行了改進,比較完善地解決了退出問題。

            最后一個示例logoutSampleStruts展示了Struts如何優美地解決登陸問題。

            注意:本文所附示例在最新版本的Microsoft Internet Explorer (IE), Netscape Navigator, Mozilla, FireFox和Avant瀏覽器上測試通過。

            Login action

            Brian Pontarelli的經典文章《J2EE Security: Container Versus Custom》討論了不同的J2EE認證途徑。文章同時指出,HTTP協議和基于form的認證并未提供處理用戶退出的機制。因此,解決途徑便是引入自定義的安全實現機制。

            自定義的安全認證機制普遍采用的方法是從form中獲得用戶輸入的認證信息,然后到諸如LDAP (lightweight directory access protocol)或關系數據庫的安全域中進行認證。如果用戶提供的認證信息是有效的,登陸動作往HttpSession對象中注入某個對象。HttpSession存在著注入的對象則表示用戶已經登陸。為了方便讀者理解,本文所附的示例只往HttpSession中寫入一個用戶名以表明用戶已經登陸。清單1是從loginAction.jsp頁面中節選的一段代碼以此闡述登陸動作:

          Listing 1
          //...
          //initialize RequestDispatcher object; set forward to home page by default
          RequestDispatcher rd = request.getRequestDispatcher("home.jsp");

          //Prepare connection and statement
          rs = stmt.executeQuery("select password from USER where userName = '" + userName + "'");
          if (rs.next()) {
           //Query only returns 1 record in the result set; only 1
           password per userName which is also the primary key
           if (rs.getString("password").equals(password)) { //If valid password
            session.setAttribute("User", userName); //Saves username string in the session object
           }
           else { //Password does not match, i.e., invalid user password
            request.setAttribute("Error", "Invalid password.");

            rd = request.getRequestDispatcher("login.jsp");
           }
          } //No record in the result set, i.e., invalid username
          else {

           request.setAttribute("Error", "Invalid user name.");
           rd = request.getRequestDispatcher("login.jsp");
          }
          }

          //As a controller, loginAction.jsp finally either forwards to "login.jsp" or "home.jsp"
          rd.forward(request, response);
          //...

            本文所附示例均以關系型數據庫作為安全域,但本文所闡述的觀點對任何類型的安全域都是適用的。

            Logout action

            退出動作就包含了簡單的刪除用戶名以及對用戶的HttpSession對象調用invalidate()方法。清單2是從loginoutAction.jsp頁面中節選的一段代碼以此闡述退出動作:

          Listing 2
          //...
          session.removeAttribute("User");
          session.invalidate();
          //...

          阻止未經認證訪問受保護的JSP頁面

            從form中獲取用戶提交的認證信息并經過驗證后,登陸動作簡單地往 HttpSession對象中寫入一個用戶名,退出動作則做相反的工作,它從用戶的HttpSession對象中刪除用戶名并調用invalidate()方法銷毀HttpSession。為了使登陸和退出動作真正發揮作用,所有受保護的JSP頁面都應該首先驗證HttpSession中是否包含了用戶名以確認當前用戶是否已經登陸。如果HttpSession中包含了用戶名,也就是說用戶已經登陸,Web應用則將剩余的JSP頁發送給瀏覽器,否則,JSP頁將跳轉到登陸頁login.jsp。頁面home.jsp, secure1.jsp, secure2.jsp和logout.jsp均包含清單3中的代碼段:

          Listing 3
          //...
          String userName = (String) session.getAttribute("User");
          if (null == userName) {
           request.setAttribute("Error", "Session has ended. Please login.");
           RequestDispatcher rd = request.getRequestDispatcher("login.jsp");
           rd.forward(request, response);
          }
          //...
          //Allow the rest of the dynamic content in this JSP to be served to the browser
          //...

            在這個代碼段中,程序從HttpSession中減縮username字符串。如果字符串為空,Web應用則自動中止執行當前頁面并跳轉到登陸頁,同時給出Session has ended. Please log in.的提示;如果不為空,Web應用則繼續執行,也就是把剩余的頁面提供給用戶。

            運行logoutSampleJSP1

            運行logoutSampleJSP1將會出現如下幾種情形:

            1) 如果用戶沒有登陸,Web應用將會正確中止受保護頁面home.jsp, secure1.jsp, secure2.jsp和logout.jsp的執行,也就是說,假如用戶在瀏覽器地址欄中直接敲入受保護JSP頁的地址試圖訪問,Web應用將自動跳轉到登陸頁并提示Session has ended.Please log in.

            2) 同樣的,當一個用戶已經退出,Web應用也會正確中止受保護頁面home.jsp, secure1.jsp, secure2.jsp和logout.jsp的執行

            3) 用戶退出后,如果點擊瀏覽器上的后退按鈕,Web應用將不能正確保護受保護的頁面——在Session銷毀后(用戶退出)受保護的JSP頁重新在瀏覽器中顯示出來。然而,如果用戶點擊返回頁面上的任何鏈接,Web應用將會跳轉到登陸頁面并提示Session has ended.Please log in.

            阻止瀏覽器緩存

            上述問題的根源在于大部分瀏覽器都有一個后退按鈕。當點擊后退按鈕時,默認情況下瀏覽器不是從Web服務器上重新獲取頁面,而是從瀏覽器緩存中載入頁面。基于Java的Web應用并未限制這一功能,在基于PHP、ASP和.NET的Web應用中也同樣存在這一問題。

            在用戶點擊后退按鈕后,瀏覽器到服務器再從服務器到瀏覽器這樣通常意思上的HTTP回路并沒有建立,僅僅只是用戶,瀏覽器和緩存進行了交互。所以,即使包含了清單3上的代碼來保護JSP頁面,當點擊后退按鈕時,這些代碼是不會執行的。

            緩存的好壞,真是仁者見仁智者見智。緩存的確提供了一些便利,但通常只在使用靜態的HTML頁面或基于圖形或影響的頁面你才能感受到。而另一方面,Web應用通常是基于數據的,數據通常是頻繁更改的。與從緩存中讀取并顯示過期的數據相比,提供最新的數據才是更重要的!

            幸運的是,HTTP頭信息“Expires”和“Cache-Control”為應用程序服務器提供了一個控制瀏覽器和代理服務器上緩存的機制。HTTP頭信息Expires告訴代理服務器它的緩存頁面何時將過期。HTTP1.1規范中新定義的頭信息Cache-Control可以通知瀏覽器不緩存任何頁面。當點擊后退按鈕時,瀏覽器重新訪問服務器已獲取頁面。如下是使用Cache-Control的基本方法:

            1) no-cache:強制緩存從服務器上獲取新的頁面

            2) no-store: 在任何環境下緩存不保存任何頁面

            HTTP1.0規范中的Pragma:no-cache等同于HTTP1.1規范中的Cache-Control:no-cache,同樣可以包含在頭信息中。

            通過使用HTTP頭信息的cache控制,第二個示例應用logoutSampleJSP2解決了logoutSampleJSP1的問題。logoutSampleJSP2與logoutSampleJSP1不同表現在如下代碼段中,這一代碼段加入進所有受保護的頁面中:

          //...
          response.setHeader("Cache-Control","no-cache"); //Forces caches to obtain a new copy of the page from the origin server
          response.setHeader("Cache-Control","no-store"); //Directs caches not to store the page under any circumstance
          response.setDateHeader("Expires", 0); //Causes the proxy cache to see the page as "stale"
          response.setHeader("Pragma","no-cache"); //HTTP 1.0 backward compatibility
          String userName = (String) session.getAttribute("User");
          if (null == userName) {
           request.setAttribute("Error", "Session has ended. Please login.");
           RequestDispatcher rd = request.getRequestDispatcher("login.jsp");
           rd.forward(request, response);
          }
          //...

            通過設置頭信息和檢查HttpSession中的用戶名確保了瀏覽器不緩存頁面,同時,如果用戶未登陸,受保護的JSP頁面將不會發送到瀏覽器,取而代之的將是登陸頁面login.jsp。

            運行logoutSampleJSP2

            運行logoutSampleJSP2后將回看到如下結果:

            1) 當用戶退出后試圖點擊后退按鈕,瀏覽器并不會顯示受保護的頁面,它只會現實登陸頁login.jsp同時給出提示信息Session has ended. Please log in.

            2) 然而,當按了后退按鈕返回的頁是處理用戶提交數據的頁面時,IE和Avant瀏覽器將彈出如下信息提示:

            警告:頁面已過期……(你肯定見過)

            選擇刷新后前一個JSP頁面將重新顯示在瀏覽器中。很顯然,這不是我們所想看到的因為它違背了logout動作的目的。發生這一現象時,很可能是一個惡意用戶在嘗試獲取其他用戶的數據。然而,這個問題僅僅出現在后退按鈕對應的是一個處理POST請求的頁面。

            記錄最后登陸時間

            上述問題之所以出現是因為瀏覽器將其緩存中的數據重新提交了。這本文的例子中,數據包含了用戶名和密碼。無論是否給出安全警告信息,瀏覽器此時起到了負面作用。

            為了解決logoutSampleJSP2中出現的問題,logoutSampleJSP3的login.jsp在包含username和password的基礎上還包含了一個稱作lastLogon的隱藏表單域,此表單域動態的用一個long型值初始化。這個long型值是調用System.currentTimeMillis()獲取到的自1970年1月1日以來的毫秒數。當login.jsp中的form提交時,loginAction.jsp首先將隱藏域中的值與用戶數據庫中的值進行比較。只有當lastLogon表單域中的值大于數據庫中的值時Web應用才認為這是個有效的登陸。

            為了驗證登陸,數據庫中lastLogon字段必須以表單中的lastLogon值進行更新。上例中,當瀏覽器重復提交數據時,表單中的lastLogon值不比數據庫中的lastLogon值大,因此,loginAction轉到login.jsp頁面,并提示Session has ended.Please log in.清單5是loginAction中節選的代碼段:

            清單5

          //...
          RequestDispatcher rd = request.getRequestDispatcher("home.jsp"); //Forward to homepage by default
          //...
          if (rs.getString("password").equals(password)) {
           //If valid password
           long lastLogonDB = rs.getLong("lastLogon");
           if (lastLogonForm > lastLogonDB) {
            session.setAttribute("User", userName); //Saves username string in the session object
            stmt.executeUpdate("update USER set lastLogon= " + lastLogonForm + " where userName = '" + userName + "'");
           }
           else {
            request.setAttribute("Error", "Session has ended. Please login.");
            rd = request.getRequestDispatcher("login.jsp"); }
           }
           else { //Password does not match, i.e., invalid user password
            request.setAttribute("Error", "Invalid password.");
            rd = request.getRequestDispatcher("login.jsp");
           }
           //...
           rd.forward(request, response);
          //...

            為了實現上述方法,你必須記錄每個用戶的最后登陸時間。對于采用關系型數據庫安全域來說,這點可以可以通過在某個表中加上lastLogin字段輕松實現。LDAP以及其他的安全域需要稍微動下腦筋,但很顯然是可以實現的。

            表示最后登陸時間的方法有很多。示例logoutSampleJSP3利用了自1970年1月1日以來的毫秒數。這個方法在許多人在不同瀏覽器中用一個用戶賬號登陸時也是可行的。

            運行logoutSampleJSP3

            運行示例logoutSampleJSP3將展示如何正確處理退出問題。一旦用戶退出,點擊瀏覽器上的后退按鈕在任何情況下都不會是受保護的頁面在瀏覽器上顯示出來。這個示例展示了如何正確處理退出問題而不需要額外的培訓。

            為了使代碼更簡練有效,一些冗余的代碼可以剔除掉。一種途徑就是把清單4中的代碼寫到一個單獨的JSP頁中,通過標簽<jsp:include>其他頁面也可以引用。


            Struts框架下的退出實現

            與直接使用JSP或JSP/servlets相比,另一個可選的方案是使用Struts。為一個基于Struts的Web應用添加一個處理退出問題的框架可以優雅地不費氣力的實現。這部分歸功于Struts是采用MVC設計模式的因此將模型和視圖清晰的分開。另外,Java是一個面向對象的語言,其支持繼承,可以比JSP中的腳本更為容易地實現代碼重用。在Struts中,清單4中的代碼可以從JSP頁面中移植到Action類的execute()方法中。
          此外,我們還可以定義一個繼承Struts Action類的基本類,其execute()方法中包含了清單4中的代碼。通過使用類繼承機制,其他類可以繼承基本類中的通用邏輯來設置HTTP頭信息以及檢索HttpSession對象中的username字符串。這個基本類是一個抽象類并定義了一個抽象方法executeAction()。所有繼承自基類的子類都應實現exectuteAction()方法而不是覆蓋它。清單6是基類的部分代碼:

            清單6

          public abstract class BaseAction extends Action {
           public ActionForward execute(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response)
           throws IOException, ServletException {
            response.setHeader("Cache-Control","no-cache");
            //Forces caches to obtain a new copy of the page from the origin server
            response.setHeader("Cache-Control","no-store");
            //Directs caches not to store the page under any circumstance
            response.setDateHeader("Expires", 0); //Causes the proxy cache to see the page as "stale"
            response.setHeader("Pragma","no-cache"); //HTTP 1.0 backward compatibility

            if (!this.userIsLoggedIn(request)) {
             ActionErrors errors = new ActionErrors();
             errors.add("error", new ActionError("logon.sessionEnded"));
             this.saveErrors(request, errors);
             return mapping.findForward("sessionEnded");
            }
            return executeAction(mapping, form, request, response);
           }

           protected abstract ActionForward executeAction(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)
           throws IOException, ServletException;

           private boolean userIsLoggedIn(HttpServletRequest request) {
            if (request.getSession().getAttribute("User") == null) {
             return false;
            }

            return true;
           }
          }

            清單6中的代碼與清單4中的很相像,僅僅只是用ActionMapping findForward替代了RequestDispatcher forward。清單6中,如果在HttpSession中未找到username字符串,ActionMapping對象將找到名為sessionEnded的forward元素并跳轉到對應的path。如果找到了,子類將執行其實現了executeAction()方法的業務邏輯。因此,在配置文件struts-web.xml中為所有子類聲明個一名為sessionEnded的forward元素是必須的。清單7以secure1 action闡明了這樣一個聲明:

            清單7

          <action path="/secure1"
          type="com.kevinhle.logoutSampleStruts.Secure1Action"
          scope="request">
          <forward name="success" path="/WEB-INF/jsps/secure1.jsp"/>
          <forward name="sessionEnded" path="/login.jsp"/>
          </action>

            繼承自BaseAction類的子類Secure1Action實現了executeAction()方法而不是覆蓋它。Secure1Action類不執行任何退出代碼,如清單8:

          public class Secure1Action extends BaseAction {
           public ActionForward executeAction(ActionMapping mapping, ActionForm form,
          HttpServletRequest request, HttpServletResponse response)
           throws IOException, ServletException {

            HttpSession session = request.getSession();
            return (mapping.findForward("success"));
           }
          }

            只需要定義一個基類而不需要額外的代碼工作,上述解決方案是優雅而有效的。不管怎樣,將通用的行為方法寫成一個繼承StrutsAction的基類是許多Struts項目的共同經驗,值得推薦。

            結論

            本文闡述了解決退出問題的方案,盡管方案簡單的令人驚訝,但卻在所有情況下都能有效地工作。無論是對JSP還是Struts,所要做的不過是寫一段不超過50行的代碼以及一個記錄用戶最后登陸時間的方法。在Web應用中混合使用這些方案能夠使擁護的私人數據不致泄露,同時,也能增加用戶的經驗。
          posted @ 2005-11-14 16:48 rkind 閱讀(225) | 評論 (0)編輯 收藏

          Beanutils用了魔術般的反射技術,實現了很多夸張有用的功能,都是C/C++時代不敢想的。無論誰的項目,始終一天都會用得上它。我算是后知后覺了,第一回看到它的時候居然錯過。

          1.屬性的動態getter,setter

          在這框架滿天飛的年代,不能事事都保證執行getter,setter函數了,有時候屬性是要需要根據名字動態的取得的,就像這樣:

            
          BeanUtils.getProperty(myBean,"code");
          
          而BeanUtils更強的功能是直接訪問內嵌對象的屬性,只要使用逗號分割。
          BeanUtils.getProperty(orderBean, "address.city");

          其他類庫的BeanUtils通常都很簡單,不能訪問內嵌的對象,所以經常要用Jakata的BeanUtils替換它們。

          BeanUtils還支持List和Map類型的屬性。如下面的語法即可取得顧客列表中第一個顧客的名字

          BeanUtils.getProperty(orderBean, "customers[1].name");
          

          其中BeanUtils會使用ConvertUtils類把字符串轉為Bean屬性的真正類型,方便從HttpServletRequest等對象中提取bean,或者把bean輸出到頁面。

          而PropertyUtils就會原色的保留Bean原來的類型。

          2.beanCompartor 動態排序

          還是通過反射,動態設定Bean按照哪個屬性來排序,而不再需要在bean的Compare接口進行復雜的條件判斷。 List peoples = ...; // Person對象的列表 Collections.sort(peoples, new BeanComparator("age")); }}} 如果要支持多個屬性的復合排序,如"Order By lastName,firstName"

          ArrayList sortFields = new ArrayList();
          sortFields.add(new BeanComparator("lastName"));
          sortFields.add(new BeanComparator("firstName"));
          ComparatorChain multiSort = new ComparatorChain(sortFields);
          Collections.sort(rows,multiSort);
          
          其中ComparatorChain屬于jakata commons-collections包。

          如果age屬性不是普通類型,構造函數需要再傳入一個comparator對象為age變量排序。

          3.Converter 把Request中的字符串轉為實際類型對象

          經常要從request,resultSet等對象取出值來賦入bean中,下面的代碼誰都寫膩了,如果不用MVC框架的綁定功能的話。
             String a = request.getParameter("a");
             bean.setA(a);
             String b = ....
          
          不妨改為
               MyBean bean = ...;
              HashMap map = new HashMap();
              Enumeration names = request.getParameterNames();
              while (names.hasMoreElements())
              {
                String name = (String) names.nextElement();
                map.put(name, request.getParameterValues(name));
              }
              BeanUtils.populate(bean, map);
          
          其中BeanUtils的populate方法或者getProperty,setProperty方法其實都會調用convert進行轉換。

          但Converter只支持一些基本的類型,甚至連java.util.Date類型也不支持。而且它比較笨的一個地方是當遇到不認識的類型時,居然會拋出異常來。

          對于Date類型,我參考它的sqldate類型實現了一個Converter,而且添加了一個設置日期格式的函數。要把這個Converter注冊,需要如下語句:

          ConvertUtilsBean convertUtils = new ConvertUtilsBean();
              DateConverter dateConverter = new DateConverter();
              convertUtils.register(dateConverter,Date.class);
               //因為要注冊converter,所以不能再使用BeanUtils的靜態方法了,必須創建BeanUtilsBean實例 
               BeanUtilsBean beanUtils = new BeanUtilsBean(convertUtils,new PropertyUtilsBean());      
               beanUtils.setProperty(bean, name, value);
          
          !4 其他功能 
          
          4.1 PropertyUtils,當屬性為Collection,Map時的動態讀取:
           
          Collection: 提供index
          {{{   BeanUtils.getIndexedProperty(orderBean,"items",1);
          或者
            BeanUtils.getIndexedProperty(orderBean,"items[1]");

          Map: 提供Key Value

          BeanUtils.getMappedProperty(orderBean, "items","111");//key-value goods_no=111 

          或者

          BeanUtils.getMappedProperty(orderBean, "items(111)");
          4.2 PropertyUtils,獲取屬性的Class類型

               public static Class getPropertyType(Object bean, String name)

          4.3 ConstructorUtils,動態創建對象

                public static Object invokeConstructor(Class klass, Object arg)

          4.4 DynaClass動態類

          其中ComparatorChain屬于jakata commons-collections包。 經常要從request,resultSet等對象取出值來賦入bean中,下面的代碼誰都寫膩了,如果不用MVC框架的綁定功能的話。 不妨改為 其中BeanUtils的populate方法或者getProperty,setProperty方法其實都會調用convert進行轉換。 或者 4.2 PropertyUtils,獲取屬性的Class類型
          posted @ 2005-11-14 15:12 rkind 閱讀(151) | 評論 (0)編輯 收藏
           

          Struts的Token(令牌)機制能夠很好的解決表單重復提交的問題,基本原理是:服務器端在處理到達的請求之前,會將請求中包含的令牌值與保存在當前用戶會話中的令牌值進行比較,看是否匹配。在處理完該請求后,且在答復發送給客戶端之前,將會產生一個新的令牌,該令牌除傳給客戶端以外,也會將用戶會話中保存的舊的令牌進行替換。這樣如果用戶回退到剛才的提交頁面并再次提交的話,客戶端傳過來的令牌就和服務器端的令牌不一致,從而有效地防止了重復提交的發生。

          這時其實也就是兩點,第一:你需要在請求中有這個令牌值,請求中的令牌值如何保存,其實就和我們平時在頁面中保存一些信息是一樣的,通過隱藏字段來保存,保存的形式如: 〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉,這個value是TokenProcessor類中的generateToken()獲得的,是根據當前用戶的session id和當前時間的long值來計算的。第二:在客戶端提交后,我們要根據判斷在請求中包含的值是否和服務器的令牌一致,因為服務器每次提交都會生成新的Token,所以,如果是重復提交,客戶端的Token值和服務器端的Token值就會不一致。下面就以在數據庫中插入一條數據來說明如何防止重復提交。

          在Action中的add方法中,我們需要將Token值明確的要求保存在頁面中,只需增加一條語句:saveToken(request);,如下所示:
          public ActionForward add(ActionMapping mapping, ActionForm form,
          HttpServletRequest request, HttpServletResponse response)
          //前面的處理省略
          saveToken(request);
          return mapping.findForward("add");
          }
          在Action的insert方法中,我們根據表單中的Token值與服務器端的Token值比較,如下所示:
          public ActionForward insert(ActionMapping mapping, ActionForm form,
          HttpServletRequest request, HttpServletResponse response)
          if (isTokenValid(request, true)) {
          // 表單不是重復提交
          //這里是保存數據的代碼
          } else {
          //表單重復提交
          saveToken(request);
          //其它的處理代碼
          }
          }

          其實使用起來很簡單,舉個最簡單、最需要使用這個的例子:
          一般控制重復提交主要是用在對數據庫操作的控制上,比如插入、更新、刪除等,由于更新、刪除一般都是通過id來操作(例如:updateXXXById, removeXXXById),所以這類操作控制的意義不是很大(不排除個別現象),重復提交的控制也就主要是在插入時的控制了。

          先說一下,我們目前所做項目的情況:
          目前的項目是用Struts+Spring+Ibatis,頁面用jstl,Struts復雜View層,Spring在Service層提供事務控制,Ibatis是用來代替JDBC,所有頁面的訪問都不是直接訪問jsp,而是訪問Structs的Action,再由Action來Forward到一個Jsp,所有針對數據庫的操作,比如取數據或修改數據,都是在Action里面完成,所有的Action一般都繼承BaseDispatchAction,這個是自己建立的類,目的是為所有的Action做一些統一的控制,在Struts層,對于一個功能,我們一般分為兩個Action,一個Action里的功能是不需要調用Struts的驗證功能的(常見的方法名稱有add,edit,remove,view,list),另一個是需要調用Struts的驗證功能的(常見的方法名稱有insert,update)。

          就拿論壇發貼來說吧,論壇發貼首先需要跳轉到一個頁面,你可以填寫帖子的主題和內容,填寫完后,單擊“提交”,貼子就發表了,所以這里經過兩個步驟:
          1、轉到一個新增的頁面,在Action里我們一般稱為add,例如:
          public ActionForward add(ActionMapping mapping, ActionForm form,
                      HttpServletRequest request, HttpServletResponse response)
                      throws Exception {
                  //這一句是輸出調試信息,表示代碼執行到這一段了
                  log.debug(":: action - subject add");    
              
                  //your code here

                 //這里保存Token值
                  saveToken(request);
                  
                 //跳轉到add頁面,在Structs-config.xml里面定義,例如,跳轉到subjectAdd.jsp
                  return mapping.findForward("add");
              }

          2、在填寫標題和內容后,選擇 提交 ,會提交到insert方法,在insert方法里判斷,是否重復提交了。
          public ActionForward insert(ActionMapping mapping, ActionForm form,
          HttpServletRequest request, HttpServletResponse response){
          if (isTokenValid(request, true)) {
          // 表單不是重復提交
          //這里是保存數據的代碼
          } else {
          //表單重復提交
          saveToken(request);
          //其它的處理代碼
          }
          }

          下面更詳細一點(注意,下面所有的代碼使用全角括號):
          1、你想發貼時,點擊“我要發貼”鏈接的代碼可以里這樣的:
          〈html:link action="subject.do?method=add"〉我要發貼〈/html:link〉
          subject.do 和 method 這些在struct-config.xml如何定義我就不說了,點擊鏈接后,會執行subject.do的add方法,代碼如上面說的,跳轉到subjectAdd.jsp頁面。頁面的代碼大概如下:
          〈html:form action="subjectForm.do?method=insert"〉
            〈html:text property="title" /〉
            〈html:textarea property="content" /〉
            〈html:submit property="發表" /〉
            〈html:reset property="重填" /〉
          〈html:form〉
          如果你在add方法里加了“saveToken(request);”這一句,那在subjectAdd.jsp生成的頁面上,會多一個隱藏字段,類似于這樣〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉,

          2、點擊發表后,表單提交到subjectForm.do里的insert方法后,你在insert方法里要將表單的數據插入到數據庫中,如果沒有進行重復提交的控制,那么每點擊一次瀏覽器的刷新按鈕,都會在數據庫中插入一條相同的記錄,增加下面的代碼,你就可以控制用戶的重復提交了。
          if (isTokenValid(request, true)) {
          // 表單不是重復提交
          //這里是保存數據的代碼
          } else {
          //表單重復提交
          saveToken(request);
          //其它的處理代碼
          }
          注意,你必須在add方法里使用了saveToken(request),你才能在insert里判斷,否則,你每次保存操作都是重復提交。
          記住一點,Struts在你每次訪問Action的時候,都會產生一個令牌,保存在你的Session里面,如果你在Action里的函數里面,使用了saveToken(request);,那么這個令牌也會保存在這個Action所Forward到的jsp所生成的靜態頁面里。
          如果你在你Action的方法里使用了isTokenValid,那么Struts會將你從你的request里面去獲取這個令牌值,然后和Session里的令牌值做比較,如果兩者相等,就不是重復提交,如果不相等,就是重復提交了。

          由于我們項目的所有Action都是繼承自BaseDispatchAction這個類,所以我們基本上都是在這個類里面做了表單重復提交的控制,默認是控制add方法和insert方法,如果需要控制其它的方法,就自己手動寫上面這些代碼,否則是不需要手寫的,控制的代碼如下:
          public abstract class BaseDispatchAction extends BaseAction {
          protected ActionForward perform(ActionMapping mapping, ActionForm form,
              HttpServletRequest request, HttpServletResponse response)
              throws Exception {
                  String parameter = mapping.getParameter();
                  String name = request.getParameter(parameter);
          if (null == name) { //如果沒有指定 method ,則默認為 list
                      name = "list";
                  }

                  if ("add".equals(name)) {
                      if ("add".equals(name)) {
                          saveToken(request);
                      }
                  } else if ("insert".equals(name)) {
                      if (!isTokenValid(request, true)) {
                          resetToken(request);
                          saveError(request, new ActionMessage("error.repeatSubmit"));
                          log.error("重復提交!");
                          return mapping.findForward("error");
                      }
                  }
                  return dispatchMethod2(mapping, form, request, response, name);
              }
          }

          posted @ 2005-11-14 14:32 rkind 閱讀(320) | 評論 (0)編輯 收藏

          SELECT COUNT(*) AS Expr1 FROM History

          DELETE FROM History WHERE (Id > 0)

          truncate table youtable;

          為什么要用TRUNCATE TABLE 語句代替DELETE語句?當你使用TRUNCATE TABLE語句時,記錄的刪除是不作記錄的。也就是說,這意味著TRUNCATE TABLE 要比DELETE快得多。

          posted @ 2005-11-11 11:25 rkind 閱讀(298) | 評論 (0)編輯 收藏
           我今年39歲了,25歲研究生畢業,工作14年,回頭看看,應該說走了不少的彎路,有一些經驗和教訓。現在開一個小公司,賺的錢剛夠養家糊口的。看看這些剛畢業的學生,對前景也很迷茫,想拋磚引玉,談談自己的看法,局限于理工科的學生,我對文科的不懂,身邊的朋友也沒有這一類型的。

            91年研究生畢業,那時出路就是1種:留在北京的國營單位,搞一個北京戶口,這是最好的選擇。到后來的2~3年內,戶口落定了,又分成4條出路:

            1、 上國內的大企業,如:華為

            2、 自己做公司,做產品開發;

            3、 上外企,比如:愛立信、諾基亞

            4、 自己做公司,做買賣;

            5、 移民加拿大

            我想,首先要看自己適合做什么?做技術還是做買賣。

            做技術,需要你對技術感興趣。我掰著數了一遍,我們研究生班的30來號人,實際上,適合做技術的,大概只有3、4個人,這幾個人,1個現在還在華為,3個移民加拿大了,現在這4個人混的還可以,在華為的同學也移民加拿大了,他在華為呆了6年,在華為獎金工資加起來大概30萬吧,還有華為的股票,再過幾年,華為的股票一上市,也能值個100~200萬。要是一畢業就去華為,那現在就絕對不是這個數字了。

            要是做技術,最好的就是上大公司,國內的大型企業,象華為中興肯定是首選,能學到很多東西。華為雖然累,但是,年輕人不能怕累,要是到老了,還需要去打拼,那才是真的累啊。

            在外企,我想他們主要就是技術支持和銷售,但是技術是學不到的,當然不能一概而論,我指的是象愛立信和諾基亞,真正的研發不會在中國做的,學到的也不如在華為多,其它的中興我不是很了解,我想應該也不錯啊。

            一個人都有一技之長,有傍身之技,那是最好的,走到哪里,都能有一口飯吃,還吃的不錯,這是傳統的觀點。

            任何技術都是要在某個行業去應用,這個行業市場越大當然越好;要在一個領域之內,做深做精,成為絕對的專家,這是走技術道路的人的選擇。不要跳來跳去,在中國,再小的行業你要做精深了,都可以產生很大的利潤。

            研究生剛畢業的時候,做產品開發的有不少人,都是自己拍拍腦子,覺得這個產品有市場,就自己出來做。現在看來,我的這些同學,做產品開發的成功的沒有一例,為什么?資源不足。

            1. 資金,剛畢業的學生啊,就是沒錢;沒錢,也意味著你開發的東西都是小產品;而且只能哥幾個自己上,研發、生產、銷售都是一個人或者幾個人自己來,沒有積累,什么都是重新來過。

            2. 人脈,任何一個行業,要想進去,需要有很深的人脈,否則,誰會用你的東西啊?誰敢用你的東西啊?

            我看到的,我這個班上開發產品的,自己還在堅持的,只剩下一個人了,說實在的,到現在,沒有自己的汽車,也沒有自己的房子,混的挺慘的。現在出國的不說了,在外企、在華為,至少都是幾十萬的年薪了,還有各種福利,就是產品開發成功了,又能如何?也就是這樣了,但是以前那些年,都沒有金錢的積累,等于白干。

            我身邊的一個自動化系的研究生班的同學,能靠自己開發產品活得還可以的,也只有2個人。說明這條路不是那么好走的啊。

            其次就是上外企。我的2個同學,一個上了愛立信,一個先到愛立信后到諾基亞,都混的不錯。到諾基亞的后來利用在諾基亞結識的人脈(就是哪些電信的頭頭腦腦),自己開了公司,也賺了不少的錢。

            外企最大的好處就是除了能學到比較規范的管理外,還能給你的職業生涯鍍金。到了一個外企外,再到同行業的外企我想就很容易了。而且外企的收入高啊。

            自己做公司,做買賣,一開始有3~4個人走這條路,但是真正發財的只有一個人,其他人后來上外企了。做買賣,還是要有一定的天賦,還有機遇。要有對金錢的赤裸裸的欲望,要有商業上的頭腦。后來我們同學在一起談,說,我們即使給自己這個機遇,也未必能做的好。何況當時那個同學看好的產品(做一個臺灣產品的代理),我們大家都沒有看好,說明,真理還是掌握在少數人手里。

            到后來,同學們紛紛移民移民加拿大。

            移民加拿大對搞技術的人來說,還是一個不錯的選擇,但是要盡早,練了幾年的技術,就趕緊出去,大概是在1996年走了不到10個,現在都還可以,買了房子和車了。要是晚了,語言再學也難了,而且在國內都混的還可以了,也就沒有必要出去了。

            我自己呢,先是在國營的研究所混了4年,后來到一家公司干了6年,2002年出來自己做公司,現在也就是混了一個溫飽吧,算是有房有車,有點積蓄,但是不多,還有一個可愛的女兒。回首這10來年,有一些經驗和教訓。
            1. 要有一個職業生涯的規劃。首先需要定位自己做什么合適,是做買賣還是做技術,一條路走到黑;當然,做了技術,后來改行也行;

            2. 做技術,就是要做精做深,成為這個行業的這個技術的專家;最好就是去國內的大公司,才能全面學到東西,能夠給你培訓的機會;如果大公司進不去,先到小公司練技術,找機會再到大公司去鍍金,學高深的技術。千萬不要自己做產品,要做也是對這個行業熟悉了,再去做。

            3. 積極爭取機會。積極爭取學習和進步的機會。比如,做技術,就需要多鍛煉,多學習,來提高自己的水平。一門技術,只要有機會去學習,都能學的會;要是沒有機會,天才也沒有辦法學到這個技術。柳傳志就說,楊元慶就是“哭著喊著要進步”,實際上,就是爭取自己的機會;當然,這種強烈的進步欲望,也是領導看重的地方。每一步都走在前面,積累10年,你就有了比其他人更多的機會了。

            4. 積累個人的信譽。從你的職業生涯的第一天,就要按照誠信的原則辦事。要做到,當人們提起你的名字的時候,說,這哥們還不錯,做事還行。

            5. 注意利用資源。如果你有有錢的親戚、成功的長輩或者朋友,可以充分利用這些機會,得到更加順利的發展前景。

            6. 注意財富的不斷積累。人生要想得到自由,財富是很關鍵的。否則,永遠仰人鼻息,永遠看人臉色。人都是勢利眼。今后的家庭、職業生涯,金錢的積累很重要,沒有錢,永遠不能開張自己的事業,得到更多的機會;財富要做到逐年積累,你才能家庭生活幸福。沒有錢是不可能有幸福的家庭的。

            7. 注意人脈的積累。最終,事業要靠在社會上的人脈的資源。要注意認識在你這個行業的人,結交他們,最終他們會成為你事業上的助力。

            8. 尋求貴人相助。要找大老板來幫助你,得到大老板的賞識。想想看,大蛋糕,切一點就夠了,小蛋糕,都給你也吃不飽啊。

            9. 多聽聽成功的前輩和成功的朋友的意見。注意少聽家里長輩的意見,尤其是都已經退休的長輩,他們對社會的認識還停留在很久以前,而這個社會已經發生很大的變化呢。最重要的是,長輩有時候會強求你做一些事情,但是,最終的結果他們是不負責的。只有你才能對自己負責。
          posted @ 2005-11-10 15:02 rkind 閱讀(256) | 評論 (0)編輯 收藏

          滾動公告欄的實現
          用到如下html標簽:
          《MARQUEE id=mar onmouseout=this.start(); direction=up height=150 name="mar"
                     onmouseover="this.stop();" scrollAmount="1" scrollDelay="0"
                     style="LINE-HEIGHT: 100%; PADDING-LEFT: 0px; PADDING-RIGHT:0px"width="100%">
                1凈化社會環境

          2也是凈化社會平步青云

          3凈化社會環境

          4也是凈化社會平步青云

          5凈化社會環境

          6也是凈化社會平步青云

          7凈化社會環境

          8也是凈化社會平步青云

             

          其中marquee中height中的屬性可以更改每次顯示的行數,……

          1凈化社會環境
          2也是凈化社會平步青云
          3凈化社會環境
          4也是凈化社會平步青云
          5凈化社會環境
          6也是凈化社會平步青云
          7凈化社會環境
          8也是凈化社會平步青云
          posted @ 2005-11-02 08:43 rkind 閱讀(429) | 評論 (0)編輯 收藏
          天涼好個秋
          對我來說,如果只是降溫的話,我未必會覺得秋來了,可是昨天早上走在路上,看到葉子在空中飛舞,地上布滿了落葉,秋終于還是來了,這一年也快要過去了,我做了什么,我還能做什么。

          昨天新裝了個系統,換了個mac的主題,用著很舒服,不過裝別的軟件又浪費了半天的時候,終究還是忘了做個鏡像了,裝好Sever-u后,測試什么問題都沒有,后來從別的機子那里一訪問,居然不行,懷疑卡巴的事,這個殺毒軟件太BT,不懷疑不行啊,關了以后還是不行,難道是windows防火墻?禁掉果然OK
          ,那總不能禁掉它吧,暫且不說那個提示很煩,別外要裝防火墻是讓我很不爽的事,一共就這么點內存,哪夠用啊。看看防火墻里面有個例外選項,我把端口20,21,22,全都加上了,結果只能連上,但是不能建立socket,讓人不爽的是,居然不能范圍添加端口。

          然后選例外程序,是在這里,server-u目錄下有三個程序文件tray.exe有來系統托盤監控,dameon主程序后臺運行,admini管理,這里我把dameon加上以后,一連接,果然ok,

          起先加錯了,加成tray.exe和admin,就沒加dameon,結果怎么試都不行,還上網查了好長時間,真是……
          看來我的英語,

          posted @ 2005-10-28 09:21 rkind 閱讀(1506) | 評論 (2)編輯 收藏
          僅列出標題
          共8頁: 上一頁 1 2 3 4 5 6 7 8 下一頁 
          主站蜘蛛池模板: 邓州市| 秀山| 湟源县| 河南省| 海兴县| 遂宁市| 巴林右旗| 神农架林区| 巧家县| 上高县| 垣曲县| 湖州市| 安阳县| 灵台县| 巧家县| 吐鲁番市| 灯塔市| 庆安县| 黑河市| 时尚| 乌什县| 贡觉县| 衡阳市| 万全县| 科技| 绿春县| 榆社县| 保靖县| 北票市| 长沙县| 五台县| 汝阳县| 中卫市| 平泉县| 汝州市| 沙雅县| 新源县| 五莲县| 汶上县| 厦门市| 淅川县|