很久很久以前

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            34 隨筆 :: 4 文章 :: 17 評論 :: 0 Trackbacks

          #

          今天看到一個朋友的Blog, 就忍不住把以前寫的這個代碼拿出來了, 不然這代碼閑著也是閑著. 當然沒有必要照搬全部, 只要中間的那個 zoomImage() 方法即可. 當然還有設置圖片部分透明的方法.

          ?

          /*
          * @(#)BlogMailHandler.java 1.00 2004-10-4
          *
          * Copyright 2004 . All rights reserved.
          * PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
          */
          import java.awt.Color;
          import java.awt.Graphics2D;
          import java.awt.RenderingHints;
          import java.awt.image.BufferedImage;
          import java.io.BufferedInputStream;
          import java.io.ByteArrayInputStream;
          import java.io.ByteArrayOutputStream;
          import java.io.File;
          import java.io.FileOutputStream;
          import java.io.InputStream;
          import java.sql.Timestamp;
          import java.util.Properties;

          import javax.imageio.ImageIO;
          import javax.mail.Address;
          import javax.mail.FetchProfile;
          import javax.mail.Flags;
          import javax.mail.Folder;
          import javax.mail.Message;
          import javax.mail.MessagingException;
          import javax.mail.Multipart;
          import javax.mail.Part;
          import javax.mail.Session;
          import javax.mail.Store;
          import javax.mail.internet.InternetAddress;
          import javax.mail.internet.MimeUtility;

          import studio.beansoft.jsp.StringUtil;
          import com.keypoint.PngEncoder;
          import com.keypoint.PngEncoderB;

          import moblog.*;

          /**
          * BlogMailHandler, 彩E博客郵件的處理程序.
          * 每 15 分鐘更新一次郵件, 發(fā)布博客并更新用戶產量.
          * 郵件 POP3 帳戶信息位于 /blogmail.properties 文件中.
          *
          * @author 劉長炯
          * @version 1.00 2004-10-4
          */
          public class BlogMailHandler extends Thread {
          /**
          * @alias 文件根目錄 : String
          */
          private String rootDirectory;

          // 郵件帳戶配置屬性
          private static Properties props = new Properties();

          static {
          try {
          InputStream in = BlogMailHandler.class
          .getResourceAsStream("/blogmail.properties");
          props.load(in);
          in.close();
          } catch (Exception ex) {
          System.err.println("無法加載配置文件 blogmail.properties:"
          + ex.getMessage());
          ex.printStackTrace();
          }
          }

          // 圖像加載的共享實例, 在 Linux 平臺上有可能無法生成圖形對象
          // private static Frame sharedFrame = new Frame();
          private boolean shouldExit = false;

          public BlogMailHandler() {
          }
          /** 定時開始讀取郵件信息 */
          public void startMailProcessCycle() {
          start();
          }
          public void run() {
          while(!shouldExit) {
          doProcess();
          try {
          // 每15分鐘讀取一次
          Thread.currentThread().sleep(60 * 15 * 1000);
          } catch (Exception e) {
          e.printStackTrace();
          }
          }
          }
          /** 處理進程 */
          private void doProcess() {
          try {
          Store store = openStore();
          Folder inbox = openInbox(store);

          processAllMessages(inbox);
          inbox.close(true);
          store.close();
          } catch (Exception e) {
          e.printStackTrace();
          }
          }
          protected void finalize() throws Throwable {
          shouldExit = true;
          }
          /**
          * 縮放原始圖片到合適大小.
          *
          * @param srcImage - 原始圖片
          * @return BufferedImage - 處理結果
          */
          private BufferedImage zoomImage(BufferedImage srcImage) {
          int MAX_WIDTH = 100;// TODO: 縮放后的圖片最大寬度
          int MAX_HEIGHT = 160;// TODO: 縮放后的圖片最大高度
          int imageWidth = srcImage.getWidth(null);
          int imageHeight = srcImage.getHeight(null);

          // determine thumbnail size from MAX_WIDTH and MAX_HEIGHT
          int thumbWidth = MAX_WIDTH;
          int thumbHeight = MAX_HEIGHT;
          double thumbRatio = (double)thumbWidth / (double)thumbHeight;
          double imageRatio = (double)imageWidth / (double)imageHeight;
          if (thumbRatio < imageRatio) {
          thumbHeight = (int)(thumbWidth / imageRatio);
          } else {
          thumbWidth = (int)(thumbHeight * imageRatio);
          }
          // 如果圖片小于所略圖大小, 不作處理
          if(imageWidth < MAX_WIDTH && imageHeight < MAX_HEIGHT) {
          thumbWidth = imageWidth;
          thumbHeight = imageHeight;
          }

          // draw original image to thumbnail image object and
          // scale it to the new size on-the-fly (drawImage is quite powerful)
          BufferedImage thumbImage = new BufferedImage(thumbWidth,
          thumbHeight, BufferedImage.TYPE_INT_RGB);
          //thumbImage.getsc
          Graphics2D graphics2D = thumbImage.createGraphics();
          graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
          RenderingHints.VALUE_INTERPOLATION_BILINEAR);
          graphics2D.drawImage(srcImage, 0, 0, thumbWidth, thumbHeight, null);
          System.out.println("thumbWidth=" + thumbWidth);
          System.out.println("thumbHeight=" + thumbHeight);
          return thumbImage;
          }

          // Open mail Store
          private Store openStore() throws Exception {
          Store store;
          //--[ Set up the default parameters
          props.put("mail.transport.protocol", "pop");
          props.put("mail.pop.port", "110");
          // props.put("mail.debug", "true");

          Session session = Session.getInstance(props);
          store = session.getStore("pop3");
          // void javax.mail.Service.connect(String host, String user, String
          // password) throws
          // MessagingException
          store.connect(props.getProperty("mail.pop3.host"), props
          .getProperty("username"), props.getProperty("password"));
          return store;
          }

          // Open Inbox
          private Folder openInbox(Store store) throws Exception {
          Folder folder = store.getDefaultFolder();
          if (folder == null) {
          System.out.println("Problem occurred");
          System.exit(1);
          }

          Folder popFolder = folder.getFolder("INBOX");
          popFolder.open(Folder.READ_WRITE);
          return popFolder;
          }

          /** Close mail store. */
          private void closeStore(Store store) {
          try {
          store.close();
          } catch (MessagingException e) {
          e.printStackTrace();
          }
          }

          /**
          * 處理賬號中的所有郵件并刪除這些郵件.
          *
          * @param folder - Folder, 收件箱
          * @throws Exception
          */
          private void processAllMessages(Folder folder) throws Exception {

          Message[] listOfMessages = folder.getMessages();
          FetchProfile fProfile = new FetchProfile();
          fProfile.add(FetchProfile.Item.ENVELOPE);
          folder.fetch(listOfMessages, fProfile);

          for (int i = 0; i < listOfMessages.length; i++) {
          try {
          processSingleMail(listOfMessages[i]);
          } catch (Exception e) {
          e.printStackTrace();
          }

          // Delete mail
          listOfMessages[i].setFlag(Flags.Flag.DELETED, true);
          }
          }

          /**
          * 處理單個 Email, 將文章發(fā)表, 并將附件圖片轉換為 PNG 后存入用戶目錄.
          *
          * @param message -
          * Message, email 消息
          */
          private void processSingleMail(Message message) throws Exception {
          BlogContent content = new BlogContent();
          BlogUser user = new BlogUser();
          BlogPicture picture = new BlogPicture();

          // 1. 假定發(fā)件人為手機號, 并嘗試根據(jù)手機號查找用戶
          Address[] addList = message.getFrom();
          if (addList.length > 0) {
          String userMail = ((InternetAddress) addList[0]).getAddress();
          // 取出 彩E 郵件用戶手機號, 格式: 手機號@someone.com
          String mobileNumber = userMail.substring(0, userMail.indexOf("@"));
          System.out.println("用戶手機號為:" + mobileNumber);
          if (!user.findByMDN(mobileNumber)) {
          // Not found user, then return
          System.err.println("user " + ((InternetAddress) addList[0]).getAddress() + " not found.");
          return;
          }
          }

          // 2. 嘗試讀取郵件正文
          // 復合郵件
          if (message.isMimeType("multipart/*")) {
          // 標記是否處理過圖片
          boolean imageHasProcessed = false;
          Multipart multipart = (Multipart) message.getContent();
          for (int i = 0, n = multipart.getCount(); i < n; i++) {
          // System.err.println("Reading multipart " + i);
          Part part = multipart.getBodyPart(i);
          // System.err.println("ContentType = " + part.getContentType());

          // 3. 處理附件圖片, 只處理第一個圖片
          String disposition = part.getDisposition();
          // System.err.println("disposition = " + disposition);
          if (disposition != null
          && (disposition.equals(Part.ATTACHMENT) || disposition
          .equals(Part.INLINE)) && !imageHasProcessed) {
          // 需要反編碼郵件文件名, 有的郵件的附件的文件名是經過編碼的,
          // 但是 JavaMail 并不能處理出來(BeanSoft, 2004-10-13)
          String fileName = MimeUtility.decodeText(part.getFileName());
          String ext = StringUtil.getExtension(fileName)
          .toLowerCase();
          System.err.println("part.getFileName() = " + fileName);

          if ("gif".equals(ext) || "jpg".equals(ext)
          || "png".equals(ext)) {
          BufferedInputStream dataIn = null;
          // 轉換非 PNG 格式圖片為 PNG 格式 -- 取消
          // if (!"png".equals(ext)) {
          ByteArrayOutputStream pngOut = new ByteArrayOutputStream();
          try {
          // Convert image file to PNG file
          BufferedImage buffImg = ImageIO.read(part
          .getInputStream());
          // Read image file from attachment
          // 縮放圖片
          buffImg = zoomImage(buffImg);

          int imageWidth = buffImg.getWidth(null);
          int imageHeight = buffImg.getHeight(null);
          BufferedImage outImg = new BufferedImage(
          imageWidth, imageHeight,
          // BufferedImage.TYPE_4BYTE_ABGR 是 24 位色深, TYPE_BYTE_INDEXED 是 8 位
          BufferedImage.TYPE_INT_RGB);
          // 使圖片透明
          // embossImage(buffImg, outImg);
          outImg.getGraphics().drawImage(buffImg, 0, 0, imageWidth, imageHeight, null);
          // Save image to PNG output stream
          // ImageIO.write(outImg, "png", pngOut);
          // Save using keypoint PNG encoder
          PngEncoderB pngb = new PngEncoderB(outImg,
          PngEncoder.NO_ALPHA, 0, 9);
          pngOut.write(pngb.pngEncode());
          dataIn = new BufferedInputStream(
          new ByteArrayInputStream(pngOut
          .toByteArray()));
          } catch (Exception e) {
          }
          // } else {
          // dataIn = new BufferedInputStream(part
          // .getInputStream());
          // }
          // All pictures change to png format
          ext = "png"
          // Insert picture info into database
          picture.setBlogID(user.getId());
          picture.setCreationTime(new Timestamp(System
          .currentTimeMillis()));
          picture.setFileEXName(ext);
          picture.setTitle(fileName);
          picture.create();
          // Save png file to user directory, /users/userId/pictureId.png
          FileOutputStream outFile = new FileOutputStream(
          rootDirectory + File.separatorChar + user.getId() + File.separatorChar
          + picture.getId() + "." + ext);
          int c;
          while ((c = dataIn.read()) != -1) {
          outFile.write(c);
          }

          outFile.close();
          imageHasProcessed = true;
          }
          }
          // 純文本郵件, 帶附件
          else if (part.isMimeType("text/plain")) {
          String body = part.getContent().toString();
          String title = message.getSubject();

          content.setBlogID(user.getId());
          content.setCreationTime(new Timestamp(System.currentTimeMillis()));
          content.setTitle(title);
          content.setNote(body);
          }

          // 典型的 HTML 和 文本郵件可選形式, 進一步分析
          if (part.getContent() instanceof Multipart) {
          Multipart subPart = (Multipart) part.getContent();

          for (int j = 0, m = subPart.getCount(); j < m; j++) {
          Part mailText = subPart.getBodyPart(j);

          if (mailText.isMimeType("text/plain")) {
          String body = mailText.getContent().toString();
          String title = message.getSubject();

          content.setBlogID(user.getId());
          content.setCreationTime(new Timestamp(System.currentTimeMillis()));
          content.setTitle(title);
          content.setNote(body);
          break;
          }
          }
          }

          }// End of multipart parse

          // 4. 創(chuàng)建博客記錄
          content.setPictureId(picture.getId());
          if(content.create() > 0) {
          // 更新用戶產量
          user.setPostTimes(user.getPostTimes() + 1);
          user.update();
          }
          }
          // 純文本郵件, 無附件
          else if (message.isMimeType("text/plain")) {
          String body = message.getContent().toString();
          String title = message.getSubject();

          content.setBlogID(user.getId());
          content.setCreationTime(new Timestamp(System.currentTimeMillis()));
          content.setTitle(title);
          content.setNote(body);

          if(content.create() > 0) {
          // 更新用戶產量
          user.setPostTimes(user.getPostTimes() + 1);
          user.update();
          }
          }
          }

          // 測試, 嘗試連接一次到郵件服務器
          public static void main(String[] args) {
          BlogMailHandler handler = new BlogMailHandler();

          // TODO: debug, 請在 JSP 里面設置圖片目錄的根路徑
          handler.rootDirectory = "F:/Moblog/users/"
          handler.doProcess();
          }

          /**
          * @return Returns the rootDirectory.
          */
          public String getRootDirectory() {
          return rootDirectory;
          }

          /**
          * @param rootDirectory
          * The rootDirectory to set.
          */
          public void setRootDirectory(String property1) {
          this.rootDirectory = property1;
          }

          /** Make image transparent */
          private void embossImage(BufferedImage srcImage, BufferedImage destImage) {
          int width = srcImage.getWidth();
          int height = srcImage.getHeight();

          for (int i = 0; i < height; i++) {
          for (int j = 0; j < width; j++) {
          int newColor = handlesinglepixel(j, i, srcImage.getRGB(j, i));
          destImage.setRGB(j, i, newColor);
          }
          }
          }

          // Handle picture single pixel, change 0xff00ff color to transparent
          private int handlesinglepixel(int x, int y, int pixel) {
          int alpha = (pixel >> 24) & 0xff;
          int red = (pixel >> 16) & 0xff;
          int green = (pixel >> 8) & 0xff;
          int blue = (pixel) & 0xff;
          // Deal with the pixel as necessary...
          // alpha 為 0 時完全透明, 為 255 時不透明
          Color back = new Color(0xFF00FF);
          // 將與背景色相同(此處PNG圖片為紫色)的像素點的透明度設為透明
          if (isDeltaInRange(back.getRed(), red, 2)
          && isDeltaInRange(back.getGreen(), green, 2)
          && isDeltaInRange(back.getBlue(), blue, 2)) {
          // System.out.println("x=" + x + "y=" + y + " is transparent.");
          alpha = 0;
          }
          // red = red / 2;
          // //green = green / 2 + 68;
          // blue = blue / 2;

          return alpha << 24 | red << 16 | green << 8 | blue;
          }

          // 判斷兩個整數(shù)的差是否在一定范圍之內
          private boolean isDeltaInRange(int first, int second, int range) {
          if (first - second <= range && second - first <= range)
          return true;
          return false;
          }
          }

          • #?re: Java 中收取郵件并自動縮放圖片的代碼(原創(chuàng))
            冷面閻羅
            Posted @ 2006-12-29 18:31
            不錯!
            自己也寫過java收發(fā)郵件的程序!??回復??
          • #?re: Java 中收取郵件并自動縮放圖片的代碼(原創(chuàng))
            托托姆
            Posted @ 2006-12-30 12:10
            不知道BeanSoft兄是不是看了我昨天的帖子有此感想。。。:)
            我測試了一下BeanSoft兄的zoomImage() 方法,如果按原代碼執(zhí)行,原本圖片透明的部分將變成黑色。如果修改TYPE_INT_RGB為TYPE_INT_ARGB,則能避免這個問題。
          posted @ 2006-12-30 13:27 Long Long Ago 閱讀(584) | 評論 (0)編輯 收藏

          寫程序中遇到一個問題?如下:
          mySoc?=?new?Socket(svrAddress,5555);
          
          
          myInput?=?new?ObjectInputStream(mySoc.getInputStream());//有問題
          myOutput?=?new?ObjectOutputStream(mySoc.getOutputStream());//有問題
          //myInput?=?new?DataInputStream(mySoc.getInputStream());
          //myOutput?=?new?DataOutputStream(mySoc.getOutputStream());?
          注銷的語句運行可以成功
          但是未注銷的那部分?運行時就卡在那里了
          但是卻沒有拋出異常
          請教原因是什么?有什么問題
          該怎么解決呢?

          找了好久終于再網上找到關于這個問題的說明了?因為問題比較特殊?所以貼出來希望對大家
          有幫助
          
          主機端先建立ObjectInputStream后建立ObjectOutputStream,則對應地客戶端要先建立
          ObjectOutputStream后建立ObjectInputStream,否則會造成兩方互相等待數(shù)據(jù)而導致死
          鎖。
          
          原因是建立ObjectInputStream對象是需要先接收一定的header數(shù)據(jù),接收到這些數(shù)據(jù)之前
          會處于阻塞狀態(tài)。故而為了防止這種死鎖狀態(tài),通訊兩方的
          ObjectInputStraem,ObjectOutputStream必須注意順序對應使用。
          
          
          目前相應的解決辦法還沒有找到?如果要解決?可以嘗試重載對象輸入輸出流
          posted @ 2006-12-30 12:46 Long Long Ago 閱讀(951) | 評論 (1)編輯 收藏

          ?????? Tabbed Property是eclipse3.2中新加入一個view,可以使屬性編輯器的功能近乎無限的擴大。這里說明一些Tabbed Property的使用方法。Tabbed Property中分成三個部分,Contributer,Tabs,Sections,一個Contributor包含若干個Tabs,一個Tabs又可以包含若干個sections。下面我們來分別進行描述。
          ????? 1。Contributor 這需要擴展org.eclipse.ui.views.properties.tabbed.PropertyContributor擴展點,定義時,最重要的是定義contributId,這個id必須是全局唯一的,這樣在加載屬性頁時,才能找到這個我們定義的屬性頁,一般地,我們都將對應于這個屬性頁的workbenchpart的id作為本contributor的id,這樣我們在代碼中可以不硬編碼本id字符串,而使用getSite().getId()就可以得到這個id了(當然,這樣定義id不是必須的)。一個property view可以被多個workbench part共享,但 一個workbench part只能有一個property view,這個workbench part需要實現(xiàn)ITabbedPropertySheetPageContributor?接口,這個接口只有一個方法,要求返回本part對應的tabbed property Contributor id,一般只要return getSite().getId();
          ?? contributor有如下幾個attribute:
          ???1)typeMapper,這個類需要實現(xiàn)org.eclipse.ui.views.properties.tabbed.ITypeMapper,主要是實現(xiàn)類型的映射,因為我們選擇的元素并不一定是實現(xiàn)IPropertySource的元素(即能夠給property view提供內容的元素),比如在GEF中,我們選擇的finger實際上是選擇了對應的EditPart,而實際上實現(xiàn)了IPropertySource一般的是model部分的元素,所以這時候我們要將Editpart映射到對應的model元素。
          ???2)labelProvider,需要一個實現(xiàn)org.eclipse.jface.viewers.ILabelProvider的類,主要是在各個tabs的最上面顯示文字和圖片。
          ???3)propertyCategory,用于聚合多個tabs,注意至少要定義一個category,來聚合tabs,否則,可能會顯示property失敗。

          ???2。Tabs,這個需要擴展org.eclipse.ui.views.properties.tabbed.propertyTabs擴展點,其中contributorId就是與之相關聯(lián)的Contributor的id,然后我們可以定義多個tab,這些tab的屬性如下:
          ???1)label,用于顯示在property view的tab bar上的字
          ???2)category,填入的就是在Contributor擴展點中定義的那些category,用于聚合tabs
          ???3)id,本tab的唯一標識
          ???4)afterTab,用于tab之間的排序,如果這是第一個tab,則沒有afterTab,afterTab指的是在本tab之前的那個tab,并且afterTab描述的是在同一個category中的tabs,不同category之間的順序是按照在contributor中定義category的順序來定義的。
          ???5)indented,如果為ture,則各個tabs是有縮進的
          ???6)image,本tab的圖片

          ???3。section ,需要擴展 org.eclipse.ui.views.properties.tabbed.PropertySections擴展點,它的contributionId就是本section所在的Contribution的id,針對每個tab,我們可以定義多個section,每個section的attribut描述如下:
          ???1)id,本secation的唯一標識
          ???2)tab,本section所屬tab的標識
          ???3)class,實現(xiàn)了org.eclipse.ui.views.properties.tabbed.AbstractPropertySection抽象類的類,用于描述這個section的控件和布局。
          ???4)aftersection和上面的aftertab差不多,描述的是同一個tab中的section的順序,注意afterserction描述的是本section之前的section的id
          ???5)filter:一個實現(xiàn)org.eclipse.jface.viewers.IFilter接口的過濾器,對選中元素進行過濾。
          ???6)enableFor:一個用于只是選擇數(shù)目的值,必須要符合這個舒服才能使能這個section。如果不符合,則這個section就被過濾了,如果省略本值,則section的使能器就不會工作了。這是一個自然數(shù),比如,當enableFor=1時,僅僅只有一個元素被選擇的時候,本section才會被使能。

          some notes:
          ??? 上面說過實現(xiàn)ITabbedPropertySheetPageContributor接口的workbench part除了要實現(xiàn)getContributeId方法外,還需要重載getAdapter方法,因為eclipse的默認加載的property veiw時原來的那個view,為了使tabbed property view能夠加載,我們就需要重載getAdapter方法,返回一個TabbedPropertySheetPage對象。

          ??? 在實現(xiàn)section class的時候需要注意,createcontrol時首先應該先創(chuàng)建一個composite,一般是 Composite composite = getWidgetFactory().createFlatFormComposite(parent); 然后各個控件在這個composite上創(chuàng)建。


          posted @ 2006-09-17 22:24 Long Long Ago 閱讀(2884) | 評論 (1)編輯 收藏

          安裝subversion
          基本命令:
          ??$?sudo?apt-get?install?subversion
          ??$?sudo?apt-get?install?libapache2-svn
          可以安裝的包:
          ?apache2
          ?apache2-common
          ?apache2-mpm-prefork
          ?apache2-utils
          ?libapache2-svn
          ?libapache2-mod-auth-pam
          ?libapache2-mod-auth-sys-group
          ?subversion
          ?subversion-tools


          創(chuàng)建一個名為subversion的組:groupadd subversion
          將自己(eg.:user)和www-data(apapch2帳號)用戶添加入subversion組,可以編輯/etc/group文件,在最后找到subversion添加入帳號名(eg:user,www-data),看上去就像這樣:subversion:x:1001:www-data,exp
          然后是創(chuàng)建subversion庫,并賦予subversion組中用戶有讀寫subversion庫的權限:
          ???$?sudo?mkdir?/home/svn??#創(chuàng)建svn庫的父路徑
          ???$?cd?/home/svn
          ???$?sudo?mkdir?myproject??#創(chuàng)建本svn庫的目錄
          ???$?sudo?svnadmin?create?/home/svn/myproject?#使用svn命令,創(chuàng)建svn庫
          ???$?sudo?chown?-R?root:subversion?myproject?#更改本目錄的組
          ???$?sudo?chmod?-R?g+rws?myproject?#給本目錄的組用戶增加讀寫和遞歸增加新加目錄的讀寫權限
          注意上面提到的命令順序,如果最后再執(zhí)行創(chuàng)建庫的命令(svnadmin create ....)則創(chuàng)建的文件沒有獲得組用戶寫的權限,這樣在外部訪問提交的時候會出錯.
          對于本機,可以直接使用file命令來訪問:
          ??$?svn?co(or?checkout)?file:///home/svn/myproject
          #or
          ??$?svn?co?file://localhost/home/svn/myproject
          注意:如果您并不確定主機的名稱,您必須使用三個斜杠(///),而如果您指定了主機的名稱,則您必須使用兩個斜杠(//).
          此時對svn庫的權限是基于文件系統(tǒng)的,只要是subversion組中的用戶都可以訪問本svn庫。

          接下來,講述如何使用apache服務器來提供對svn庫的訪問
          編輯文件/etc/apache2/mods-available/dav_svn.conf
          增加如下的內容:
          ??<Location?/svn/myproject>
          ?????DAV?svn
          ?????SVNPath?/home/svn/myproject
          ?????AuthType?Basic
          ?????AuthName?"myproject?subversion?repository"
          ?????AuthUserFile?/etc/subversion/passwd
          ?????
          <LimitExcept?GET?PROPFIND?OPTIONS?REPORT>
          ????????Require?valid-user
          ?????
          </LimitExcept>
          ??
          </Location>

          apache會解析url中的/svn/myproject部分,來定位svn庫,當收到此請求時,會查詢svn庫:/home/svn/myproject,這里的認證方式是basic,對于訪問要求valid-user,帳號文件在/etc/subversion/passwd中。
          注意重新設置后要重啟apache2:sudo /etc/init.d/apache2 restart
          編輯生成帳號文件: sudo htpasswd2 -c /etc/subversion/passwd user? #給user帳號創(chuàng)建口令
          這時候可以通過瀏覽器來瀏覽svn庫了
          在我的設置中發(fā)現(xiàn),apache2會自動綁定ipv6地址,可能會有些問題,可以強制apache綁定v4地址,在/etc/apache2/port.conf中改成:Listen [bindedip]:[port]的形式

          通過https來訪問svn庫
          首先生成一個 SSL 簽名,使用命令

          ?# apache2-ssl-certificate

          這里會有一系列關于你的個人隱私的問題,回答完了,自然的簽名也就生成了,然
          后我們就要在 apache2 里面打開 SSL 了,現(xiàn)在要做的是開啟 ssl 模塊

          ?# a2enmod ssl

          然后,使用 apache2 的虛擬主機功能來添加 SSL 的支持,將

          ?/etc/apache2/sites-available/default

          復制一份,叫

          ?/etc/apache2/sites-available/ssl

          好啦

          修改 default 文件的開頭為

          ?NameVirtualHost *:80
          ?<VirtualHost *:80>

          修改 ssl 文件的開頭為

          ?NameVirtualHost *:443
          ?<VirtualHost *:443>

          這里 443 是 SSL 的標準端口。

          并在 ssl 文件中加入如下內容,在<VirtualHost></VirtualHost>內

          ?SSLEngine On
          ?SSLCertificateFile /etc/apache2/ssl/apache.pem

          保存文件后,運行命令

          ?? # a2ensite ssl

          來激活這個虛擬主機

          現(xiàn)在,修改文件

          ?/etc/apache2/ports.conf

          加上一行

          ?Listen 443

          好了,到此為止,SSL 服務器配置完成,重新啟動 apache 吧。

          ?

          一些問題:
          可能出現(xiàn) RA layer request failed svn: MKACTIVITY of 400 Bad Request 之類的錯誤,這可能是因為使用了代理的原因,代理不支持svn的擴展命令,see:http://subversion.tigris.org/faq.html#proxy
          還有種原因,就是可能是你的客戶端使用的是windowsxp,其他版本的windows我沒試過,也是這樣的錯誤,在linux下正常,解決方法不太清楚。
          RA layer request failed svn: MKACTIVITY of 400 Bad Request,無論什么原因都可以用https代替http來暫時解決這樣的問題。

          參考:
          http://fanqiang.chinaunix.net/app/web/2005-05-18/3257.shtml
          http://wiki.ubuntu.org.cn/SubVersion?highlight=%28subversion%29

          posted @ 2006-09-05 17:00 Long Long Ago 閱讀(2996) | 評論 (0)編輯 收藏

          在sources.list中添加如下幾個源:
          deb?http://www.beerorkid.com/compiz/?dapper?main
          deb?http://xgl.compiz.info/?dapper?main
          deb-src?http://xgl.compiz.info/?dapper?main
          添加代理:
          export?http_proxy="http://xxx.xxx.xxx.xxx:xxxx"
          獲取pgp密鑰:
          wget?http://www.beerorkid.com/compiz/quinn.key.asc?-O?-?|?sudo?apt-key?add?-?

          nivida的驅動:
          sudo?apt-get?install?nvidia-kernel-common?nvidia-glx
          編輯文件:/etc/X11/xorg.conf
          在module部分中確定lode xgl,有如下代碼:
          Load?"glx"
          在devices部分修改除了Identifier行的其他各行,修改后如下:
          Section?"Device"
          ????Identifier-?leave?this?line?alone!
          ????Driver????????"nvidia"
          ????BusID????????"PCI:1:0:0"
          ????Option?????????"RenderAccel"?????????"true"
          EndSection
          在最下面添加Extensions部分,代碼如下:
          Section?"Extensions"
          ??????????Option??"Composite"?"Enable"
          EndSection
          下面是安裝必要的庫文件:
          sudo?apt-get?install?compiz?xserver-xgl?libgl1-mesa?xserver-xorg?libglitz-glx1?compiz-gnome
          以上是引文http://www.ubuntuforums.org/showthread.php?t=131267 中的方法,此文所講的后面是加載方法,我沒有采用,用的是這里講的方法:http://forum.ubuntu.org.cn/viewtopic.php?t=16777 不過這里講的安裝方法中少了一個庫文件,呵呵
          設置xgl啟動入口:
          新建一個xgl啟動腳本/usr/bin/startxgl.sh,內容如下:
          Xgl?-fullscreen?:1?-ac?-accel?glx:pbuffer?-accel?xv:pbuffer?&?sleep?2?&&?DISPLAY=:1
          #?Start?GNOME
          exec?gnome-session?
          使腳本可執(zhí)行: sudo chmod 755 /usr/bin/startxgl.sh
          新建一個compiz腳本/usr/bin/startcompiz,內容如下:
          #!/bin/sh
          killall?gnome-window-decorator
          wait
          gnome-window-decorator?&?LD_PRELOAD=/usr/lib/fglrx/libGL.so.1.2.xlibmesa
          compiz?--replace?gconf?miniwin?decoration?transset?wobbly?fade?minimize?cube?rotate?zoom?scale?move?resize?place?switcher?trailfocus?water?&?
          使得腳本可執(zhí)行:sudo chmod 755 /usr/bin/startcompiz
          在登陸管理器里建一個XGL會話: 建立一個文件/usr/share/xsessions/xgl.desktop ,內容如下:
          [Desktop?Entry]
          Encoding=UTF-8
          Name=XGl
          Exec=/usr/bin/startxgl.sh
          Icon=
          Type=Application?

          打開桌面菜單-〉系統(tǒng)-〉首選項-〉會話
          在最右邊的“啟動程序”里添加 /usr/bin/startcompiz 這句話
          最后不要忘了

          sudo aptitude update
          sudo aptitude upgrade
          關閉所有程序
          ctrl-alt-backspace啟動X
          登錄時在會話中選擇xgl
          會提示是否為默認會話,建議選擇僅本次
          哦,差點忘了,怎么使用:
          CTRL + ALT + Left/right arrow key. Switches to the new side of the cube for me.

          CTRL + ALT + SHIFT + Left/Right arrow key- Takes the in focused app around cube.

          CTRL + ALT + Left Click on Desktop - allows you to use the mouse to rotate cube.

          F12 - uses the Expose like trick

          Alt- Tab - switcher Vista-style
          看起來有點暈,尤其是輸入法的浮動窗體
          posted @ 2006-08-31 13:03 Long Long Ago 閱讀(915) | 評論 (3)編輯 收藏

          java.lang.NoClassDefFoundError: com/sun/tools/javac/Main
          最近在使用java的動態(tài)編譯的時候出現(xiàn)的問題,主要是由于在使用類com.sun.tool.javac.Main時,總是出現(xiàn)NoClassDefFoundError的錯誤,后來找到如下的文章,分析,可能是由于對于包tools.jar的加載問題,雖然我在classpath中聲明了這個包,但在eclipse環(huán)境下,始終都還是出現(xiàn)運行時異常,對于編譯時正確,運行時異常的情況,eclipse一般都是由于其自身的加載機制造成的.在eclipse下,對于一般的java工程,只要設置了系統(tǒng)的classpath,在其中添加了tools.jar包,即可;對于plugin工程,我是將tools.jar包,直接拷貝到本工程下,并在property中引用,而且在META-INF/MANIFEST.MF文件中的Runtime頁的classpath中添加了這個tool.jar包,這樣在運行時就沒有異常了,可以正常編譯了.

          理解Java?ClassLoader機制

          Java?ClassLoader

          2006-5-23
          當JVM(Java虛擬機)啟動時,會形成由三個類加載器組成的初始類加載器層次結構:

          ? ? ? ?bootstrap classloader
          ? ? ? ? ? ? ? ? |
          ? ? ? ?extension classloader
          ? ? ? ? ? ? ? ? |
          ? ? ? ?system classloader

          bootstrap classloader -引導(也稱為原始)類加載器,它負責加載Java的核心類。在Sun的JVM中,在執(zhí)行java的命令中使用-Xbootclasspath選項或使用- D選項指定sun.boot.class.path系統(tǒng)屬性值可以指定附加的類。這個加載器的是非常特殊的,它實際上不是 java.lang.ClassLoader的子類,而是由JVM自身實現(xiàn)的。大家可以通過執(zhí)行以下代碼來獲得bootstrap classloader加載了那些核心類庫:
          ? ?URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
          ? ?for (int i = 0; i < urls.length; i++) {
          ? ? ?System.out.println(urls.toExternalform());
          ? ?}
          在我的計算機上的結果為:
          文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/dom.jar
          文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/sax.jar
          文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xalan-2.3.1.jar
          文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xercesImpl-2.0.0.jar
          文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xml-apis.jar
          文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xsltc.jar
          文件:/C:/j2sdk1.4.1_01/jre/lib/rt.jar
          文件:/C:/j2sdk1.4.1_01/jre/lib/i18n.jar
          文件:/C:/j2sdk1.4.1_01/jre/lib/sunrsasign.jar
          文件:/C:/j2sdk1.4.1_01/jre/lib/jsse.jar
          文件:/C:/j2sdk1.4.1_01/jre/lib/jce.jar
          文件:/C:/j2sdk1.4.1_01/jre/lib/charsets.jar
          文件:/C:/j2sdk1.4.1_01/jre/classes
          這時大家知道了為什么我們不需要在系統(tǒng)屬性CLASSPATH中指定這些類庫了吧,因為JVM在啟動的時候就自動加載它們了。

          extension classloader -擴展類加載器,它負責加載JRE的擴展目錄(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系統(tǒng)屬性指定的)中JAR的類包。這為引入除Java核心類以外的新功能提供了一個標準機制。因為默認的擴展目錄對所有從同一個JRE中啟動的JVM都是通用的,所以放入這個目錄的 JAR類包對所有的JVM和system classloader都是可見的。在這個實例上調用方法getParent()總是返回空值null,因為引導加載器bootstrap classloader不是一個真正的ClassLoader實例。所以當大家執(zhí)行以下代碼時:
          ? ?System.out.println(System.getProperty("java.ext.dirs"));
          ? ?ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();
          ? ?System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());
          結果為:
          C:\j2sdk1.4.1_01\jre\lib\ext
          the parent of extension classloader : null
          extension classloader是system classloader的parent,而bootstrap classloader是extension classloader的parent,但它不是一個實際的classloader,所以為null。

          system classloader -系統(tǒng)(也稱為應用)類加載器,它負責在JVM被啟動時,加載來自在命令java中的-classpath或者java.class.path系統(tǒng)屬性或者 CLASSPATH操作系統(tǒng)屬性所指定的JAR類包和類路徑。總能通過靜態(tài)方法ClassLoader.getSystemClassLoader()找到該類加載器。如果沒有特別指定,則用戶自定義的任何類加載器都將該類加載器作為它的父加載器。執(zhí)行以下代碼即可獲得:
          ? ?System.out.println(System.getProperty("java.class.path"));
          輸出結果則為用戶在系統(tǒng)屬性里面設置的CLASSPATH。
          classloader 加載類用的是全盤負責委托機制。所謂全盤負責,即是當一個classloader加載一個Class的時候,這個Class所依賴的和引用的所有 Class也由這個classloader負責載入,除非是顯式的使用另外一個classloader載入;委托機制則是先讓parent(父)類加載器 (而不是super,它與parent classloader類不是繼承關系)尋找,只有在parent找不到的時候才從自己的類路徑中去尋找。此外類加載還采用了cache機制,也就是如果 cache中保存了這個Class就直接返回它,如果沒有才從文件中讀取和轉換成Class,并存入cache,這就是為什么我們修改了Class但是必須重新啟動JVM才能生效的原因。


          每個ClassLoader加載Class的過程是:
          1.檢測此Class是否載入過(即在cache中是否有此Class),如果有到8,如果沒有到2
          2.如果parent classloader不存在(沒有parent,那parent一定是bootstrap classloader了),到4
          3.請求parent classloader載入,如果成功到8,不成功到5
          4.請求jvm從bootstrap classloader中載入,如果成功到8
          5.尋找Class文件(從與此classloader相關的類路徑中尋找)。如果找不到則到7.
          6.從文件中載入Class,到8.
          7.拋出ClassNotFoundException.
          8.返回Class.

          其中5.6步我們可以通過覆蓋ClassLoader的findClass方法來實現(xiàn)自己的載入策略。甚至覆蓋loadClass方法來實現(xiàn)自己的載入過程。

          類加載器的順序是:
          先是bootstrap classloader,然后是extension classloader,最后才是system classloader。大家會發(fā)現(xiàn)加載的Class越是重要的越在靠前面。這樣做的原因是出于安全性的考慮,試想如果system classloader“親自”加載了一個具有破壞性的“java.lang.System”類的后果吧。這種委托機制保證了用戶即使具有一個這樣的類,也把它加入到了類路徑中,但是它永遠不會被載入,因為這個類總是由bootstrap classloader來加載的。大家可以執(zhí)行一下以下的代碼:
          ? ?System.out.println(System.class.getClassLoader());
          將會看到結果是null,這就表明java.lang.System是由bootstrap classloader加載的,因為bootstrap classloader不是一個真正的ClassLoader實例,而是由JVM實現(xiàn)的,正如前面已經說過的。

          下面就讓我們來看看JVM是如何來為我們來建立類加載器的結構的:
          sun.misc.Launcher,顧名思義,當你執(zhí)行java命令的時候,JVM會先使用bootstrap classloader載入并初始化一個Launcher,執(zhí)行下來代碼:
          ? System.out.println("the Launcher's classloader is "+sun.misc.Launcher.getLauncher().getClass().getClassLoader());
          結果為:
          ? the Launcher's classloader is null (因為是用bootstrap classloader加載,所以class loader為null)
          Launcher 會根據(jù)系統(tǒng)和命令設定初始化好class loader結構,JVM就用它來獲得extension classloader和system classloader,并載入所有的需要載入的Class,最后執(zhí)行java命令指定的帶有靜態(tài)的main方法的Class。extension classloader實際上是sun.misc.Launcher$ExtClassLoader類的一個實例,system classloader實際上是sun.misc.Launcher$AppClassLoader類的一個實例。并且都是 java.net.URLClassLoader的子類。

          讓我們來看看Launcher初試化的過程的部分代碼。

          Launcher的部分代碼:
          public class Launcher ?{
          ? ?public Launcher() {
          ? ? ? ?ExtClassLoader extclassloader;
          ? ? ? ?try {
          ? ? ? ? ? ?//初始化extension classloader
          ? ? ? ? ? ?extclassloader = ExtClassLoader.getExtClassLoader();
          ? ? ? ?} catch(IOException ioexception) {
          ? ? ? ? ? ?throw new InternalError("Could not create extension class loader");
          ? ? ? ?}
          ? ? ? ?try {
          ? ? ? ? ? ?//初始化system classloader,parent是extension classloader
          ? ? ? ? ? ?loader = AppClassLoader.getAppClassLoader(extclassloader);
          ? ? ? ?} catch(IOException ioexception1) {
          ? ? ? ? ? ?throw new InternalError("Could not create application class loader");
          ? ? ? ?}
          ? ? ? ?//將system classloader設置成當前線程的context classloader(將在后面加以介紹)
          ? ? ? ?Thread.currentThread().setContextClassLoader(loader);
          ? ? ? ?......
          ? ?}
          ? ?public ClassLoader getClassLoader() {
          ? ? ? ?//返回system classloader
          ? ? ? ?return loader;
          ? ?}
          }

          extension classloader的部分代碼:
          static class Launcher$ExtClassLoader extends URLClassLoader {

          ? ?public static Launcher$ExtClassLoader getExtClassLoader()
          ? ? ? ?throws IOException
          ? ?{
          ? ? ? ?File afile[] = getExtDirs();
          ? ? ? ?return (Launcher$ExtClassLoader)AccessController.doPrivileged(new Launcher$1(afile));
          ? ?}
          ? private static File[] getExtDirs() {
          ? ? ? ?//獲得系統(tǒng)屬性“java.ext.dirs”
          ? ? ? ?String s = System.getProperty("java.ext.dirs");
          ? ? ? ?File afile[];
          ? ? ? ?if(s != null) {
          ? ? ? ? ? ?StringTokenizer stringtokenizer = new StringTokenizer(s, File.pathSeparator);
          ? ? ? ? ? ?int i = stringtokenizer.countTokens();
          ? ? ? ? ? ?afile = new File;
          ? ? ? ? ? ?for(int j = 0; j < i; j++)
          ? ? ? ? ? ? ? ?afile[j] = new File(stringtokenizer.nextToken());

          ? ? ? ?} else {
          ? ? ? ? ? ?afile = new File[0];
          ? ? ? ?}
          ? ? ? ?return afile;
          ? ?}
          }

          system classloader的部分代碼:
          static class Launcher$AppClassLoader extends URLClassLoader
          {

          ? ?public static ClassLoader getAppClassLoader(ClassLoader classloader)
          ? ? ? ?throws IOException
          ? ?{
          ? ? ? ?//獲得系統(tǒng)屬性“java.class.path”
          ? ? ? ?String s = System.getProperty("java.class.path");
          ? ? ? ?File afile[] = s != null ? Launcher.access$200(s) : new File[0];
          ? ? ? ?return (Launcher$AppClassLoader)AccessController.doPrivileged(new Launcher$2(s, afile, classloader));
          ? ?}
          }

          看了源代碼大家就清楚了吧,extension classloader是使用系統(tǒng)屬性“java.ext.dirs”設置類搜索路徑的,并且沒有parent。system classloader是使用系統(tǒng)屬性“java.class.path”設置類搜索路徑的,并且有一個parent classloader。Launcher初始化extension classloader,system classloader,并將system classloader設置成為context classloader,但是僅僅返回system classloader給JVM。

            這里怎么又出來一個context classloader呢?它有什么用呢?我們在建立一個線程Thread的時候,可以為這個線程通過setContextClassLoader方法來指定一個合適的classloader作為這個線程的context classloader,當此線程運行的時候,我們可以通過getContextClassLoader方法來獲得此context classloader,就可以用它來載入我們所需要的Class。默認的是system classloader。利用這個特性,我們可以“打破”classloader委托機制了,父classloader可以獲得當前線程的context classloader,而這個context classloader可以是它的子classloader或者其他的classloader,那么父classloader就可以從其獲得所需的 Class,這就打破了只能向父classloader請求的限制了。這個機制可以滿足當我們的classpath是在運行時才確定,并由定制的 classloader加載的時候,由system classloader(即在jvm classpath中)加載的class可以通過context classloader獲得定制的classloader并加載入特定的class(通常是抽象類和接口,定制的classloader中是其實現(xiàn)),例如web應用中的servlet就是用這種機制加載的.


          好了,現(xiàn)在我們了解了classloader的結構和工作原理,那么我們如何實現(xiàn)在運行時的動態(tài)載入和更新呢?只要我們能夠動態(tài)改變類搜索路徑和清除classloader的cache中已經載入的Class就行了,有兩個方案,一是我們繼承一個classloader,覆蓋loadclass方法,動態(tài)的尋找Class文件并使用defineClass方法來;另一個則非常簡單實用,只要重新使用一個新的類搜索路徑來new一個classloader就行了,這樣即更新了類搜索路徑以便來載入新的Class,也重新生成了一個空白的cache(當然,類搜索路徑不一定必須更改)。噢,太好了,我們幾乎不用做什么工作,java.netURLClassLoader正是一個符合我們要求的classloader!我們可以直接使用或者繼承它就可以了!

          這是j2se1.4 API的doc中URLClassLoader的兩個構造器的描述:
          URLClassLoader(URL[] urls)
          ? ? ? ? ?Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader.
          URLClassLoader(URL[] urls, ClassLoader parent)
          ? ? ? ? ?Constructs a new URLClassLoader for the given URLs.
          其中URL[] urls就是我們要設置的類搜索路徑,parent就是這個classloader的parent classloader,默認的是system classloader。


          好,現(xiàn)在我們能夠動態(tài)的載入Class了,這樣我們就可以利用newInstance方法來獲得一個Object。但我們如何將此Object造型呢?可以將此Object造型成它本身的Class嗎?

          首先讓我們來分析一下java源文件的編譯,運行吧!javac命令是調用“JAVA_HOME/lib/tools.jar”中的“com.sun.tools.javac.Main”的compile方法來編譯:

          ? ?public static int compile(String as[]);

          ? ?public static int compile(String as[], PrintWriter printwriter);

          返回0表示編譯成功,字符串數(shù)組as則是我們用javac命令編譯時的參數(shù),以空格劃分。例如:
          javac -classpath c:\foo\bar.jar;. -d c:\ c:\Some.java
          則字符串數(shù)組as為{"-classpath","c:\\foo\\bar.jar;.","-d","c:\\","c:\\Some.java"},如果帶有PrintWriter參數(shù),則會把編譯信息出到這個指定的printWriter中。默認的輸出是System.err。

          其中 Main是由JVM使用Launcher初始化的system classloader載入的,根據(jù)全盤負責原則,編譯器在解析這個java源文件時所發(fā)現(xiàn)的它所依賴和引用的所有Class也將由system classloader載入,如果system classloader不能載入某個Class時,編譯器將拋出一個“cannot resolve symbol”錯誤。

          所以首先編譯就通不過,也就是編譯器無法編譯一個引用了不在CLASSPATH中的未知Class的java源文件,而由于拼寫錯誤或者沒有把所需類庫放到CLASSPATH中,大家一定經常看到這個“cannot resolve symbol”這個編譯錯誤吧!

          其次,就是我們把這個Class放到編譯路徑中,成功的進行了編譯,然后在運行的時候不把它放入到CLASSPATH中而利用我們自己的 classloader來動態(tài)載入這個Class,這時候也會出現(xiàn)“java.lang.NoClassDefFoundError”的違例,為什么呢?

          我們再來分析一下,首先調用這個造型語句的可執(zhí)行的Class一定是由JVM使用Launcher初始化的system classloader載入的,根據(jù)全盤負責原則,當我們進行造型的時候,JVM也會使用system classloader來嘗試載入這個Class來對實例進行造型,自然在system classloader尋找不到這個Class時就會拋出“java.lang.NoClassDefFoundError”的違例。

          OK,現(xiàn)在讓我們來總結一下,java文件的編譯和Class的載入執(zhí)行,都是使用Launcher初始化的system classloader作為類載入器的,我們無法動態(tài)的改變system classloader,更無法讓JVM使用我們自己的classloader來替換system classloader,根據(jù)全盤負責原則,就限制了編譯和運行時,我們無法直接顯式的使用一個system classloader尋找不到的Class,即我們只能使用Java核心類庫,擴展類庫和CLASSPATH中的類庫中的Class。

          還不死心!再嘗試一下這種情況,我們把這個Class也放入到CLASSPATH中,讓system classloader能夠識別和載入。然后我們通過自己的classloader來從指定的class文件中載入這個Class(不能夠委托 parent載入,因為這樣會被system classloader從CLASSPATH中將其載入),然后實例化一個Object,并造型成這個Class,這樣JVM也識別這個Class(因為 system classloader能夠定位和載入這個Class從CLASSPATH中),載入的也不是CLASSPATH中的這個Class,而是從 CLASSPATH外動態(tài)載入的,這樣總行了吧!十分不幸的是,這時會出現(xiàn)“java.lang.ClassCastException”違例。

          為什么呢?我們也來分析一下,不錯,我們雖然從CLASSPATH外使用我們自己的classloader動態(tài)載入了這個Class,但將它的實例造型的時候是JVM會使用system classloader來再次載入這個Class,并嘗試將使用我們的自己的classloader載入的Class的一個實例造型為system classloader載入的這個Class(另外的一個)。大家發(fā)現(xiàn)什么問題了嗎?也就是我們嘗試將從一個classloader載入的Class的一個實例造型為另外一個classloader載入的Class,雖然這兩個Class的名字一樣,甚至是從同一個class文件中載入。但不幸的是JVM 卻認為這個兩個Class是不同的,即JVM認為不同的classloader載入的相同的名字的Class(即使是從同一個class文件中載入的)是不同的!這樣做的原因我想大概也是主要出于安全性考慮,這樣就保證所有的核心Java類都是system classloader載入的,我們無法用自己的classloader載入的相同名字的Class的實例來替換它們的實例。

          看到這里,聰明的讀者一定想到了該如何動態(tài)載入我們的Class,實例化,造型并調用了吧!

          那就是利用面向對象的基本特性之一的多形性。我們把我們動態(tài)載入的Class的實例造型成它的一個system classloader所能識別的父類就行了!這是為什么呢?我們還是要再來分析一次。當我們用我們自己的classloader來動態(tài)載入這我們只要把這個Class的時候,發(fā)現(xiàn)它有一個父類Class,在載入它之前JVM先會載入這個父類Class,這個父類Class是system classloader所能識別的,根據(jù)委托機制,它將由system classloader載入,然后我們的classloader再載入這個Class,創(chuàng)建一個實例,造型為這個父類Class,注意了,造型成這個父類 Class的時候(也就是上溯)是面向對象的java語言所允許的并且JVM也支持的,JVM就使用system classloader再次載入這個父類Class,然后將此實例造型為這個父類Class。大家可以從這個過程發(fā)現(xiàn)這個父類Class都是由 system classloader載入的,也就是同一個class loader載入的同一個Class,所以造型的時候不會出現(xiàn)任何異常。而根據(jù)多形性,調用這個父類的方法時,真正執(zhí)行的是這個Class(非父類 Class)的覆蓋了父類方法的方法。這些方法中也可以引用system classloader不能識別的Class,因為根據(jù)全盤負責原則,只要載入這個Class的classloader即我們自己定義的 classloader能夠定位和載入這些Class就行了。

          這樣我們就可以事先定義好一組接口或者基類并放入CLASSPATH中,然后在執(zhí)行的時候動態(tài)的載入實現(xiàn)或者繼承了這些接口或基類的子類。還不明白嗎?讓我們來想一想Servlet吧,web application server能夠載入任何繼承了Servlet的Class并正確的執(zhí)行它們,不管它實際的Class是什么,就是都把它們實例化成為一個Servlet Class,然后執(zhí)行Servlet的init,doPost,doGet和destroy等方法的,而不管這個Servlet是從web- inf/lib和web-inf/classes下由system classloader的子classloader(即定制的classloader)動態(tài)載入。說了這么多希望大家都明白了。在applet,ejb等容器中,都是采用了這種機制.

          對于以上各種情況,希望大家實際編寫一些example來實驗一下。

          最后我再說點別的, classloader雖然稱為類加載器,但并不意味著只能用來加載Class,我們還可以利用它也獲得圖片,音頻文件等資源的URL,當然,這些資源必須在CLASSPATH中的jar類庫中或目錄下。我們來看API的doc中關于ClassLoader的兩個尋找資源和Class的方法描述吧:
                  public URL getResource(String name)
                  用指定的名字來查找資源,一個資源是一些能夠被class代碼訪問的在某種程度上依賴于代碼位置的數(shù)據(jù)(圖片,音頻,文本等等)。
          ? ? ? ? ? ? ? ?一個資源的名字是以'/'號分隔確定資源的路徑名的。
          ? ? ? ? ? ? ? ?這個方法將先請求parent classloader搜索資源,如果沒有parent,則會在內置在虛擬機中的classloader(即bootstrap classloader)的路徑中搜索。如果失敗,這個方法將調用findResource(String)來尋找資源。
                  public static URL getSystemResource(String name)
          ? ? ? ? ? ? ? ?從用來載入類的搜索路徑中查找一個指定名字的資源。這個方法使用system class loader來定位資源。即相當于ClassLoader.getSystemClassLoader().getResource(name)。

          例如:
          ? ?System.out.println(ClassLoader.getSystemResource("java/lang/String.class"));
          的結果為:
          ? ?jar:文件:/C:/j2sdk1.4.1_01/jre/lib/rt.jar!/java/lang/String.class
          表明String.class文件在rt.jar的java/lang目錄中。
          因此我們可以將圖片等資源隨同Class一同打包到jar類庫中(當然,也可單獨打包這些資源)并添加它們到class loader的搜索路徑中,我們就可以無需關心這些資源的具體位置,讓class loader來幫我們尋找了!
          posted @ 2006-08-20 17:28 Long Long Ago 閱讀(12594) | 評論 (5)編輯 收藏

          最近做的gef編輯器在刪除時遇到了一些問題,就是不能通過delete鍵刪除,到處搜集資料,解決了,
          首先需要在相應rcp工程中的ActionBarAdviser類中注冊相應的Action,比如對應于deleteAction,我在方法org.eclipse.ui.application.ActionBarAdvisor#makeAction(IWorkbenchWindow)中注冊deleteAction,如下:
          protected?void?makeAction(final?IWorkbenchWindow?window){
          ????IAction?delAction?
          =?ActionFactory.DELETE.create(window);
          ????register(delAction);
          }


          只是這么設置還是不能刪除相應的圖形元素,需要在相應的編輯器中重載init方法,添加如下的代碼
          public?void?init(IEditorSite?site,?IEditorInput?input)?throws?PartInitException?{
          ????
          //?TODO?Auto-generated?method?stub
          ????super.init(site,?input);
          ????ActionRegistry?registry?
          =?getActionRegistry();
          ????IActionBars?bar?
          =?site.getActionBars();
          ????String?id?
          =?ActionFactory.DELETE.getId();
          ????bar.setGlobalActionHandler(id,registry.getAction(id));
          ????bar.updateActionBars();
          }

          在這里仔細研究會發(fā)現(xiàn),在第一段代碼中實際上時創(chuàng)建了一個action,這是一個RetargetAction,而在super.init()方法會調用一個createAction方法,這里創(chuàng)建的是gef默認的redoAction?undoAction selectionAction deleteAction saveAction.需要注意的是RetargetAction是一種可以跟蹤當前活動的部分,由于retargetAction含有一個id,如果這個活動部分提供的handler的id和retargetAction的id相同,那么相應的對retargetAction的調用就轉嫁成對當前這個活動部分的handle的調用,(當然如果根本就沒有handle,那么這個action會disable).所以,我們可以看出來,這個retargetAction會在gef編輯器激活后調用gef的deleteAction.
          posted @ 2006-08-19 17:35 Long Long Ago 閱讀(1201) | 評論 (0)編輯 收藏

          http://www.approc.com/

          [Java|Eclipse|GEF]Figure是如何定位和設置大小?

          有兩種會創(chuàng)建圖元:
          1、打開圖形文件時,先會創(chuàng)建所有的model,這樣圖元的定位和設置大小是依據(jù)圖元model的size和location屬性,通過圖元EditPart.refreshVisuals()來設置的
          2、通過拖放托盤里面的圖標來建立的圖元,會先設置model的size,然后通過model對應的figure.setPreferredSize()來設置了,而location則是通過鼠標的位置來確定的
          posted @ 2006-08-14 11:18 Long Long Ago 閱讀(681) | 評論 (0)編輯 收藏

          http://www.approc.com/
          現(xiàn)在在3.1里面訪問資源文件變得比較簡單了,用戶可以通過繼承osgi.util.NLS,典型例子:
          public?class?MsgBoundle?extends?NLS{
          ??????
          private?final?static?String?BOUNDLE_NAME?
          ???????????????????????
          =?"resource.msg";
          ??????
          public?static?String?mo_1;
          ??????
          public?static?String?mo_2;

          ??????
          static{
          ?????????NLS.initializeMessage(BOUNDLE_NAME,
          ???????????????????????MsgBoundle.
          class);
          ??????}

          }


          好啦,現(xiàn)在就可以建立資源文件了,文件應該建在哪里呢?跟蹤代碼發(fā)現(xiàn),這個由BOUNDLE_NAME決定,在MessageResourceBoundle.buildVariants()中,會將BOUNDLE_NAME中的“."替換成"/",然后再根據(jù)地區(qū)設定組合幾種不同的資源文件名稱,比如我的就是:

          1、resource/msg_zh_CN.properties
          2、resource/msg_zh.properties
          3、resource/msg.properties
          注意:這三個文件是有順序的哦

          然后通過EclipseClassLoader.getResource()來查找這些文件,并將文件中的值賦予給MsgBoundle對應的成員變量。
          posted @ 2006-08-14 11:16 Long Long Ago 閱讀(841) | 評論 (0)編輯 收藏

          在linux(ubuntu)下引入了在windows工作時建立的一個pluginProject,但是發(fā)現(xiàn),在plugin.xml中require包都沒有了,在3.0以后這些require都是放在MANIFEST.MF文件中的,這兩個文件是同步的,即在編輯plugin.xml文件視圖時會看到MANIFEST.MF文件中的require,不過在從windows引入后就看不到了,不過這是發(fā)現(xiàn)MANIFEST.MF和他所在的目錄META-INF都變成了小寫,改成大寫后,再update classpath就可以了。
          posted @ 2006-07-19 10:26 Long Long Ago 閱讀(349) | 評論 (0)編輯 收藏

          僅列出標題
          共4頁: 上一頁 1 2 3 4 下一頁 
          主站蜘蛛池模板: 新沂市| 井陉县| 米林县| 许昌市| 德昌县| 肇东市| 沧源| 红安县| 封开县| 西和县| 莆田市| 夏邑县| 神木县| 宁城县| 定陶县| 广东省| 兴宁市| 马山县| 大余县| 石家庄市| 克东县| 彭水| 平果县| 宝清县| 宣威市| 驻马店市| 芜湖县| 额尔古纳市| 怀来县| 隆昌县| 临江市| 芒康县| 彭泽县| 嵩明县| 孟连| 巴林左旗| 盐津县| 乌苏市| 海兴县| 旅游| 克山县|