PariScamper的java天空

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            14 Posts :: 0 Stories :: 7 Comments :: 0 Trackbacks

          2007年10月11日 #

          以下文章在我配置vsftpd時有幫助,特轉載:

          關于“vsftpd 部分本地用戶不能登錄,部分可以”的問題,我重新做了一些實驗,我把這個問題結合實驗的結果再重新描述一下,請各位高人,幫忙看看可能的原因。 謝謝了!

          系統中原來就有的本地帳號都不能登錄,我的/etc/vsftpd/vsftpd.conf文件的配置如下:
          local_enable=YES
          write_enable=YES
          chroot_local_user=YES
          pam_service_name=vsftpd
          /etc/pam.d/vsftpd存在且正常。

          登錄時錯誤信息都是一樣的:
          500 OOPS: cannot change directory:/home/xxxx
          Login failed.
          421 Service not available, remote server has closed connection

          他們的home目錄都是/home/xxxx。/home和/home/xxxx的權限都是755。
          以上這些帳號都不能ftp登錄,這些都是平常經常使用的,可以用shell登錄的。

          我新創建了一個usr1帳號,
          # useradd -G test -d /tmp/usr1 usr1
          能ftp登錄,他的home為/tmp/usr1,在/分區上。而/home我是mount到/dev/hda9上的。
          #mount
          /dev/hdb1 on / type ext3 (rw)
          /dev/hda9 on /home type ext2 (rw)

          所以,我猜想:是否是由于/home分區的原因,而造成“主目錄在/home分區的帳號”都不能登錄呢?

          為了驗證以上設想,我試著再創建了一個帳號,
          useradd -G test -d /home/usr3 usr3
          /home, /home/usr3 的權限都是755。

          usr3 ftp登錄失敗。
          500 OOPS: cannot change directory:/home/usr3
          Login failed.
          421 Service not available, remote server has closed connection

          至此,我覺得可以確定是由于/home分區的原因,而造成“主目錄在/home分區的帳號”都不能登錄。
          參考文章:
          -----------------------------------------------------------------------------------------
          I finished my second upgrade to Fedora Core 4. Not everything is ironed out yet with the build of course. But one thing is for sure a lot has happened to the RedHat I knew before.

          I must say of all the changes, for me the nicest addition is the new SELinux extensions. For deep background on the reasons for and theory of SELinux read, The Inevitability of Failure: The Flawed Assumption of Security in Modern Computing Environments

          The more I work with SELinux the more I realize I need to know about it, and how exactly it does all its stuff. It certainly changes things relating to users, directories and access. As I am starting to learn it, I'm sure I'm doing things the hard-way. :)

          The major difference, so far for me, in Red Hat's SELinux is the way ftp is handled. vsftpd is still the server which is great. However, it seems to be designed to run as a daemon rather than invoked via xinet.d. If you grab a working copy of the xinet.d file for vsftpd you can invoke it via xinet.d wrapper. I did my first server upgrade in this manner. The current one I am trying as a daemon. I certainly think I will miss some of the features that the xinet.d wrapper brings, and may yet return to it.

          Of all the issues I saw most notable is if you want to enable chroot directory's outside of the normal /home/xxx vsftpd. These will fail with a

              500 OOPS: cannot change directory: /mnt/xxxxx

          I was able to use ftp if I logged in with an account with a directory in /home, but once I set a user account to have a home drive outside of /home (in this case on a mounted secondary disk) vsftpd barfs the above.


          I found information at the NSA that indicates you can disable SELinux protection of the ftp daemon.

              setsebool -P ftpd_disable_trans 1

          This seems a bit drastic. It certainly works for now though.

          I think ultimately the issue resides with policies, but as SELinux policies are new to me, it will take time before it all gets sorted out. As I spend time with the new SELinux extensions in Fedora Core 4 I will keep you updated on my thoughts and configuration lessons.


          ---------------------------------------------------------------------------------------

          解決辦法:
          --------------------------------------------------------------------------------------
           # setsebool ftpd_disable_trans 1
            # service vsftpd restart
          我用的是FC4,按照你上一帖子里的方法試了,馬上就解決了。所以,可以確定原因就在SELinux。
          ------------------------------------------------------------------------------------

          posted @ 2008-02-27 22:45 PariScamper 閱讀(448) | 評論 (0)編輯 收藏

          1          什么是 Clone ,容易實現嗎?

          簡單地說, Clone 就是對于給定的一個對象實例 o ,得到另一個對象實例 o’ o o’

          型相同( o.getClass() == o’.getClass() ),內容相同(對于 o/o’ 中的字段 f ,如果 f 是基本數據類型,則 o.f == o’.f ;如果 f 是對象引用,則 o.f == o’.f o.f 指向的對象與 o’.f 指向的對象的內容相同)。通常稱 o’ o 的克隆或副本。

                 直觀上看,似乎很容易為一個類加上 clone 方法:

          class A {

                 private Type1 field1;

              private Type2 field2;

               …..

              public Object clone() {

                        A a = new A();

                  a.field1 = a.getField1();

                  a.field2 = a.getField2();

                  ……

                  return a;

              }

          }

           

          然而,稍加推敲,就會發現這樣的實現方法有兩個問題:

          1.         要想某個類有 clone 功能,必須單獨為其實現 clone() 函數,函數實現代碼與該類定義密切相關。

          2.         即使基類 A 已有 clone() 函數,其子類 ExtendA 若要具備 clone 功能,則必須 override 其基類 A clone() 函數。否則,對類型為 ExtendA 的對象 ea clone() 方法的調用,會執行于類 A 中定義的 clone() 方法而返回一個類型為 A 的對象,它顯然不是 ea 的克隆。

          2          Java clone 的支持

          萬類之初的 Object 類有 clone() 方法:

          protected native Object clone() throws CloneNotSupportedException;

          該方法是 protected 的,顯然是留待被子類 override 的。該方法又是 native 的,必然做了

          與具體平臺相關的底層工作。

          事實上,類 Object clone() 方法首先會檢查 this.getClass() 是否實現了 Cloneable 接口。

          Cloneable 只是一個標志接口而已,用來標志該類是否有克隆功能。

          public interface Cloneable {

          }

                 如果 this.getClass() 沒有實現 Cloneable 接口, clone() 就會拋 CloneNotSupportedException 返回。否則就會創建一個類型為 this.getClass() 的對象 other ,并將 this field 的值賦值給 other 的對應 field ,然后返回 other 。

                 如此一來,我們要定義一個具有 Clone 功能的類就相當方便:

          1.         在類的聲明中加入“ implements Cloneable ”,標志該類有克隆功能;

          2.         Override Object clone() 方法,在該方法中調用 super.clone()

          class CloneableClass implements Cloneable {

                 ……

          public Object clone() {

                 try {

                        return super.clone(); // 直接讓 Object.clone() 為我們代勞一切

              } catch (CloneNotSupportedException e) {

                        throw new InternalError();

                 }

          }

          }

           

          3          Shallow Clone Deep Clone

          3.1     Shallow Deep 從何而來

          一個具有克隆功能的類,如果有可變( Mutable )類類型的字段 field ,如何為其克隆(副

          本)對象 o’ 中的 field 賦值?

                 方法一、如 Object clone() 方法所實現:設原始對象為 o ,其克隆對象是 o’ ,執行 o’.field = o.field 。這樣, o’.field o.field 指向同一個可變對象 m 。 o o’ 可能會相互影響(一個對象的狀態可能會隨著另一個對象的狀態的改變而改變)。這樣的 Clone 稱為 Shallow Clone 。這也是 Object clone() 方法的實現方式。

                 方法二、將 o.field 指向的可變對象 m 克隆,得到 m’ ,將 m’ 的引用賦值給 o’.field 。這樣 o’ o 內容相同,且相互之間無影響(一個對象狀態的改變不會影響另一個對象的狀態)。這樣的 Clone 稱為 Deep Clone 。

                 Java Collection 類庫中具體數據結構類( ArrayList/LinkedList HashSet/TreeSet , HashMap/TreeMap 等)都具有克隆功能,且都是 Shallow Clone ,這樣設計是合理的,因為它們不知道存放其中的每個數據對象是否也有克隆功能。 System.arrayCopy() 的實現采用的也是 Shallow Clone

                 Deep Clone 對于實現不可變( Immutable )類很有幫助。設一個類包含可變類 M 類型的 field ,如何將其設計為不可變類呢?先為 M 實現 Deep Clone 功能,然后這樣設計類 ImmutableClass

          class ImmutableClass {

                 MutableClass m;

          ImmutableClass(MutableClass m) {

                 this.m = m.clone(); // 將傳入的 m clone 賦值給內部 m

          }

          public MutableClass getM() {

              return this.m.clone(); // 將內部 m clone 返回給外部

          }

          }

           

                

          3.2     如何實現 Deep Clone

          檢查類有無可變類類型的字段。如果無,返回 super.clone() 即可;

          如果有,確保包含的可變類本身都實現了 Deep Clone

          Object o = super.clone(); // 先執行淺克隆,確保類型正確和基本類型及非可變類類型字段內容正確

          對于每一個可變類類型的字段 field

                 o.field = this.getField().clone();

          返回 o 。

          posted @ 2007-12-12 14:49 PariScamper 閱讀(757) | 評論 (0)編輯 收藏

          什么要用日志(Log?
          這個……就不必說了吧。

          為什么不用System.out.println()?
          功能太弱;不易于控制。如果暫時不想輸出了怎么辦?如果想輸出到文件怎么辦?如果想部分輸出怎么辦?……

          為什么同時使用commons-loggingLog4j?為什么不僅使用其中之一?
          Commons-loggin的目的是為“所有的Java日志實現”提供一個統一的接口,它自身的日志功能平常弱(只有一個簡單的SimpleLog?),所以一般不會單獨使用它。

          Log4j的功能非常全面強大,是目前的首選。我發現幾乎所有的Java開源項目都會用到Log4j,但我同時發現,所有用到Log4j的項目一般也同時會用到commons-loggin。我想,大家都不希望自己的項目與Log4j綁定的太緊密吧。另外一個我能想到的“同時使用commons-loggingLog4j”的原因是,簡化使用和配置。

                 強調一點,“同時使用commons-loggingLog4j”,與“單獨使用Log4j”相比,并不會帶來更大的學習、配置和維護成本,反而更加簡化了我們的工作。我想這也是為什么“所有用到Log4j的項目一般也同時會用到commons-loggin”的原因之一吧。

          Commons-logging能幫我們做什么?
          l         提供一個統一的日志接口,簡單了操作,同時避免項目與某個日志實現系統緊密a耦合
          l         很貼心的幫我們自動選擇適當的日志實現系統(這一點非常好?。?/span>
          l         它甚至不需要配置

          這里看一下它怎么“‘很貼心的’幫我們‘自動選擇’‘適當的’日志實現系統”:
          1)        首先在classpath下尋找自己的配置文件commons-logging.properties,如果找到,則使用其中定義的Log實現類;
          2)        如果找不到commons-logging.properties文件,則在查找是否已定義系統環境變量org.apache.commons.logging.Log,找到則使用其定義的Log實現類;
          3)        否則,查看classpath中是否有Log4j的包,如果發現,則自動使用Log4j作為日志實現類;
          4)        否則,使用JDK自身的日志實現類(JDK1.4以后才有日志實現類);
          5)        否則,使用commons-logging自己提供的一個簡單的日志實現類SimpleLog
          (以上順序不保證完全準確,請參考官方文檔)

          可見,commons-logging總是能找到一個日志實現類,并且盡可能找到一個“最合適”的日志實現類。我說它“很貼心”實際上是因為:1、可以不需要配置文件;2、自動判斷有沒有Log4j包,有則自動使用之;3、最悲觀的情況下也總能保證提供一個日志實現(SimpleLog)。 
                 可以看到,commons-logging對編程者和Log4j都非常友好。

                 為了簡化配置commons-logging,一般不使用commons-logging的配置文件,也不設置與commons-logging相關的系統環境變量,而只需將Log4jJar包放置到classpash中就可以了。這樣就很簡單地完成了commons-loggingLog4j的融合。如果不想用Log4j了怎么辦?只需將classpath中的Log4jJar包刪除即可。

          就這么簡單!

          代碼應該怎么寫?

          我們在需要輸出日志信息的“每一人”類中做如下的三個工作:
          1、導入所有需的commongs-logging類:
          import org.apache.commons.logging.Log;
          import org.apache.commons.logging.LogFactory;

          如果愿意簡化的話,還可以兩行合為一行:
          import org.apache.commons.logging.*;  

          2、在自己的類中定義一個org.apache.commons.logging.Log類的私有靜態類成員:
          private static Log log = LogFactory.getLog(YouClassName.class);

          注意這里定義的是static成員,以避免產生多個實例。

          LogFactory.getLog()方法的參數使用的是當前類的class,這是目前被普通認為的最好的方式。為什么不寫作LogFactory.getLog(this.getClass())?因為static類成員訪問不到this指針!

          3、使用org.apache.commons.logging.Log類的成員方法輸出日志信息:
          log.debug("111");
          log.info("222");
          log.warn("333");
          log.error("444");
          log.fatal("555");

          這里的log,就是上面第二步中定義的類成員變量,其類型是org.apache.commons.logging.Log,通過該類的成員方法,我們就可以將不同性質的日志信息輸出到目的地(目的地是哪里?視配置可定,可能是stdout,也可能是文件,還可能是發送到郵件,甚至發送短信到手機……詳見下文對log4j.properties的介紹):
          l         debug()   輸出“調試”級別的日志信息;
          l         info()      輸出“信息”級別的日志信息;
          l         warn()    輸出“警告”級別的日志信息;
          l         error()     輸出“錯誤”級別的日志信息;
          l         fatal()      輸出“致命錯誤”級別的日志信息;

          根據不同的性質,日志信息通常被分成不同的級別,從低到高依次是:“調試(DEBUG)”“信息(INFO)”“警告(WARN)”“錯誤(ERROR)”“致命錯誤(FATAL)”。為什么要把日志信息分成不同的級別呢?這實際上是方便我們更好的控制它。比如,通過Log4j的配置文件,我們可以設置“輸出‘調試’及以上級別的日志信息”(即“調試”“信息”“警告”“錯誤”“致命錯誤”),這對項目開發人員可能是有用的;我們還可以設置“輸出“警告”及以上級別的日志信息”(即“警告”“錯誤”“致命錯誤”),這對項目最終用戶可能是有用的。 
                 僅從字面上理解,也可以大致得出結論:最常用的應該是debug()info();而warn()、error()fatal()僅在相應事件發生后才使用。  

          從上面三個步驟可以看出,使用commons-logging的日志接口非常的簡單,不需要記憶太多東西:僅僅用到了兩個類Log, LogFactory,并且兩個類的方法都非常少(后者只用到一個方法,前者經常用到的也只是上面第三步中列出的幾個),同時參數又非常簡單。

          上面所介紹的方法是目前被普通應用的,可以說是被標準化了的方法,幾乎所有的人都是這么用。如果不信,或想確認一下,就去下載幾個知名的Java開源項目源代碼看一下吧。  

          下面給出一個完整的Java類的代碼: 
          package liigo.testlog; 
          import org.apache.commons.logging.Log;
          import org.apache.commons.logging.LogFactory; 

          public class TestLog
              private static Log log = LogFactory.getLog(TestLog.class); 
              public void test()    
                  log.debug("111"); 
                  log.info("222"); 
                  log.warn("333"); 
                  log.error("444"); 
                  log.fatal("555"); 
               

              public static void main(String[] args)    
                  TestLog testLog = new TestLog(); 
                  testLog.test(); 
              }
           

          只要保證commons-loggingjar包在classpath中,上述代碼肯定可以很順利的編譯通過。那它的執行結果是怎么樣的呢?恐怕會有很大的不同,請繼續往下看。  

          Log4j在哪里呢?它發揮作用了嗎?

          應該注意到,我們上面給出的源代碼,完全沒有涉及到Log4j——這正是我們所希望的,這也正是commons-logging所要達到的目標之一。

          可是,怎么才能讓Log4j發揮它的作用呢?答案很簡單,只需滿足“classpath中有Log4jjar包”。前面已經說過了,commons-logging會自動發現并應用Log4j。所以只要它存在,它就發揮作用。(它不存在呢?自然就不發揮作用,commons-logging會另行選擇其它的日志實現類。)

          注意:配置文件log4j.propertiesLog4j來說是必須的。如果classpath中沒有該配置文件,或者配置不對,將會引發運行時異常。

                 這樣,要正確地應用Log4j輸出日志信息,log4j.properties的作用就很重要了。好在該文件有通用的模板,復制一份(稍加修改)就可以使用。幾乎每一個Java項目目錄內都會有一個log4j.properties文件,可下載幾個Java開源項目源代碼查看。本文最后也附一個模板性質的log4j.properties文件,直接復制過去就可以用,或者根據自己的需要稍加修改。后文將會log4j.properties文件適當作一些介紹。

          關于Log4j比較全面的配置

          LOG4J的配置之簡單使它遍及于越來越多的應用中了:Log4J配置文件實現了輸出到控制臺、文件、

          回滾文件、發送日志郵件、輸出到數據庫日志表、自定義標簽等全套功能。擇其一二使用就夠用了

          log4j.rootLogger=DEBUG,CONSOLE,A1,im
          log4j.addivity.org.apache=true 
           

          # 應用于控制臺
          log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
          log4j.appender.Threshold=DEBUG
          log4j.appender.CONSOLE.Target=System.out
          log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
          log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
          #log4j.appender.CONSOLE.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD] n%c[CATEGORY]%n%m[MESSAGE]%n%n

          #應用于文件
          log4j.appender.FILE=org.apache.log4j.FileAppender
          log4j.appender.FILE.File=file.log
          log4j.appender.FILE.Append=false
          log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
          log4j.appender.FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
          # Use this layout for LogFactor 5 analysis


          # 應用于文件回滾
          log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
          log4j.appender.ROLLING_FILE.Threshold=ERROR
          log4j.appender.ROLLING_FILE.File=rolling.log
          log4j.appender.ROLLING_FILE.Append=true
          log4j.appender.ROLLING_FILE.MaxFileSize=10KB
          log4j.appender.ROLLING_FILE.MaxBackupIndex=1
          log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
          log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n

          #應用于socket
          log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender
          log4j.appender.SOCKET.RemoteHost=localhost
          log4j.appender.SOCKET.Port=5001
          log4j.appender.SOCKET.LocationInfo=true
          # Set up for Log Facter 5
          log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout
          log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n


          # Log Factor 5 Appender
          log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
          log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000

          # 發送日志給郵件
          log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
          log4j.appender.MAIL.Threshold=FATAL
          log4j.appender.MAIL.BufferSize=10
          log4j.appender.MAIL.From=web@www.wuset.com
          log4j.appender.MAIL.SMTPHost=www.wusetu.com
          log4j.appender.MAIL.Subject=Log4J Message
          log4j.appender.MAIL.To=web@www.wusetu.com
          log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
          log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n


          # 用于數據庫
          log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender
          log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test
          log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver
          log4j.appender.DATABASE.user=root
          log4j.appender.DATABASE.password=
          log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n')
          log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout
          log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n


          log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
          log4j.appender.A1.File=SampleMessages.log4j
          log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j'
          log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout

          #自定義Appender
          log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender

          log4j.appender.im.host = mail.cybercorlin.net
          log4j.appender.im.username = username
          log4j.appender.im.password = password
          log4j.appender.im.recipient = corlin@cybercorlin.net

          log4j.appender.im.layout=org.apache.log4j.PatternLayout
          log4j.appender.im.layout.ConversionPattern =[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n



          Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1545873

          posted @ 2007-12-12 14:48 PariScamper 閱讀(2053) | 評論 (2)編輯 收藏

          就servlet規范本身,數據可以放在3個地方:request、session、servletContext.

          request:
          好處:用完就仍,不會導致資源占用的無限增長。
          弊處:每次要用都從數據庫中抓,多做操作,自然會對性能有一些影響。

          session:
          好處:不用每次都去數據庫抓,少做操作。
          弊處:每個客戶都有一個session,只能自己使用,不同session可能保存大量重復數據;
          可能耗費大量服務器內存;
          另外session構建在cookie和url重寫的基礎上,所以用session實現會話跟蹤,會用掉一點點服務器帶寬和客戶端保持聯絡,
          當然session越多,耗費的帶寬越多,理論上也會對性能造成影響。
          集群的session同步會是個問題。

          servletContext:
          好處:不用每次都去數據庫抓,少做操作。
          存儲的數據所有客戶都可以用。
          可減少重復在內存中存儲數據造成的開銷。
          弊處:很多時候相同的數據可能不多(相當于cache的命中率很低)。


          其實以上3中方法都有利有弊,各自的好處在某種條件下,也都會轉變為弊處。所以不妨綜合使用,相當于一個“第三方用法”(只講一下思路,否則太過繁瑣,涉及到的相關技術點請參考有關技術資料):

          request不說了,重點說說session和servletContext:

          --session的可控應用
          session的最大問題是資源回收,兩類回收方法:
          主動回收:瀏覽器被關閉,而為提交觸發清理動作的請求時,該方法失效,而且很常見。
          超時回收:設置session的setMaxInactiveInterval屬性或在web.xml中配置超時時間,然后交給jvm的垃圾處理器處理。
          不過不要報太大希望,jvm的垃圾收集器并不靈光。
          可以用另一種替代方法緩解該問題,比如限制session的數量,可以用HttpSessionListener實現,這樣可以緩解session帶來的吃內存問題,當然這種做法每次都需要判斷session數量,當session達到限定數量時還必須用其他方法處理了,這些細節繁瑣,而且要謹慎處理。

          --servletContext
          如果說session是一個“局部緩存”,那servletContext就是一個“全局緩存”了,不妨把它當作cache(這里不講究用詞的嚴謹性,僅為了更好說明問題)。cache的大小是當前應用可使用的最大內存。cache的最大問題是提高命中率,命中率高,內存占用少,效率高,命中率低,則內存占用多而且效率低。這種應用的技術實現比“session的可空應用”要簡單,適用于相同數據多的地方,這個要事先有所判斷,如果用不好則有弊無利。

          如果僅使用servlet規范給出的3種機制,任何一種都達不到好處兼收的效果,所以要發揮3種方法的好處、摒棄弊處,必須綜合運用,做一些技術框架的構建工作,而且有些地方還比較繁瑣(還好框架是可重用的)。
           

          有時候尋求或實現“平衡”(或者說盡取其利而摒其害),要付出很大代價,根據不同的情況,這些代價或是值得,或是不值得。也可以“兩害相權取其輕”,或許是最便捷的方法。

          posted @ 2007-11-19 13:18 PariScamper 閱讀(784) | 評論 (0)編輯 收藏

          注意如果該字段在Oracle中設定非空性為YES,則即使是插入String a=""或者String a=new String()也是相對于空值。

          posted @ 2007-10-23 15:29 PariScamper 閱讀(7677) | 評論 (3)編輯 收藏

          六種異常處理的陋習

          你覺得自己是一個Java專家嗎?是否肯定自己已經全面掌握了Java的異常處理機制?在下面這段代碼中,你能夠迅速找出異常處理的六個問題嗎?

          1 OutputStreamWriter out = ...
          2 java.sql.Connection conn = ...
          3 try { // ⑸
          4  Statement stat = conn.createStatement();
          5  ResultSet rs = stat.executeQuery(
          6   "select uid, name from user");
          7  while (rs.next())
          8  {
          9   out.println("ID:" + rs.getString("uid") // ⑹
          10    ",姓名:" + rs.getString("name"));
          11  }
          12  conn.close(); // ⑶
          13  out.close();
          14 }
          15 catch(Exception ex) // ⑵
          16 {
          17  ex.printStackTrace(); //⑴,⑷
          18 }


            作為一個Java程序員,你至少應該能夠找出兩個問題。但是,如果你不能找出全部六個問題,請繼續閱讀本文。

            本文討論的不是Java異常處理的一般性原則,因為這些原則已經被大多數人熟知。我們要做的是分析各種可稱為“反例”(anti-pattern)的違背優秀編碼規范的常見壞習慣,幫助讀者熟悉這些典型的反面例子,從而能夠在實際工作中敏銳地察覺和避免這些問題。

            反例之一:丟棄異常

            代碼:15行-18行。

            這段代碼捕獲了異常卻不作任何處理,可以算得上Java編程中的殺手。從問題出現的頻繁程度和禍害程度來看,它也許可以和C/C++程序的一個惡名遠播的問題相提并論??不檢查緩沖區是否已滿。如果你看到了這種丟棄(而不是拋出)異常的情況,可以百分之九十九地肯定代碼存在問題(在極少數情況下,這段代碼有存在的理由,但最好加上完整的注釋,以免引起別人誤解)。

            這段代碼的錯誤在于,異常(幾乎)總是意味著某些事情不對勁了,或者說至少發生了某些不尋常的事情,我們不應該對程序發出的求救信號保持沉默和無動于衷。調用一下printStackTrace算不上“處理異常”。不錯,調用printStackTrace對調試程序有幫助,但程序調試階段結束之后,printStackTrace就不應再在異常處理模塊中擔負主要責任了。

            丟棄異常的情形非常普遍。打開JDK的ThreadDeath類的文檔,可以看到下面這段說明:“特別地,雖然出現ThreadDeath是一種‘正常的情形’,但ThreadDeath類是Error而不是Exception的子類,因為許多應用會捕獲所有的Exception然后丟棄它不再理睬。”這段話的意思是,雖然ThreadDeath代表的是一種普通的問題,但鑒于許多應用會試圖捕獲所有異常然后不予以適當的處理,所以JDK把ThreadDeath定義成了Error的子類,因為Error類代表的是一般的應用不應該去捕獲的嚴重問題??梢?,丟棄異常這一壞習慣是如此常見,它甚至已經影響到了Java本身的設計。

            那么,應該怎樣改正呢?主要有四個選擇:

            1、處理異常。針對該異常采取一些行動,例如修正問題、提醒某個人或進行其他一些處理,要根據具體的情形確定應該采取的動作。再次說明,調用printStackTrace算不上已經“處理好了異常”。

            2、重新拋出異常。處理異常的代碼在分析異常之后,認為自己不能處理它,重新拋出異常也不失為一種選擇。

            3、把該異常轉換成另一種異常。大多數情況下,這是指把一個低級的異常轉換成應用級的異常(其含義更容易被用戶了解的異常)。

            4、不要捕獲異常。

            結論一:既然捕獲了異常,就要對它進行適當的處理。不要捕獲異常之后又把它丟棄,不予理睬。

            反例之二:不指定具體的異常

            代碼:15行。

            許多時候人們會被這樣一種“美妙的”想法吸引:用一個catch語句捕獲所有的異常。最常見的情形就是使用catch(Exception ex)語句。但實際上,在絕大多數情況下,這種做法不值得提倡。為什么呢?

            要理解其原因,我們必須回顧一下catch語句的用途。catch語句表示我們預期會出現某種異常,而且希望能夠處理該異常。異常類的作用就是告訴Java編譯器我們想要處理的是哪一種異常。由于絕大多數異常都直接或間接從java.lang.Exception派生,catch(Exception ex)就相當于說我們想要處理幾乎所有的異常。

            再來看看前面的代碼例子。我們真正想要捕獲的異常是什么呢?最明顯的一個是SQLException,這是JDBC操作中常見的異常。另一個可能的異常是IOException,因為它要操作OutputStreamWriter。顯然,在同一個catch塊中處理這兩種截然不同的異常是不合適的。如果用兩個catch塊分別捕獲SQLException和IOException就要好多了。這就是說,catch語句應當盡量指定具體的異常類型,而不應該指定涵蓋范圍太廣的Exception類。

            另一方面,除了這兩個特定的異常,還有其他許多異常也可能出現。例如,如果由于某種原因,executeQuery返回了null,該怎么辦?答案是讓它們繼續拋出,即不必捕獲也不必處理。實際上,我們不能也不應該去捕獲可能出現的所有異常,程序的其他地方還有捕獲異常的機會??直至最后由JVM處理。

            結論二:在catch語句中盡可能指定具體的異常類型,必要時使用多個catch。不要試圖處理所有可能出現的異常。

            反例之三:占用資源不釋放

            代碼:3行-14行。

            異常改變了程序正常的執行流程。這個道理雖然簡單,卻常常被人們忽視。如果程序用到了文件、Socket、JDBC連接之類的資源,即使遇到了異常,也要正確釋放占用的資源。為此,Java提供了一個簡化這類操作的關鍵詞finally。

            finally是樣好東西:不管是否出現了異常,Finally保證在try/catch/finally塊結束之前,執行清理任務的代碼總是有機會執行。遺憾的是有些人卻不習慣使用finally。

            當然,編寫finally塊應當多加小心,特別是要注意在finally塊之內拋出的異常??這是執行清理任務的最后機會,盡量不要再有難以處理的錯誤。

            結論三:保證所有資源都被正確釋放。充分運用finally關鍵詞。

          反例之四:不說明異常的詳細信息

            代碼:3行-18行。

            仔細觀察這段代碼:如果循環內部出現了異常,會發生什么事情?我們可以得到足夠的信息判斷循環內部出錯的原因嗎?不能。我們只能知道當前正在處理的類發生了某種錯誤,但卻不能獲得任何信息判斷導致當前錯誤的原因。

            printStackTrace的堆棧跟蹤功能顯示出程序運行到當前類的執行流程,但只提供了一些最基本的信息,未能說明實際導致錯誤的原因,同時也不易解讀。

            因此,在出現異常時,最好能夠提供一些文字信息,例如當前正在執行的類、方法和其他狀態信息,包括以一種更適合閱讀的方式整理和組織printStackTrace提供的信息。

            結論四:在異常處理模塊中提供適量的錯誤原因信息,組織錯誤信息使其易于理解和閱讀。

            反例之五:過于龐大的try塊

            代碼:3行-14行。

            經??梢钥吹接腥税汛罅康拇a放入單個try塊,實際上這不是好習慣。這種現象之所以常見,原因就在于有些人圖省事,不愿花時間分析一大塊代碼中哪幾行代碼會拋出異常、異常的具體類型是什么。把大量的語句裝入單個巨大的try塊就象是出門旅游時把所有日常用品塞入一個大箱子,雖然東西是帶上了,但要找出來可不容易。

            一些新手常常把大量的代碼放入單個try塊,然后再在catch語句中聲明Exception,而不是分離各個可能出現異常的段落并分別捕獲其異常。這種做法為分析程序拋出異常的原因帶來了困難,因為一大段代碼中有太多的地方可能拋出Exception。

            結論五:盡量減小try塊的體積。

            反例之六:輸出數據不完整

            代碼:7行-11行。

            不完整的數據是Java程序的隱形殺手。仔細觀察這段代碼,考慮一下如果循環的中間拋出了異常,會發生什么事情。循環的執行當然是要被打斷的,其次,catch塊會執行??就這些,再也沒有其他動作了。已經輸出的數據怎么辦?使用這些數據的人或設備將收到一份不完整的(因而也是錯誤的)數據,卻得不到任何有關這份數據是否完整的提示。對于有些系統來說,數據不完整可能比系統停止運行帶來更大的損失。

            較為理想的處置辦法是向輸出設備寫一些信息,聲明數據的不完整性;另一種可能有效的辦法是,先緩沖要輸出的數據,準備好全部數據之后再一次性輸出。

            結論六:全面考慮可能出現的異常以及這些異常對執行流程的影響。

            改寫后的代碼

            根據上面的討論,下面給出改寫后的代碼。也許有人會說它稍微有點?嗦,但是它有了比較完備的異常處理機制。

          OutputStreamWriter out = ...
          java.sql.Connection conn = ...
          try {
           Statement stat = conn.createStatement();
           ResultSet rs = stat.executeQuery(
            "select uid, name from user");
           while (rs.next())
           {
            out.println("ID:" + rs.getString("uid") + ",姓名: " + rs.getString("name"));
           }
          }
          catch(SQLException sqlex)
          {
           out.println("警告:數據不完整");
           throw new ApplicationException("讀取數據時出現SQL錯誤", sqlex);
          }
          catch(IOException ioex)
          {
           throw new ApplicationException("寫入數據時出現IO錯誤", ioex);
          }
          finally
          {
           if (conn != null) {
            try {
             conn.close();
            }
            catch(SQLException sqlex2)
            {
             System.err(this.getClass().getName() + ".mymethod - 不能關閉數據庫連接: " + sqlex2.toString());
            }
           }

           if (out != null) {
            try {
             out.close();
            }
            catch(IOException ioex2)
            {
             System.err(this.getClass().getName() + ".mymethod - 不能關閉輸出文件" + ioex2.toString());
            }
           }
          }

            本文的結論不是放之四海皆準的教條,有時常識和經驗才是最好的老師。如果你對自己的做法沒有百分之百的信心,務必加上詳細、全面的注釋。

            另一方面,不要笑話這些錯誤,不妨問問你自己是否真地徹底擺脫了這些壞習慣。即使最有經驗的程序員偶爾也會誤入歧途,原因很簡單,因為它們確確實實帶來了“方便”。所有這些反例都可以看作Java編程世界的惡魔,它們美麗動人,無孔不入,時刻誘惑著你。也許有人會認為這些都屬于雞皮蒜毛的小事,不足掛齒,但請記住:勿以惡小而為之,勿以善小而不為。





          ------------------------------------------------------------------下面是一些java異常集-------------------------------------------------------------------------------------------


          算術異常類:ArithmeticExecption

          空指針異常類:NullPointerException

          類型強制轉換異常:ClassCastException

          數組負下標異常:NegativeArrayException

          數組下標越界異常:ArrayIndexOutOfBoundsException

          違背安全原則異常:SecturityException

          文件已結束異常:EOFException

          文件未找到異常:FileNotFoundException

          字符串轉換為數字異常:NumberFormatException


          操作數據庫異常:SQLException


          輸入輸出異常:IOException


          方法未找到異常:NoSuchMethodException

          java.lang.AbstractMethodError

          抽象方法錯誤。當應用試圖調用抽象方法時拋出。

          java.lang.AssertionError

          斷言錯。用來指示一個斷言失敗的情況。

          java.lang.ClassCircularityError

          類循環依賴錯誤。在初始化一個類時,若檢測到類之間循環依賴則拋出該異常。

          java.lang.ClassFormatError

          類格式錯誤。當Java虛擬機試圖從一個文件中讀取Java類,而檢測到該文件的內容不符合類的有效格式時拋出。

          java.lang.Error

          錯誤。是所有錯誤的基類,用于標識嚴重的程序運行問題。這些問題通常描述一些不應被應用程序捕獲的反常情況。

          java.lang.ExceptionInInitializerError

          初始化程序錯誤。當執行一個類的靜態初始化程序的過程中,發生了異常時拋出。靜態初始化程序是指直接包含于類中的static語句段。

          java.lang.IllegalAccessError

          違法訪問錯誤。當一個應用試圖訪問、修改某個類的域(Field)或者調用其方法,但是又違反域或方法的可見性聲明,則拋出該異常。

          java.lang.IncompatibleClassChangeError

          不兼容的類變化錯誤。當正在執行的方法所依賴的類定義發生了不兼容的改變時,拋出該異常。一般在修改了應用中的某些類的聲明定義而沒有對整個應用重新編譯而直接運行的情況下,容易引發該錯誤。

          java.lang.InstantiationError

          實例化錯誤。當一個應用試圖通過Java的new操作符構造一個抽象類或者接口時拋出該異常.

          java.lang.InternalError

          內部錯誤。用于指示Java虛擬機發生了內部錯誤。

          java.lang.LinkageError

          鏈接錯誤。該錯誤及其所有子類指示某個類依賴于另外一些類,在該類編譯之后,被依賴的類改變了其類定義而沒有重新編譯所有的類,進而引發錯誤的情況。

          java.lang.NoClassDefFoundError

          未找到類定義錯誤。當Java虛擬機或者類裝載器試圖實例化某個類,而找不到該類的定義時拋出該錯誤。

          java.lang.NoSuchFieldError

          域不存在錯誤。當應用試圖訪問或者修改某類的某個域,而該類的定義中沒有該域的定義時拋出該錯誤。

          java.lang.NoSuchMethodError

          方法不存在錯誤。當應用試圖調用某類的某個方法,而該類的定義中沒有該方法的定義時拋出該錯誤。

          java.lang.OutOfMemoryError

          內存不足錯誤。當可用內存不足以讓Java虛擬機分配給一個對象時拋出該錯誤。

          java.lang.StackOverflowError

          堆棧溢出錯誤。當一個應用遞歸調用的層次太深而導致堆棧溢出時拋出該錯誤。

          java.lang.ThreadDeath

          線程結束。當調用Thread類的stop方法時拋出該錯誤,用于指示線程結束。

          java.lang.UnknownError

          未知錯誤。用于指示Java虛擬機發生了未知嚴重錯誤的情況。

          java.lang.UnsatisfiedLinkError

          未滿足的鏈接錯誤。當Java虛擬機未找到某個類的聲明為native方法的本機語言定義時拋出。

          java.lang.UnsupportedClassVersionError

          不支持的類版本錯誤。當Java虛擬機試圖從讀取某個類文件,但是發現該文件的主、次版本號不被當前Java虛擬機支持的時候,拋出該錯誤。

          java.lang.VerifyError

          驗證錯誤。當驗證器檢測到某個類文件中存在內部不兼容或者安全問題時拋出該錯誤。

          java.lang.VirtualMachineError

          虛擬機錯誤。用于指示虛擬機被破壞或者繼續執行操作所需的資源不足的情況。


          java.lang.ArithmeticException

          算術條件異常。譬如:整數除零等。

          java.lang.ArrayIndexOutOfBoundsException

          數組索引越界異常。當對數組的索引值為負數或大于等于數組大小時拋出。

          java.lang.ArrayStoreException

          數組存儲異常。當向數組中存放非數組聲明類型對象時拋出。

          java.lang.ClassCastException

          類造型異常。假設有類A和B(A不是B的父類或子類),O是A的實例,那么當強制將O構造為類B的實例時拋出該異常。該異常經常被稱為強制類型轉換異常。

          java.lang.ClassNotFoundException

          找不到類異常。當應用試圖根據字符串形式的類名構造類,而在遍歷CLASSPAH之后找不到對應名稱的class文件時,拋出該異常。

          java.lang.CloneNotSupportedException

          不支持克隆異常。當沒有實現Cloneable接口或者不支持克隆方法時,調用其clone()方法則拋出該異常。

          java.lang.EnumConstantNotPresentException

          枚舉常量不存在異常。當應用試圖通過名稱和枚舉類型訪問一個枚舉對象,但該枚舉對象并不包含常量時,拋出該異常。

          java.lang.Exception

          根異常。用以描述應用程序希望捕獲的情況。

          java.lang.IllegalAccessException

          違法的訪問異常。當應用試圖通過反射方式創建某個類的實例、訪問該類屬性、調用該類方法,而當時又無法訪問類的、屬性的、方法的或構造方法的定義時拋出該異常。

          java.lang.IllegalMonitorStateException

          違法的監控狀態異常。當某個線程試圖等待一個自己并不擁有的對象(O)的監控器或者通知其他線程等待該對象(O)的監控器時,拋出該異常。

          java.lang.IllegalStateException

          違法的狀態異常。當在Java環境和應用尚未處于某個方法的合法調用狀態,而調用了該方法時,拋出該異常。

          java.lang.IllegalThreadStateException

          違法的線程狀態異常。當縣城尚未處于某個方法的合法調用狀態,而調用了該方法時,拋出異常。

          java.lang.IndexOutOfBoundsException

          索引越界異常。當訪問某個序列的索引值小于0或大于等于序列大小時,拋出該異常。

          java.lang.InstantiationException

          實例化異常。當試圖通過newInstance()方法創建某個類的實例,而該類是一個抽象類或接口時,拋出該異常。

          java.lang.InterruptedException

          被中止異常。當某個線程處于長時間的等待、休眠或其他暫停狀態,而此時其他的線程通過Thread的interrupt方法終止該線程時拋出該異常。

          java.lang.NegativeArraySizeException

          數組大小為負值異常。當使用負數大小值創建數組時拋出該異常。

          java.lang.NoSuchFieldException

          屬性不存在異常。當訪問某個類的不存在的屬性時拋出該異常。

          java.lang.NoSuchMethodException

          方法不存在異常。當訪問某個類的不存在的方法時拋出該異常。

          java.lang.NullPointerException

          空指針異常。當應用試圖在要求使用對象的地方使用了null時,拋出該異常。譬如:調用null對象的實例方法、訪問null對象的屬性、計算null對象的長度、使用throw語句拋出null等等。

          java.lang.NumberFormatException

          數字格式異常。當試圖將一個String轉換為指定的數字類型,而該字符串確不滿足數字類型要求的格式時,拋出該異常。

          java.lang.RuntimeException

          運行時異常。是所有Java虛擬機正常操作期間可以被拋出的異常的父類。

          java.lang.SecurityException

          安全異常。由安全管理器拋出,用于指示違反安全情況的異常。

          java.lang.StringIndexOutOfBoundsException

          字符串索引越界異常。當使用索引值訪問某個字符串中的字符,而該索引值小于0或大于等于序列大小時,拋出該異常。

          java.lang.TypeNotPresentException

          類型不存在異常。當應用試圖以某個類型名稱的字符串表達方式訪問該類型,但是根據給定的名稱又找不到該類型是拋出該異常。該異常與ClassNotFoundException的區別在于該異常是unchecked(不被檢查)異常,而ClassNotFoundException是checked(被檢查)異常。

          java.lang.UnsupportedOperationException

          不支持的方法異常。指明請求的方法不被支持情況的異常。

          異常
          javax.servlet.jsp.JspException: Cannot retrieve mapping for action /Login (/Login是你的action名字)  

          可能原因
          action沒有再struts-config.xml 中定義,或沒有找到匹配的action,例如在JSP文件中使用 <html:form action="Login.do".將表單提交給Login.do處理,如果出現上述異常,請查看struts-config.xml中的定義部分,有時可能是打錯了字符或者是某些不符合規則,可以使用strutsconsole工具來檢查。
          -----------------------------------------------------------------------------------------------------------------
          異常
          org.apache.jasper.JasperException: Cannot retrieve definition for form bean null

          可能原因     
                
          這個異常是因為Struts根據struts-config.xml中的mapping沒有找到action期望的form bean。大部分的情況可能是因為在form-bean中設置的name屬性和action中設置的name屬性不匹配所致。換句話說,action和form都應該各自有一個name屬性,并且要精確匹配,包括大小寫。這個錯誤當沒有name屬性和action關聯時也會發生,如果沒有在action中指定name屬性,那么就沒有name屬性和action相關聯。當然當action制作某些控制時,譬如根據參數值跳轉到相應的jsp頁面,而不是處理表單數據,這是就不用name屬性,這也是action的使用方法之一。
          -----------------------------------------------------------------------------------------------------------------
          異常
          No action instance for path /xxxx could be created

          可能原因
          特別提示:因為有很多中情況會導致這個錯誤的發生,所以推薦大家調高你的web服務器的日志/調試級別,這樣可以從更多的信息中看到潛在的、在試圖創建action類時發生的錯誤,這個action類你已經在struts-config.xml中設置了關聯(即添加了<action>標簽)。

          在struts-config.xml中通過action標簽的class屬性指定的action類不能被找到有很多種原因,例如:定位編譯后的.class文件失敗。Failure to place compiled .class file for the action in the classpath (在web開發中,class的的位置在r WEB-INF/classes,所以你的action class必須要在這個目錄下。例如你的action類位于WEB-INF/classes/action/Login.class,那么在struts-config.xml中設置action的屬性type時就是action.Login).
          拼寫錯誤,這個也時有發生,并且不易找到,特別注意第一個字母的大小寫和包的名稱。
          -----------------------------------------------------------------------------------------------------------------
          異常
          javax.servlet.jsp.JspException: No getter method for property username of bean org.apache.struts.taglib.html.BEAN

          可能原因
          沒有位form bean中的某個變量定義getter 方法

          這個錯誤主要發生在表單提交的FormBean中,用struts標記<html:text property=”username”>時,在FormBean中必須有一個getUsername()方法。注意字母“U”。
          -----------------------------------------------------------------------------------------------------------------
          異常
          java.lang.NoClassDefFoundError: org/apache/struts/action/ActionForm

          可能原因
          這個錯誤主要發生在在classpath中找不到相應的Java .class文件。如果這個錯誤發生在web應用程序的運行時,主要是因為指定的class文件不在web server的classpath中(/WEB-INF/classes 和 /WEB-INF/lib)。在上面的錯誤中,原因是找不到ActionForm類。
          -----------------------------------------------------------------------------------------------------------------
          異常
          javax.servlet.jsp.JspException: Exception creating bean of class org.apache.struts.action.ActionForm: {1}

          可能原因
          Instantiating Struts-provided ActionForm class directly instead of instantiating a class derived off ActionForm. This mightoccur implicitly if you specify that a form-bean is this Struts ActionForm class rather than specifying a child of this classfor the form-bean.

          Not associating an ActionForm-descended class with an action can also lead to this error.
          -----------------------------------------------------------------------------------------------------------------
          異常
          javax.servlet.jsp.JspException: Cannot find ActionMappings or ActionFormBeans collection

          可能原因
          不是標識Struts actionServlet的<servlet>標記就是映射.do擴展名的<sevlet-mapping>標記或者兩者都沒有在web.xml中聲明。

          在struts-config.xml中的打字或者拼寫錯誤也可導致這個異常的發生。例如缺少一個標記的關閉符號/>。最好使用struts console工具檢查一下。

          另外,load-on-startup必須在web.xml中聲明,這要么是一個空標記,要么指定一個數值,這個數值用來表servlet運行的優先級,數值越大優先級越低。

          還有一個和使用load-on-startup有關的是使用Struts預編譯JSP文件時也可能導致這個異常。
          -----------------------------------------------------------------------------------------------------------------
          異常
          java.lang.NullPointerException at org.apache.struts.util.RequestUtils.forwardURL(RequestUtils.java:1223)

          可能原因
          在struts-config.xml中的forward元素缺少path屬性。例如應該是如下形式:
          <forward name="userhome" path="/user/userhome.jsp"/>
          -----------------------------------------------------------------------------------------------------------------
          異常
          javax.servlet.jsp.JspException: Cannot find bean org.apache.struts.taglib.html.BEAN in any scope


           

          Probable Causes
          試圖在Struts的form標記外使用form的子元素。這常常發生在你在</html:form>后面使用Struts的html標記。另外要注意可能你不經意使用的無主體的標記,如<html:form … />,這樣web 服務器解析時就當作一個無主體的標記,隨后使用的所有<html>標記都被認為是在這個標記之外的,如又使用了<html:text property=”id”>還有就是在使用taglib引入HTML標記庫時,你使用的prefix的值不是html。
          -----------------------------------------------------------------------------------------------------------------
          異常
          javax.servlet.jsp.JspException: Missing message for key xx.xx.xx

          Probable Causes
          這個key的值對沒有在資源文件ApplicationResources.properties中定義。如果你使用eclipse時經常碰到這樣的情況,當項目重新編譯時,eclipse會自動將classes目錄下的資源文件刪除。

          資源文件ApplicationResources.properties 不在classpath中應將資源文件放到 WEB-INF/classes 目錄下,當然要在struts-config.xml中定義)
          -----------------------------------------------------------------------------------------------------------------
          異常
          Cannot find message resources under key org.apache.struts.action.MESSAGE

          可能原因
          很顯然,這個錯誤是發生在使用資源文件時,而Struts沒有找到資源文件。

          Implicitly trying to use message resources that are not available (such as using empty html:options tag instead of specifyingthe options in its body -- this assumes options are specified in ApplicationResources.properties file)

          XML parser issues -- too many, too few, incorrect/incompatible versions
          -----------------------------------------------------------------------------------------------------------------
          異常
          Strange and seemingly random characters in HTML and on screen, but not in original JSP or servlet.

          可能原因
          混和使用Struts的html:form標記和標準的HTML標記不正確。

          使用的編碼樣式在本頁中不支持。
          -----------------------------------------------------------------------------------------------------------------
          異常
          "Document contained no data" in Netscape

          No data rendered (completely empty) page in Microsoft Internet Explorer

          可能原因
          使用一個Action的派生類而沒有實現perform()方法或execute()方法。在Struts1.0中實現的是perform()方法,在Struts1.1中實現的是execute()方法,但Struts1.1向后兼容perform()方法。但你使用Struts1.1創建一個Action的派生類,并且實現了execute()方法,而你在Struts1.0中運行的話,就會得到"Document contained nodata" error message in Netscape or a completely empty (no HTML whatsoever) page rendered in Microsoft Internet Explorer.”的錯誤信息。

          ---------------------------------------------------------------------------------------------------------------------------
          異常
          ServletException: BeanUtils.populate
          解決方案
          在用Struts上傳文件時,遇到了javax.servlet.ServletException: BeanUtils.populate異常。
          我的ActionServlet并沒有用到BeanUtils這些工具類。后來仔細檢查代碼發現是在jsp文件里的form忘了加enctype=&quot;multipart/form-data&quot; 了。所以寫程序遇到錯誤或異常應該從多方面考慮問題存在的可能性,想到系統提示信息以外的東西。
          ----------------------------------------------------------------------------------------------------------------------------
          1. 定義Action后, 如果指定了name, 那么必須要定義一個與它同名的FormBean才能進行form映射.2. 如果定義Action后, 提交頁面時出現 "No input attribute for mapping path..." 錯誤, 則需要在其input屬性中定義轉向的頁面.3. 如果插入新的數據時出現 "Batch update row count wrong:..." 錯誤, 則說明XXX.hbm.xml中指定的key的類型為原始類型(int, long),因為這種類型會自動分配值, 而這個值往往會讓系統認為已經存在該記錄, 正確的方法是使用java.lang.Integer或java.lang.Long對象.4. 如果插入數據時出現 "argument type mismatch" 錯誤, 可能是你使用了Date等特殊對象, 因為struts不能自動從String型轉換成Date型,所以, 你需要在Action中手動把String型轉換成Date型.5. Hibernate中, Query的iterator()比list()方法快很多.6. 如果出現 "equal symbol expected" 錯誤, 說明你的strtus標簽中包含另一個標簽或者變量, 例如:
          <html:select property="test" onchange="<%=test%>"/>
          或者
          <html:hidden property="test" value="<bean:write name="t" property="p"/>"/>
          這樣的情況...
          ---------------------------------------------------------------------------------------------------------------------------
          錯誤:Exception in thread "main" org.hibernate.exception.SQLGrammarException: Could not execute JDBC batch update原因與解決:      因為Hibernate Tools(或者Eclipse本身的Database Explorer)生成*.hbn.xml工具中包含有catalog="***"(*表示數據庫名稱)這樣的屬性,將該屬性刪除就可以了
          ---------------------------------------------------------------------------------------------------------------------------
          錯誤:org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations)
          原因與解決:
          方法1 刪除Set方的cascade
          方法2 解決關聯關系后,再刪除
          方法3 在many-to-one方增加cascade 但值不能是none
          最后一招:
          檢查一下hashCode equals是否使用了id作為唯一標示的選項了;我用uuid.hex時是沒有問題的;但是用了native,就不行了,怎么辦?刪除啊!
          ----------------------------------------------------------------------------------------------------------------------------
          問題:今天用Tomcat 5.5.12,發現原來很好用的系統不能用了,反復測試發現頁面中不能包含 taglib,否則會出現以下提示:HTTP Status 500 -type Exception reportMessage description The server encountered an internal error () that prevented it from fulfilling this request.exceptionorg.apache.jasper.JasperException: /index.jsp(1,1) Unable to read TLD "META-INF/tlds/struts-bean.tld" from JAR file"file:*****/WEB-INF/lib/struts.jar":原因:更新了工程用的lib文件夾下的jar,發布時也發布了servlet.jar和jsp-api.jar。解決:把jsp-api.jar刪除就解決這個問題了。-----------------------------------------------------------------------------------------------------------------------------
          錯誤: java.lang.NullPointerException
          原因: 發現 dao 實例、 manage 實例等需要注入的東西沒有被注入(俗稱空指針異常)解決:這個時候,你應該查看日志文件;默認是應用服務器的 log 文件,比如 Tomcat 就是 [Tomcat 安裝目錄 ]/logs ;你會發現提示你:可能是:org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sf' defined in ServletContextresource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception isorg.hibernate.HibernateException: could not configure from URL: file:src/hibernate.cfg.xmlorg.hibernate.HibernateException: could not configure from URL: file:src/hibernate.cfg.xml……………………….Caused by: java.io.FileNotFoundException: src\hibernate.cfg.xml可能是:org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined inServletContext resource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception isorg.hibernate.MappingException: Resource: com/mcc/coupon/model/UserRole.hbm.xml not foundorg.hibernate.MappingException: Resource: com/mcc/coupon/model/UserRole.hbm.xml not found然后你就知道原因是因為配置文件的解析出了錯誤,這個通過 Web 頁面是看不出來的。更多的是持久化影射文件出的錯誤;導致了沒有被解析;當然你需要的功能就無法使用了。
          ----------------------------------------------------------------------------------------------------------------------------
          錯誤:StandardWrapperValve[action]: Servlet.service() for servlet action threw exception
          javax.servlet.jsp.JspException: Cannot retrieve mapping for action /settlementTypeManage
          或者:      type Status report      message Servlet action is not available      description The requested resource (Servlet action is not available) is not available.
          原因: 同 上
          ----------------------------------------------------------------------------------------------------------------------------
          錯誤StandardWrapperValve[jsp]: Servlet.service() for servlet jsp threw exceptionjava.lang.ClassNotFoundException: org.apache.struts.taglib.bean.CookieTei界面錯誤具體描述:
          org.apache.jasper.JasperException: Failed to load or instantiate TagExtraInfo class: org.apache.struts.taglib.bean.CookieTei
                原因與解決:    <方案一>你的“html:”開頭的標簽沒有放在一個<html:form>中       <方案二>重新啟動你的應用服務器,自動就沒有這個問題

          posted @ 2007-10-15 13:09 PariScamper 閱讀(1798) | 評論 (0)編輯 收藏

          網上說是數據庫的列名與oracle的保留字重名,的確如此。
          比如我的有一列叫user就是,另外我驗證叫level也是。
          網上有的說叫start和end也不行,但經我試驗,是可以的,也就是start和end不是保留字。
          另外,這個錯誤只在使用hibernate才出現(使用jdbc也可能出現,沒試過)。
          直接用oracle的客戶端Enterprise Manager Console卻沒有這個問題,就是列名叫user和level,照樣可以插入數據。

          posted @ 2007-10-12 23:05 PariScamper 閱讀(6669) | 評論 (0)編輯 收藏

          SQL語句為
          insert into room(id,bid,name) values(room_seq.nextval,12,'1346S');
          room_seq.nextval為取得room_seq的下一值后,取完值后會自動增長
          select room_seq.nextval from dual則選出room_seq的nextval值,取值后也會自動增長
          select room_seq.currval from dual則選出room_seq的當前值,取值后不會自動增長
          另外,Oracle不像其他數據庫,NVARCHAR2的數據類型的取值居然是'abcd'(單引號),既不是雙引號,也不是無引號。

          posted @ 2007-10-12 19:33 PariScamper 閱讀(2354) | 評論 (1)編輯 收藏

          1、 Log4j是什么?
            Log4j可以幫助調試(有時候debug是發揮不了作 用的)和分析,要下載和了解更詳細的內容,還是訪問其官方網站吧:
          http://jakarta.apache.org/log4j

          2、Log4j的概念
             Log4j中有三個主要的組件,它們分別是
          Logger、Appender和Layout,Log4j 允許開發人員定義多個Logger,每個Logger擁有自己的名字,Logger之間通過名字來表明隸屬關系。有一個Logger稱為Root,它永遠存在,且不能通過名字檢索或引用,可以通過Logger.getRootLogger()方法獲得,其它Logger通過 Logger.getLogger(String name)方法。
             Appender則是用來指明將所有的log信息存放到什么地方,Log4j中支持多種appender,如
          console、files、GUI components、NT Event Loggers等,一個Logger可以擁有多個Appender,也就是你既可以將Log信息輸出到屏幕,同時存儲到一個文件中。
             Layout的作用是控制Log信息的輸出方式,也就是格式化輸出的信息。
             Log4j中將要輸出的Log信息定義了5種級別,依次為DEBUG、INFO、WARN、ERROR和FATAL,當輸出時,只有級別高過配置中規定的級別的信息才能真正的輸出,這樣就很方便的來配置不同情況下要輸出的內容,而不需要更改代碼,這點實在是方便啊。

          3、Log4j的配置文件
            雖然可以不用配置文件,而在程序中實現配置,但這種方法在如今的系統開發中顯然是不可取的,能采用配置文件的地方一定一定要用配置文件。Log4j支持兩種格式的配置文件:XML格式和Java的property格式,本人更喜歡后者,首先看一個簡單的例子吧,如下:

            log4j.rootLogger=debug, stdout, R
            log4j.appender.stdout=org.apache.log4j.ConsoleAppender
            log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

            # Pattern to output the caller's file name and line number.
            log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

            log4j.appender.R=org.apache.log4j.RollingFileAppender
            log4j.appender.R.File=example.log
            log4j.appender.R.MaxFileSize=
          100KB

            # Keep one backup file
            log4j.appender.R.MaxBackupIndex=1

            log4j.appender.R.layout=org.apache.log4j.PatternLayout
            log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n         

            首先,是設置root,格式為 log4j.rootLogger=[level],appenderName, ...,其中level就是設置需要輸出信息的級別,后面是appender的輸出的目的地,appenderName就是指定日志信息輸出到哪個地方。您可以同時指定多個輸出目的地。 配置日志信息輸出目的地Appender,其語法為
            log4j.appender.appenderName = fully.qualified.name.of.appender.class
            log4j.appender.appenderName.option1 = value1
            ...
            log4j.appender.appenderName.option = valueN

          Log4j提供的appender有以下幾種:
            org.apache.log4j.ConsoleAppender(控制臺)
            org.apache.log4j.FileAppender(文件)
            org.apache.log4j.DailyRollingFileAppender(每天產生一個日志文件)
            org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產生新文件)
            org.apache.log4j.WriterAppender(將日志信息以流格式發送到任意指定的地方)
          配置日志信息的格式(布局),其語法為:
            log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
            log4j.appender.appenderName.layout.option1 = value1
            ....
            log4j.appender.appenderName.layout.option = valueN

          Log4j提供的layout有以下幾種:
            org.apache.log4j.HTMLLayout(以HTML表格形式布局),
            org.apache.log4j.PatternLayout(可以靈活地指定布局模式),
            org.apache.log4j.SimpleLayout(包含日志信息的級別和信息字符串),
            org.apache.log4j.TTCCLayout(包含日志產生的時間、線程、類別等等信息)

          Log4J采用類似C語言中的printf函數的打印格式格式化日志信息,打印參數如下: %m 輸出代碼中指定的消息

             %p 輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL
            %r 輸出自應用啟動到輸出該log信息耗費的毫秒數
            %c 輸出所屬的類目,通常就是所在類的全名
            %t 輸出產生該日志事件的線程名
            %n 輸出一個回車換行符,Windows平臺為“\r\n”,Unix平臺為“\n”
            %d 輸出日志時間點的日期或時間,默認格式為ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},輸出類似:
          2002年10月18日 22:10:28,921
            %l 輸出日志事件的發生位置,包括類目名、發生的線程,以及在代碼中的行數。舉例:Testlog4.main(TestLog4.java:10)



          4、Log4j在程序中的使用

            要在自己的程序中使用Log4j,首先需要將commons-logging.jar和logging-log4j-1.2.9.jar導入到構建路徑中。然后再將log4j.properties放到src根目錄下。這樣就可以在程序中使用log4j了。在類中使用log4j,
          首先聲明一個靜態變量 Logger logger=Logger.getLog("classname");現在就可以使用了,用法如下:logger.debug("debug message")或者logger.info("info message"),看下面一個小例子:

            import com.foo.Bar;
            import org.apache.log4j.Logger;
            import org.apache.log4j.PropertyConfigurator;
            public class MyApp {
              static Logger logger = Logger.getLogger(MyApp.class.getName());
              public static void main(String[] args) {
                // BasicConfigurator replaced with PropertyConfigurator.
                PropertyConfigurator.configure(args[0]);
                logger.info("Entering application.");
                Bar bar = new Bar();
                bar.doIt();
                logger.info("Exiting application.");
              }
            }
          posted @ 2007-10-11 21:06 PariScamper 閱讀(715) | 評論 (0)編輯 收藏

          (1) 類名首字母應該大寫。字段、方法以及對象(句柄)的首字母應小寫。對于所有標識符,其中包含的所有單詞都應緊靠在一起,而且大寫中間單詞的首字母。例如: ThisIsAClassName thisIsMethodOrFieldName 若在定義中出現了常數初始化字符,則大寫static final基本類型標識符中的所有字母。這樣便可標志出它們屬于編譯期的常數。 Java包(Package)屬于一種特殊情況:它們全都是小寫字母,即便中間的單詞亦是如此。對于域名擴展名稱,如com,org,net或者edu等,全部都應小寫(這也是Java 1.1和Java 1.2的區別之一)。
          (2) 為了常規用途而創建一個類時,請采取"經典形式",并包含對下述元素的定義: equals()
          hashCode()
          toString()
          clone()(implement Cloneable)
          implement Serializable
          (3) 對于自己創建的每一個類,都考慮置入一個main(),其中包含了用于測試那個類的代碼。為使用一個項目中的類,我們沒必要刪除測試代碼。若進行了任何形式的改動,可方便地返回測試。這些代碼也可作為如何使用類的一個示例使用。
          (4) 應將方法設計成簡要的、功能性單元,用它描述和實現一個不連續的類接口部分。理想情況下,方法應簡明扼要。若長度很大,可考慮通過某種方式將其分割成較短的幾個方法。這樣做也便于類內代碼的重復使用(有些時候,方法必須非常大,但它們仍應只做同樣的一件事情)。
          (5) 設計一個類時,請設身處地為客戶程序員考慮一下(類的使用方法應該是非常明確的)。然后,再設身處地為管理代碼的人考慮一下(預計有可能進行哪些形式的修改,想想用什么方法可把它們變得更簡單)。
          (6) 使類盡可能短小精悍,而且只解決一個特定的問題。下面是對類設計的一些建議:
          ■一個復雜的開關語句:考慮采用"多形"機制
          ■數量眾多的方法涉及到類型差別極大的操作:考慮用幾個類來分別實現
          ■許多成員變量在特征上有很大的差別:考慮使用幾個類
          (7) 讓一切東西都盡可能地"私有"--private??墒箮斓哪骋徊糠?公共化"(一個方法、類或者一個字段等等),就永遠不能把它拿出。若強行拿出,就可能破壞其他人現有的代碼,使他們不得不重新編寫和設計。若只公布自己必須公布的,就可放心大膽地改變其他任何東西。在多線程環境中,隱私是特別重要的一個因素--只有private字段才能在非同步使用的情況下受到保護。
          (8) 謹惕"巨大對象綜合癥"。對一些習慣于順序編程思維、且初涉OOP領域的新手,往往喜歡先寫一個順序執行的程序,再把它嵌入一個或兩個巨大的對象里。根據編程原理,對象表達的應該是應用程序的概念,而非應用程序本身。
          (9) 若不得已進行一些不太雅觀的編程,至少應該把那些代碼置于一個類的內部。
          (10) 任何時候只要發現類與類之間結合得非常緊密,就需要考慮是否采用內部類,從而改善編碼及維護工作(參見第14章14.1.2小節的"用內部類改進代碼")。
          (11) 盡可能細致地加上注釋,并用javadoc注釋文檔語法生成自己的程序文檔。
          (12) 避免使用"魔術數字",這些數字很難與代碼很好地配合。如以后需要修改它,無疑會成為一場噩夢,因為根本不知道"100"到底是指"數組大小"還是"其他全然不同的東西"。所以,我們應創建一個常數,并為其使用具有說服力的描述性名稱,并在整個程序中都采用常數標識符。這樣可使程序更易理解以及更易維護。
          (13) 涉及構建器和異常的時候,通常希望重新丟棄在構建器中捕獲的任何異常--如果它造成了那個對象的創建失敗。這樣一來,調用者就不會以為那個對象已正確地創建,從而盲目地繼續。
          (14) 當客戶程序員用完對象以后,若你的類要求進行任何清除工作,可考慮將清除代碼置于一個良好定義的方法里,采用類似于cleanup()這樣的名字,明確表明自己的用途。除此以外,可在類內放置一個boolean(布爾)標記,指出對象是否已被清除。在類的finalize()方法里,請確定對象已被清除,并已丟棄了從RuntimeException繼承的一個類(如果還沒有的話),從而指出一個編程錯誤。在采取象這樣的方案之前,請確定finalize()能夠在自己的系統中工作(可能需要調用System.runFinalizersonExit(true),從而確保這一行為)。
          (15) 在一個特定的作用域內,若一個對象必須清除(非由垃圾收集機制處理),請采用下述方法:初始化對象;若成功,則立即進入一個含有finally從句的try塊,開始清除工作。
          (16) 若在初始化過程中需要覆蓋(取消)finalize(),請記住調用super.finalize()(若Object屬于我們的直接超類,則無此必要)。在對finalize()進行覆蓋的過程中,對super.finalize()的調用應屬于最后一個行動,而不應是第一個行動,這樣可確保在需要基礎類組件的時候它們依然有效。
          (17) 創建大小固定的對象集合時,請將它們傳輸至一個數組(若準備從一個方法里返回這個集合,更應如此操作)。這樣一來,我們就可享受到數組在編譯期進行類型檢查的好處。此外,為使用它們,數組的接收者也許并不需要將對象"造型"到數組里。
          (18) 盡量使用interfaces,不要使用abstract類。若已知某樣東西準備成為一個基礎類,那么第一個選擇應是將其變成一個interface(接口)。只有在不得不使用方法定義或者成員變量的時候,才需要將其變成一個abstract(抽象)類。接口主要描述了客戶希望做什么事情,而一個類則致力于(或允許)具體的實施細節。
          (19) 在構建器內部,只進行那些將對象設為正確狀態所需的工作。盡可能地避免調用其他方法,因為那些方法可能被其他人覆蓋或取消,從而在構建過程中產生不可預知的結果(參見第7章的詳細說明)。
          (20) 對象不應只是簡單地容納一些數據;它們的行為也應得到良好的定義。
          (21) 在現成類的基礎上創建新類時,請首先選擇"新建"或"創作"。只有自己的設計要求必須繼承時,才應考慮這方面的問題。若在本來允許新建的場合使用了繼承,則整個設計會變得沒有必要地復雜。
          (22) 用繼承及方法覆蓋來表示行為間的差異,而用字段表示狀態間的區別。一個非常極端的例子是通過對不同類的繼承來表示顏色,這是絕對應該避免的:應直接使用一個"顏色"字段。
          (23) 為避免編程時遇到麻煩,請保證在自己類路徑指到的任何地方,每個名字都僅對應一個類。否則,編譯器可能先找到同名的另一個類,并報告出錯消息。若懷疑自己碰到了類路徑問題,請試試在類路徑的每一個起點,搜索一下同名的.class文件。
          (24) 在Java 1.1 AWT中使用事件"適配器"時,特別容易碰到一個陷阱。若覆蓋了某個適配器方法,同時拼寫方法沒有特別講究,最后的結果就是新添加一個方法,而不是覆蓋現成方法。然而,由于這樣做是完全合法的,所以不會從編譯器或運行期系統獲得任何出錯提示--只不過代碼的工作就變得不正常了。
          (25) 用合理的設計方案消除"偽功能"。也就是說,假若只需要創建類的一個對象,就不要提前限制自己使用應用程序,并加上一條"只生成其中一個"注釋。請考慮將其封裝成一個"獨生子"的形式。若在主程序里有大量散亂的代碼,用于創建自己的對象,請考慮采納一種創造性的方案,將些代碼封裝起來。
          (26) 警惕"分析癱瘓"。請記住,無論如何都要提前了解整個項目的狀況,再去考察其中的細節。由于把握了全局,可快速認識自己未知的一些因素,防止在考察細節的時候陷入"死邏輯"中。
          (27) 警惕"過早優化"。首先讓它運行起來,再考慮變得更快--但只有在自己必須這樣做、而且經證實在某部分代碼中的確存在一個性能瓶頸的時候,才應進行優化。除非用專門的工具分析瓶頸,否則很有可能是在浪費自己的時間。性能提升的隱含代價是自己的代碼變得難于理解,而且難于維護。
          (28) 請記住,閱讀代碼的時間比寫代碼的時間多得多。思路清晰的設計可獲得易于理解的程序,但注釋、細致的解釋以及一些示例往往具有不可估量的價值。無論對你自己,還是對后來的人,它們都是相當重要的。如對此仍有懷疑,那么請試想自己試圖從聯機Java文檔里找出有用信息時碰到的挫折,這樣或許能將你說服。
          (29) 如認為自己已進行了良好的分析、設計或者實施,那么請稍微更換一下思維角度。試試邀請一些外來人士--并不一定是專家,但可以是來自本公司其他部門的人。請他們用完全新鮮的眼光考察你的工作,看看是否能找出你一度熟視無睹的問題。采取這種方式,往往能在最適合修改的階段找出一些關鍵性的問題,避免產品發行后再解決問題而造成的金錢及精力方面的損失。
          (30) 良好的設計能帶來最大的回報。簡言之,對于一個特定的問題,通常會花較長的時間才能找到一種最恰當的解決方案。但一旦找到了正確的方法,以后的工作就輕松多了,再也不用經歷數小時、數天或者數月的痛苦掙扎。我們的努力工作會帶來最大的回報(甚至無可估量)。而且由于自己傾注了大量心血,最終獲得一個出色的設計方案,成功的快感也是令人心動的。堅持抵制草草完工的誘惑--那樣做往往得不償失。
          posted @ 2007-10-11 15:24 PariScamper 閱讀(298) | 評論 (0)編輯 收藏

          主站蜘蛛池模板: 乌兰浩特市| 黄龙县| 平罗县| 宁陕县| 武汉市| 湾仔区| 芜湖县| 互助| 阿瓦提县| 琼海市| 武清区| 北流市| 隆子县| 永寿县| 宁晋县| 安西县| 大英县| 京山县| 栖霞市| 大石桥市| 寻乌县| 牙克石市| 漳平市| 土默特右旗| 科技| 高安市| 双流县| 和顺县| 南康市| 班戈县| 台南县| 望江县| 南郑县| 大余县| 来宾市| 从江县| 东源县| 石门县| 凌云县| 抚州市| 达拉特旗|