:::
針對查詢「dspace」依關聯性排序顯示文章。依日期排序 顯示所有文章

DSpace擴增MediaFilter格式(安裝篇)

布丁布丁吃布丁

DSpace擴增MediaFilter格式(安裝篇)

DSpace擴增MediaFilter的目錄:

本篇介紹DSpace擴增MediaFilter格式的安裝篇,安裝完之後,還要做一次[dspace]/bin/filter-media才能有展示篇的功能。

需求環境

  • DSpace 1.5.0 release (1.4.2或之後的版本似乎也可以)
  • 作業系統:CentOS 5 Final
  • 必須要具備yum的功能
  • 必須安裝OpenOffice 2.0以上

用yum安裝轉檔軟體

如果你不是root的話,先切換到root權限

[dspace@dspace ~]# su root

修改/etc/yum.repos.d/CentOS-Base.repo

[root@dspace ~]# vim /etc/yum.repos.d/CentOS-Base.repo

你可以上鳥哥來看一下vim文字編輯器要怎麼使用。

在該檔案最後加入以下設定:

[dag]
name=Dag RPM Repository for Red Hat Enterprise Linux
baseurl=http://apt.sw.be/redhat/el$releasever/en/$basearch/dag
gpgcheck=1
enabled=1

把金鑰加入RPM的資料庫

[root@dspace ~]# rpm --import http://dag.wieers.com/packages/RPM-GPG-KEY.dag.txt


如果你沒辦法下載RPM-GPG-KEY.dag.txt的話,可以下載我的備份檔案。(雖然沒什麼意義,他的伺服器掛點的話,之後yum也沒辦法安裝所需要的檔案啊。)

執行yum的安裝指令

[root@dspace ~]# yum -y install ffmpeg mencoder imagemagick python python-imaging

如果安裝都順利進行的話,那麼這個步驟就完成囉。

配置ZoomifyImage

ZoomifyImage的計畫網站中去下載最新版本,目前是1.4版,也可以下載我另外備份的檔案(24.06MB,有點大喔)。解壓縮之後,擺到/opt/當中 。

在Linux指令列之下,你可以用wget下載檔案,然後利用tar解壓縮,將之移至/opt/當中。指令如下:

[root@dspace ~]# wget http://nchc.dl.sourceforge.net/sourceforge/zoomifyimage/ZoomifyImage1_4.tar.gz
[root@dspace ~]# tar -zxvf ZoomifyImage1_4.tar.gz
[root@dspace ~]# mv ZoomifyImage /opt/

ZoomifyImage必須搭配Python才能執行,所以要記得一定要在之前yum安裝步驟中安裝Python喔。

配置JODconverter跟OpenOffice

請下載JODconverter的檔案,取出lib資料夾之中的所有jar檔(除了commons-cli-1.2.jar跟commons-io-1.4.jar之外),放到[dspace]/lib跟Java安裝目錄的擴增目錄(如果你跟我一樣安裝的是JDK 1.6.0.06,那麼路徑就是/usr/java/jdk1.6.0_06/jre/lib/ext/)之中。或是下載我另外備份的檔案:jodconverter-2.2.2-lib.zip

然後伺服器端必需要開啟OpenOffice的服務,用SSH開啟的話不知為何會失敗,請到本機端執行以下指令:

[root@dspace ~]# /usr/lib/openoffice.org2.0/program/soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard

出現以下訊息的時候,表示正常開啟:

image

同樣的,也把這段指令加入開機執行的指令當中吧。

修改/etc/rc.local

[root@dspace ~]# vim /etc/rc.local

請把這段指令「/usr/lib/openoffice.org2.0/program/soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard」加到最後去,這樣就大功告成了。

DSpace註冊Bitstream Format

image

開啟DSpace的管理介面,到「Bitstream Format Regisry(附加檔案(二進位檔案)格式登記)」當中,新增以下幾種檔案格式。支援等級皆選擇「已知」、取消「內部」的打勾。

MIME形式 名稱 完整描述 延伸檔名
video/x-msvideo AVI Audio Video Interleave avi
video/x-flv FLV Flash Video flv
video/x-ms-wmv WMV Windows Media Video wmv
application/vnd.rn-realmedia RM Real Media rm
application/vnd.rn-realmedia-vbr RMVB RealVideo Variable Bit Rate File rmvb
audio/3gpp audio/3gpp 3GPP Multimedia File 3gp
video/3gpp video/3gpp 3GPP Multimedia File 3gp
video/mp4v-es MP4 MPEG-4 Video File mp4
application/zip ZIP ZIP zip
audio/mpeg MP3 MPEG-1 Audio Layer 3 mp3
audio/mid MID Musical Instrument Digital Interface mid
audio/x-aac AAC Advanced Audio Coding aac
audio/flac FLAC Free Lossless Audio Codec flac
audio/ogg OGG Ogg ogg
audio/x-ms-wma WMA Windows Media Audio wma
audio/mp4a-latm M4A MPEG-4 Part 14 m4a
text/tab-separated-values TSV Tab-separated values tsv
text/csv CSV Comma-separated values csv

大部分的檔案格式在DSpace 1.5.0當中已經登錄了,特別是文件檔的類型,包括Microsoft Office跟OpenOffice系列,所以在此不特別敘述。目前我測試機上使用的Bitstream Format Registry資料表可以參考這個檔案,裡面有詳盡的設定,如果之後你發現有些格式沒有辦法正確辨識的話,再來比較這個檔案看看缺了哪些格式。

DSpace增加MediaFilter相關檔案

請將以下檔案下載,新增到指定的位置:

  • mediafilter-dspace-api.zip
    [dspace-source]/dspace-api/src/main/java/org/dspace/app/mediafilter/
  • BitstreamDisplay.zip
    [dspace-source]/dspace-jspui/dspace-jspui-api/src/main?java/org/dspace/app/webui/util/
  • RetrieveZIPServlet.zip
    [dspace-source]/dspace-jspui/dspace-jspui-api/src/main?java/org/dspace/app/webui/util/
  • extension-jspui.zip
    [dspace-source]/dspace-jspui/dspace-jspui-webapp/src/main/webapp/extension/
    ※預設是沒有extension這個資料夾的,請自行建立。

如果你的DSpace是預設的狀態,並且安裝過我之前介紹的XMLMetadata,那麼可以採用我的檔案:

如果你這些檔案已經做過更動,那麼取代的話會造成你設計的功能消失。因此接下來我介紹怎麼調整這些檔案。

DSpace調整display-item.jsp

位置在[dspace-source]/dspace-jspui/dspace-jspui-webapp/src/main/webapp/display-item.jsp

請在「<dspace:item-preview item="<%= item %>" />」程式碼之前,加入以下JavaScript跟CSS的引用語法,以便帶出jQuery UI等效果:

<script type="text/javascript" src="<%= request.getContextPath() %>/extension/jquery.js"></script> 
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/bitstream-display/thickbox.js"></script> <script type="text/javascript" src="<%= request.getContextPath() %>/extension/bitstream-display/jquery-ui/js/jquery-ui-1.7.1.custom.min.js"></script> <script type="text/javascript" src="<%= request.getContextPath() %>/extension/bitstream-display/doDialog.js"></script> <link rel="stylesheet" href="<%= request.getContextPath() %>/extension/bitstream-display/thickbox.css" type="text/css" /> <link rel="stylesheet" href="<%= request.getContextPath() %>/extension/bitstream-display/jquery-ui/css/smoothness/jquery-ui-1.7.1.custom.css" type="text/css" />

 

DSpace調整web.xml

位置在[dspace-source]/dspace-jspui/dspace-jspui-webapp/src/main/webapp/WEB-INF/web.xml

加入以下設定,讓DSpace擁有直接讀取zip檔案的功能:

  <servlet>
    <servlet-name>retrieve-zip</servlet-name>
    <servlet-class>org.dspace.app.webui.servlet.RetrieveZIPServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>retrieve-zip</servlet-name>
    <url-pattern>/retrieve-zip/*</url-pattern>
  </servlet-mapping>

 

DSpace調整ItemTag.java

位置在[dspace-source]/dspace-jspui/dspace-jspui-api/src/main/java/org/dspace/app/webui/util/ItemTag.java

主要是使用BitstreamDisplay.java來分析、製作要顯示的連結。BitstreamDisplay.java詳細的用法我會另開一篇教,現在先簡介一下加入BitstreamDisplay功能的作法。

先在開頭引用BitstreamDisplay

import org.dspace.app.webui.util.BitstreamDisplay;

然後找到私有方法listBitstreams(),修改顯示連結的部份。請找到以下部份的程式碼:

                                // Work out what the bitstream link should be
                                // (persistent
                                // ID if item has Handle)
                                String bsLink = "<a target=\"_blank\" href=\""
                                        + request.getContextPath();

                                if ((handle != null)
                                        && (bitstreams[k].getSequenceID() > 0))
                                {
                                    bsLink = bsLink + "/bitstream/"
                                            + item.getHandle() + "/"
                                            + bitstreams[k].getSequenceID() + "/";
                                }
                                else
                                {
                                    bsLink = bsLink + "/retrieve/"
                                            + bitstreams[k].getID() + "/";
                                }

                                bsLink = bsLink
                                        + UIUtil.encodeBitstreamName(bitstreams[k]
                                            .getName(),
                                            Constants.DEFAULT_ENCODING) + "\">";

            					out
                                    .print("<tr><td headers=\"t1\" class=\"standard\">");
                                out.print(bsLink);
            					out.print(bitstreams[k].getName());
                                out.print("</a>");
                                

            					if (multiFile)
            					{
            						out
                                        .print("</td><td headers=\"t2\" class=\"standard\">");

            						String desc = bitstreams[k].getDescription();
            						out.print((desc != null) ? desc : "");
            					}

            					out
                                    .print("</td><td headers=\"t3\" class=\"standard\">");
                                out.print(UIUtil.formatFileSize(bitstreams[k].getSize()));
            					out
                                .print("</td><td headers=\"t4\" class=\"standard\">");
            					out.print(bitstreams[k].getFormatDescription());
            					out
                                    .print("</td><td class=\"standard\" align=\"center\">");

            					// is there a thumbnail bundle?
            					if ((thumbs.length > 0) && showThumbs)
            					{
            						String tName = bitstreams[k].getName() + ".jpg";
                                    String tAltText = LocaleSupport.getLocalizedMessage(pageContext, "org.dspace.app.webui.jsptag.ItemTag.thumbnail");
            						Bitstream tb = thumbs[0]
                                        .	getBitstreamByName(tName);

            						if (tb != null)
            						{
            							String myPath = request.getContextPath()
                                            	+ "/retrieve/"
                                            	+ tb.getID()
                                            	+ "/"
                                            	+ UIUtil.encodeBitstreamName(tb
                                            			.getName(),
                                            			Constants.DEFAULT_ENCODING);

            							out.print(bsLink);
            							out.print("<img src=\"" + myPath + "\" ");
            							out.print("alt=\"" + tAltText
            									+ "\" /></a><br />");
            						}
            					}

            					out
                                    .print(bsLink
                                            + LocaleSupport
                                                    .getLocalizedMessage(
                                                            pageContext,
                                                            "org.dspace.app.webui.jsptag.ItemTag.view")
                                            + "</a></td></tr>");

然後修改成以下程式碼,注意標註For Bitstream Display的部份:

                                // Work out what the bitstream link should be
                                // (persistent
                                // ID if item has Handle)
                                String bsLink = "<a target=\"_blank\" href=\""
                                        + request.getContextPath();
                                //For Bitstream Display
								String bdLink = request.getContextPath();
								
                                if ((handle != null)
                                        && (bitstreams[k].getSequenceID() > 0))
                                {
                                    bsLink = bsLink + "/bitstream/"
                                            + item.getHandle() + "/"
                                            + bitstreams[k].getSequenceID() + "/";
                                    //For Bitstream Display
                                    bdLink = bdLink + "/bitstream/"
                                            + item.getHandle() + "/"
                                            + bitstreams[k].getSequenceID() + "/";
                                }
                                else
                                {
                                    bsLink = bsLink + "/retrieve/"
                                            + bitstreams[k].getID() + "/";
                                    //For Bitstream Display
                                    bdLink = bdLink + "/retrieve/"
                                            + bitstreams[k].getID() + "/";
                                }

                                bsLink = bsLink
                                        + UIUtil.encodeBitstreamName(bitstreams[k]
                                            .getName(),
                                            Constants.DEFAULT_ENCODING) + "\">";
                                //For Bitstream Display
								bdLink = bdLink + UIUtil.encodeBitstreamName(bitstreams[k]
                                            .getName(),
                                            Constants.DEFAULT_ENCODING);
            					out.print("<tr><td headers=\"t1\" class=\"standard\">");
                                //out.print(bsLink);
            					//out.print(bitstreams[k].getName());
                                //out.print("</a>");
                                
                                //For Bitstream Display
                                BitstreamDisplay db = new BitstreamDisplay(item, bdLink);
                                out.print(db.doDialog(bitstreams[k].getName()));

            					if (multiFile)
            					{
            						out
                                        .print("</td><td headers=\"t2\" class=\"standard\">");

            						String desc = bitstreams[k].getDescription();
            						out.print((desc != null) ? desc : "");
            					}

            					out
                                    .print("</td><td headers=\"t3\" class=\"standard\">");
                                out.print(UIUtil.formatFileSize(bitstreams[k].getSize()));
            					out
                                .print("</td><td headers=\"t4\" class=\"standard\">");
            					out.print(bitstreams[k].getFormatDescription());
            					out.print("</td><td class=\"standard\" align=\"center\">");

            					// is there a thumbnail bundle?
            					if ((thumbs.length > 0) && showThumbs)
            					{
            						String tName = bitstreams[k].getName() + ".jpg";
                                    String tAltText = LocaleSupport.getLocalizedMessage(pageContext, "org.dspace.app.webui.jsptag.ItemTag.thumbnail");
            						Bitstream tb = thumbs[0]
                                        .	getBitstreamByName(tName);

            						if (tb != null)
            						{
            							String myPath = request.getContextPath()
                                            	+ "/retrieve/"
                                            	+ tb.getID()
                                            	+ "/"
                                            	+ UIUtil.encodeBitstreamName(tb
                                            			.getName(),
                                            			Constants.DEFAULT_ENCODING);

            							//out.print(bsLink);
            							//out.print("<img src=\"" + myPath + "\" ");
            							//out.print("alt=\"" + tAltText
            							//		+ "\" /></a>");
            							
            							//For Bitstream Display
            							String thumbnailImg = "<img src=\"" + myPath + "\" alt=\"" + tAltText
            									+ "\" />";
            							out.print(db.doDialog(thumbnailImg));
            							out.print("<br />");
            						}
            					}

            					//out.print(bsLink
            					//	+ LocaleSupport.getLocalizedMessage(pageContext,"org.dspace.app.webui.jsptag.ItemTag.view")
            					//	+ "</a>");
                                
                                //For Bitstream Display
                                out.print(db.doDialog(LocaleSupport.getLocalizedMessage( pageContext, "org.dspace.app.webui.jsptag.ItemTag.view")));
                                out.print("</td></tr>");

 

DSpace調整style.css.jsp

位置在[dspace-source]/dspace-jspui/dspace-jspui-webapp/src/main/webapp/style.css.jsp

請新增一段程式碼,以讓Bitstream的列表置中:

table.miscTable, table.attentionTable {
	margin: auto;
}

效果圖片如下:

image

DSpace修改dspace.cfg設定

位置在[dspace]/config/dspace.cfg

最後,要加入設定,Media Filter才能生效。預設的Media Filter部份設定如下:

#### Media Filter / Format Filter plugins (through PluginManager) ####
# Media/Format Filters help to full-text index content or
# perform automated format conversions

#Names of the enabled MediaFilter or FormatFilter plugins
filter.plugins = PDF Text Extractor, HTML Text Extractor, \
				 Word Text Extractor, JPEG Thumbnail
# [To enable Branded Preview]: remove last line above, and uncomment 2 lines below
#   			 Word Text Extractor, JPEG Thumbnail, \
#   			 Branded Preview JPEG

#Assign 'human-understandable' names to each filter
plugin.named.org.dspace.app.mediafilter.FormatFilter = \
  org.dspace.app.mediafilter.PDFFilter = PDF Text Extractor, \
  org.dspace.app.mediafilter.HTMLFilter = HTML Text Extractor, \
  org.dspace.app.mediafilter.WordFilter = Word Text Extractor, \
  org.dspace.app.mediafilter.JPEGFilter = JPEG Thumbnail, \
  org.dspace.app.mediafilter.BrandedPreviewJPEGFilter = Branded Preview JPEG

#Configure each filter's input format(s)
filter.org.dspace.app.mediafilter.PDFFilter.inputFormats = Adobe PDF
filter.org.dspace.app.mediafilter.HTMLFilter.inputFormats = HTML, Text
filter.org.dspace.app.mediafilter.WordFilter.inputFormats = Microsoft Word
filter.org.dspace.app.mediafilter.JPEGFilter.inputFormats = BMP, GIF, JPEG, image/png
filter.org.dspace.app.mediafilter.BrandedPreviewJPEGFilter.inputFormats = BMP, GIF, JPEG, image/png

請修改成以下設定:

#### Media Filter / Format Filter plugins (through PluginManager) ####
# Media/Format Filters help to full-text index content or
# perform automated format conversions

#Names of the enabled MediaFilter or FormatFilter plugins
filter.plugins = PDF Text Extractor, HTML Text Extractor, \
				 Word Text Extractor, JPEG Thumbnail, \
FFmpeg Image Capturer, MEncoder Image Capturer, \
FFmpeg Video Converter, MEncoder Video Converter, \
Zoomify Image Converter, FFmpeg Audio Converter, \
ImageMagick Converter, OOg Text Converter, OOg PPT Converter, OOg PDF Converter

# [To enable Branded Preview]: remove last line above, and uncomment 2 lines below
#   			 Word Text Extractor, JPEG Thumbnail, \
#   			 Branded Preview JPEG

#Assign 'human-understandable' names to each filter
plugin.named.org.dspace.app.mediafilter.FormatFilter = \
  org.dspace.app.mediafilter.FFmpegVideoFilter = FFmpeg Video Converter, \
  org.dspace.app.mediafilter.FFmpegImgFilter = FFmpeg Image Capturer, \
  org.dspace.app.mediafilter.MEncoderFilter = MEncoder Video Converter, \
  org.dspace.app.mediafilter.MEncImgFilter = MEncoder Image Capturer, \
  org.dspace.app.mediafilter.ZoomifyFilter = Zoomify Image Converter, \
  org.dspace.app.mediafilter.FFmpegAudioFilter = FFmpeg Audio Converter, \
  org.dspace.app.mediafilter.PDFFilter = PDF Text Extractor, \
  org.dspace.app.mediafilter.HTMLFilter = HTML Text Extractor, \
  org.dspace.app.mediafilter.WordFilter = Word Text Extractor, \
  org.dspace.app.mediafilter.JPEGFilter = JPEG Thumbnail, \
  org.dspace.app.mediafilter.BrandedPreviewJPEGFilter = Branded Preview JPEG, \
  org.dspace.app.mediafilter.ImageMagickFilter = ImageMagick Converter, \
  org.dspace.app.mediafilter.OOgPDFFilter = OOg PDF Converter, \
  org.dspace.app.mediafilter.OOgTextFilter = OOg Text Converter, \
  org.dspace.app.mediafilter.OOgPPTFilter = OOg PPT Converter

#Configure each filter's input format(s)
filter.org.dspace.app.mediafilter.PDFFilter.inputFormats = Adobe PDF
filter.org.dspace.app.mediafilter.HTMLFilter.inputFormats = HTML, Text
filter.org.dspace.app.mediafilter.WordFilter.inputFormats = Microsoft Word
filter.org.dspace.app.mediafilter.JPEGFilter.inputFormats = BMP, GIF, JPEG, image/png
filter.org.dspace.app.mediafilter.BrandedPreviewJPEGFilter.inputFormats = BMP, GIF, JPEG, image/png
filter.org.dspace.app.mediafilter.FFmpegVideoFilter.inputFormats = MP4, MPEG, AVI, FLV, WMV
filter.org.dspace.app.mediafilter.FFmpegImgFilter.inputFormats = MP4, MPEG, AVI, FLV, WMV
filter.org.dspace.app.mediafilter.MEncoderFilter.inputFormats = RM, RMVB, video/3gpp, Video Quicktime
filter.org.dspace.app.mediafilter.MEncImgFilter.inputFormats = RM, RMVB, video/3gpp, Video Quicktime
filter.org.dspace.app.mediafilter.ZoomifyFilter.inputFormats = BMP, GIF, JPEG, image/png, TIFF
filter.org.dspace.app.mediafilter.FFmpegAudioFilter.inputFormats = MP3, AAC, FLAC, OGG, WMA, WAV, M4A
filter.org.dspace.app.mediafilter.ImageMagickFilter.inputFormats = TIFF
filter.org.dspace.app.mediafilter.OOgPDFFilter.inputFormats = Microsoft Powerpoint, OpenDocument Presentation, RTF, OpenDocument Spreadsheet, Calc 6.0 spreadsheets, CSV, TSV, Impress 6.0 presentations, Microsoft Word, OpenDocument Text, Writer 6.0 documents
filter.org.dspace.app.mediafilter.OOgTextFilter.inputFormats = Microsoft Powerpoint, OpenDocument Presentation, RTF, OpenDocument Spreadsheet, Calc 6.0 spreadsheets, CSV, TSV, Impress 6.0 presentations, OpenDocument Text, Writer 6.0 documents
filter.org.dspace.app.mediafilter.OOgPPTFilter.inputFormats = Microsoft Powerpoint, Impress 6.0 presentations, OpenDocument Presentation

# Where is Temp Directory?
filter.tempfile.config = /tmp/

# How to excute FFmpeg?
filter.exec.ffmpeg = ffmpeg
# FFmpeg Config
filter.FFmpegVideoFilter.config = -ar 22050 -ab 56 -f flv -y -s 320x240
filter.FFmpegImgFilter.config = -y -f image2 -ss 8 -t 0.001 -s 80x60
filter.FFmpegAudioFilter.config = -ar 22050 -y

# How to excute MEncoder?
filter.exec.mencoder = mencoder
# MEncoder Config
filter.MEncoderFilter.config = -vf scale=320:240 -ffourcc FLV1 -of lavf -lavfopts i_certify_that_my_video_stream_does_not_use_b_frames -ovc lavc -lavcopts vcodec=flv:vbitrate=200 -srate 22050 -oac lavc -lavcopts acodec=mp3:abitrate=56

# How to excute Python?
filter.exec.python = python
# Where is ZoomifyFileProcessor.py ?
filter.exec.zoomifyImage = /opt/ZoomifyImage/ZoomifyFileProcessor.py
# How to excute ImageMagick?
filter.exec.imagemagick = convert

簡單說明一下各設定檔的用處:

  • filter.plugins:要採用哪些filter,輸入他們的名稱。
  • plugin.named.org.dspace.app.mediafilter.FormatFilter:設定各個filter的名稱。
  • filter.org.dspace.app.mediafilter.*******.inputFormats:設定該filter處理的檔案類型,名稱是Bitstream Format當中的「名稱」,所以上面才要先設定Bitstream Format。
  • 以下都是各個filter的設定參數,可以做進一步的微調。如果你要在Windows環境底下執行的話,這些參數可能需要重新調整。

DSpace重新編譯

步驟如下,注意[tomcat]、[dspace-source]跟[dspace]要替換成你Tomcat的位置、原始碼跟安裝目的地的位置:

  1. cd [dspace-source]/dspace/
  2. mvn package
  3. cd [dspace-source]/dspace/target/dspace-1.5.0-build.dir/
  4. ant -Dconfig=[dspace]/config/dspace.cfg update
  5. \cp -rf [dspace]/webapps/* [tomcat]/webapps/
  6. [tomcat]/bin/shutdown.sh
  7. [tomcat]/bin/startup.sh

DSpace執行filter-media

請執行以下指令:

[root@dspace ~]# [dspace]/bin/filter-media -v

加入-v指令,程式就會顯示他處理的詳細過程,也可以看到剛剛新增的MediaFilter有沒有正確地被採用,如下圖:

image

如果有完成的話,就可以找一個有Bitstream的Item頁面看看這些功能有沒有出來囉。

image


因為MediaFilter結合了許多工具,儘管我已經盡量把安裝動作簡化了,但還是很繁雜,如果哪裡有出錯,請務必通知我。下一篇則是給進階者的BitstreamDisplay使用篇,讓你更進一步地去調整display-item或其他的版面。<-- Post Catalog -->

(more...)

DSpace改用Proxool增加連線效率

布丁布丁吃布丁

DSpace改用Proxool增加連線效率

image

Proxool 是一個開放原始碼的連線池(Connection Pool)管理套件,引用一下「Proxool - 快速入門」來介紹一下什麼連線池:

在資料庫應用程式中,資料庫連線的取得是一個耗費時間的動作,為了避免花費時間在連線的取得與關閉上,我們會將連線資源放置在一個池中,有需要連線時就從池中取得,不需要連線時就將之放回池中,以求重複利用連線,連線池也擔任連線數量、連線時間的控制等等動作。

連線池的設置可以讓資料庫連線比較不容易當機(當然,你要惡意地一直對伺服器發出請求,伺服器還是會當的)。DSpace的資料庫連也有設置連線池,但是連線數量一多的時候,系統卻不會踢掉已經發呆太久的連線,而導致發生「PSQLException: This statement has been closed (這個statement已經被關閉)」的例外錯誤。

Proxool在JDBC的連線池當中似乎是頗富盛名的套件,我們試著以Proxool取代DSpace預設的連線池,也大幅度地改善了DSpace的資料庫連線容易當機的問題!此外我順便撰寫了一個可直接使用SQL語法查詢的類別,可直接輸入SQL語法而回傳ResultSet物件,而不需要另外去學習DSpace的DatabaseManager怎麼使用。

以下教學都是基於我之前撰寫的DSpace 1.5.1安裝法,如果你也是照著這方法安裝,那麼以下的路徑設定幾乎不需要更動。如果不是的話,那麼請你務必注意到路徑的地方必需要改成你的環境!

DSpace的路徑我們通常會用[dspace-source]表示DSpace原始的程式檔,在本文中則是「/opt/dspace-1.5.1-src-release」,而[dspace]則是表示DSpace的安裝檔,本文中是「/dspace」,請依照你實際的情況來調整。

安裝步驟

Step 1. 在伺服器上安裝Proxool到Java的函式庫中

請到下載網頁下載Proxool的編譯打包檔,檔案類型為jar。

請取出壓縮檔裡面的proxool-0.9.1.zip/proxool-0.9.1/jarjar/jarjar-0.6.jar

接下來安裝到Java的函式庫當中,請把jarjar-0.6.jar擺到JAVA_HOME/jre/lib/ext/[dspace]/lib/當中。JAVA_HOME是你Java環境路徑檔,以我以前的DSpace安裝法來說,jarjar-0.6.jar的位置應該在/usr/java/jdk1.6.0_12/jre/lib/ext/jarjar-0.6.jar以及/dspace/lib/jarjar-0.6.jar

Step 2. 修改DatabaseManager.java

請修改你的[dsapce-source]/dspace-api/src/main/java/org/dspace/storage/rdbms/DatabaseManager.java,也就是請修改/opt/dspace-1.5.1-src-release2/dspace-api/src/main/java/org/dspace/storage/rdbms/DatabaseManager.java

請找到getConnection()的方法:

    public static Connection getConnection() throws SQLException
    {
        initialize();

        return DriverManager
                .getConnection("jdbc:apache:commons:dbcp:" + poolName);
    }

請修改成以下程式碼:

    public static Connection getConnection() throws SQLException //, ClassNotFoundException
    {
        boolean useProxool = ConfigurationManager.getBooleanProperty("db.proxool",true);
		if (useProxool != true)
		{
			initialize();

        	return DriverManager
                .getConnection("jdbc:apache:commons:dbcp:" + poolName);
		}
		else
		{
			Connection conn = null;
			try {
				Class.forName("org.logicalcobwebs.proxool.ProxoolDriver");
				conn = DriverManager.getConnection("proxool."+ConfigurationManager.getProperty("db.poolname")+":org.postgresql.Driver:"+ConfigurationManager.getProperty("db.url") 
					, ConfigurationManager.getProperty("db.username")
					, ConfigurationManager.getProperty("db.password"));
			}
			catch (ClassNotFoundException e) {e.printStackTrace();}
			return conn;
		}
    }

如果你的DatabaseManager.java之前並沒有修改過,那麼你也可以用我已經改好的DatabaseManager.java檔案 (http://0rz.tw/a4Vat)。

Step.3 加入SQLQuery.java

請新增[dspace-source]/dspace-api/src/main/java/org/dspace/storage/rdbms/SQLQuery.java。或是用我已經寫好的SQLQuery檔案 (http://0rz.tw/a4Vat)。

SQLQuery是直接用SQL語法直接查詢資料庫的類別,內容也很簡單(版權宣告反而比內容還要長),後面章節會教讀者如何使用,程式碼如下:

package org.dspace.storage.rdbms;

import org.dspace.core.Context;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;

public class SQLQuery
{
	private Context context = null;
	private Statement statement = null;
	
	public SQLQuery(Context c)
	{
		context = c;
	}
	
	public ResultSet query(String query) throws SQLException   
    {
		try{
			if (statement != null)
			{
				statement.close();
			}
			statement = context.getDBConnection().createStatement();
			ResultSet rs = statement.executeQuery(query);
			return rs;
		}
		catch (SQLException sqle)
        {
            throw sqle;
        }
		
    }
}
Step.4 修改web.xml

請修改

[dspace-source]/dspace-jspui/dspace-jspui-webapp/src/main/webapp/WEB-INF/web.xml,一樣地你也可以選擇用我已經寫好的web.xml檔案 (http://0rz.tw/a4Vat)。

修改的過程如下:

先找到「</web-app>」,這應該在web.xml的最後一行。

然後在</web-app>之前,加入以下xml設定:

<!-- proxool setting -->
<servlet>
  <servlet-name>ServletConfigurator</servlet-name>
  <servlet-class>org.logicalcobwebs.proxool.configuration.ServletConfigurator</servlet-class>
  <init-param>
    <param-name>propertyFile</param-name>
    <param-value>${dspace.dir}/config/dspace.cfg</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

<!-- Proxool Admin -->
<!-- If you want to check database connection detail, just remove comment marks. -->
<!--
<servlet>
  <servlet-name>proxool</servlet-name>
  <servlet-class>org.logicalcobwebs.proxool.admin.servlet.AdminServlet</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>proxool</servlet-name>
  <url-pattern>/proxool</url-pattern>
</servlet-mapping>


<display-name>proxool</display-name>
<servlet>
<servlet-name>Admin</servlet-name>
<servlet-class>org.logicalcobwebs.proxool.admin.servlet.AdminServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>Admin</servlet-name>
<url-pattern>/admin</url-pattern>
</servlet-mapping>
-->

 

注意到後面的註解標籤「<!—」跟「-->」,裡面的內容是Proxool的管理介面,可以看到你資料庫的連線狀態。稍後我會介紹怎麼開啟Proxool的管理介面,預設我們就把他關起來吧。

Step.5 修改dspace.cfg,增加Proxool.properties

請修改[dspace-source]/dspace/config/dspace.cfg,這個設定檔在安裝DSpace的時候一定會修改到,所以我不提供寫好的檔案,請依照以下步驟修改:

首先先找個地方插入設定,其實插入到哪裡都可以,但我想擺在一個跟資料庫靠近的地方還是比較好。請找到「##### Email settings #####」,通常會位於db.poolname的設定之後,原始的dspace.cfg那部份的程式碼如下:

# Specify a name for the connection pool (useful if you have multiple applications sharing Tomcat's dbcp)
# If not specified, defaults to 'dspacepool'
# db.poolname = dspacepool

##### Email settings ######

# SMTP mail server
mail.server=smtp.myu.edu

請先把db.poolname的註解拿掉,變成如下:(如果你有多個DSpace系統,那麼最好用不同的名稱)

db.poolname = dspacepool

請增加以下程式碼:

##### Proxool settings #####

# Use Proxool, you have to install Proxool's jar file (jarjar-0.6.jar) to JAVA_HOME/jre/lib/ext/
db.proxool = true

# Proxool Properties
jdbc-0.proxool.alias = ${db.poolname}
jdbc-0.proxool.driver-class = ${db.driver}
jdbc-0.proxool.driver-url = ${db.url}
jdbc-0.user = ${db.username}
jdbc-0.password = ${db.password}
jdbc-0.proxool.house-keeping-sleep-time = 3000
jdbc-0.proxool.simultaneous-build-throttle = 5
jdbc-0.proxool.prototype-count = 4
jdbc-0.proxool.maximun-connection-lifetime = 60000
jdbc-0.proxool.maximum-connection-count = 500
jdbc-0.proxool.minimum-connection-count = 3
jdbc-0.proxool.statistics = 1m,15m,1d
jdbc-0.proxool.overload-without-refusal-lifetime = 10000
jdbc-0.proxool.maximum-active-time = 60000
jdbc-0.proxool.statistics-log-level=ERROR

如果你想要使用DSpace預設的連線池,那麼可以把db.proxool設為false。

Proxool可以設定的參數還有很多,以上的設定是我們在測試的過程中決定的連線參數,這讓伺服器在短時間內容易到達連線上限,但又很快可以恢復。有興趣可以到Proxool的網站去查查看Properties的意思,或是參考proxool 连接池配置属性说明

稍後我會介紹之後調整參數的方法,預設值就設成這樣吧!

Step.6 DSpace編譯、配置

由於我們修改了dspace.cfg,所以我們必須使用ant的另一個參數來更新DSpace安裝檔。以下照著這個步驟確實地作吧:

移動到[dspace-source]/dspace

[dspace@dspace ~]# cd /opt/dspace-1.5.1-src-release/dspace

執行「mvn package」。

[dspace@dspace dspace]# mvn package

如果最後看到「[INFO] BUILD SUCCESSFUL」訊息,表示編譯成功。如果失敗了,那麼可能是DatabaseManager.java或是SQLQuery.java這兩個檔案出錯了,請檢查編碼或是錯字等問題,然後再來執行mvn package。

請移動到[dspace-source]/dspace/target/dspace-{verion}-build.dir/

[dspace@dspace dspace]# cd /opt/dspace-1.5.1-src-release/dspace/target/dspace-1.5.1-build.dir/

接下來要用ant配置。如果你是第一次安裝DSpace,那麼請執行以下指令:

[dspace@dspace dspace-1.5.1-build.dir]# ant fresh-install

如果你已經安裝了DSpace,那麼請執行以下指令。請務必注意到dspace.cfg必須要指定你的DSpace安裝位置(dspace.dir),我們通常會用[dspace]來表示!

[dspace@dspace dspace-1.5.1-build.dir]# ant -Dconfig=/dspace/config/dspace.cfg init_configs
[dspace@dspace dspace-1.5.1-build.dir]# ant -Dconfig=/dspace/config/dspace.cfg update_webapps

然後把[dspace]/webapps/jspui複製到你的Tomcat的webapps資料夾當中,請注意到要設置成你的Tomcat的安裝位置

[dspace@dspace ~]# 
\cp -rf /dspace/webapps/jspui /opt/apache-tomcat-6.0.13/webapps/

最後重新啟動Tomcat。

[dspace@dspace ~]# /opt/apache-tomcat-6.0.13/bin/shutdown.sh
[dspace@dspace ~]# /opt/apache-tomcat-6.0.13/bin/startup.sh

大功告成了!

接下來你可以打開你的DSpace,移動到display-item的頁面,然後用連點程式狂按「重新整理」或「F5鍵」,看看你的DSpace哪時候會掛點吧!

附帶一題,我是用連點程式每秒按2下「F5」來搞壞我的DSpace的,歡迎大家分享更有「創意」的搞壞方法。(誤)


進階調整

有些預設安裝沒有開啟的功能,或是之後要調整的參數設定,在這邊我一口氣介紹吧!由於DSpace在經過ant配置完之後,需要修改的位置都不大相同。你當然可以用上面的步驟整個慢慢地修改、mvn package、ant,但也可以用以下的偷吃步來修改!

快速修改Proxool參數

請直接修改[dspace]/config/dspace.cfg,修改完畢之後重新啟動Tomcat即可生效。

請注意!如果你的參數設置錯誤的話,Proxool會使用預設值來連線。預設最大連線數(maximum-connection-count)只有15,連線量一多很快就會當掉了。

你會發現[dspace]/config/dspace.cfg中Proxool有些參數跟上面不一樣,這是因為他經過ant配置,把一些變數替換掉的結果。那些都是資料庫連線的必要參數,請不要變更。

如果你發現更好的參數配置,請務必告訴我喔!

開啟Proxool Admin介面

請修改Tomcat中webapps/jspui/WEB-INF/web.xml (/opt/apache-tomcat-6.0.13/webapps/jspui/WEB-INF/web.xml),把Proxool Admin的註解拿掉。然後重新啟動Tomcat,就用網址http://你的主機:8080/jspui/proxool來開啟Proxool Admin。

image

Proxool Admin的畫面如上,當中可以看到Proxool的各個參數跟現在資料庫的連線狀態。如果你發現參數跟你設置的不一樣,例如最大連線數(maximum-connection-count)只有預設值的15,而不是我們設定的500,那麼就可能是你的Proxool設定檔有問題囉。

當不使用的時候,建議是把Proxool Admin介面關掉比較好喔。

使用SQLQuery

還記得我們剛剛新增了一個「SQLQuery.java」嗎?現在我就來介紹一下他要怎麼在DSpace裡面使用。

首先要先引用SQLQuery。

<%@ page import="org.dspace.storage.rdbms.SQLQuery" %>

然後你必需找到context物件。如果你的程式之中沒有預先宣告的話,那麼你可以用以下方法來建立一個context物件:

<%
Context context = new Context();
%>

然後建立一個SQLQuery物件:

<%
SQLQuery sq = new SQLQuery(context);
%>

接著你就可以使用query方法來取得ResultSet囉!

<%
ResultSet rs = sq.query("SELECT count(*) FROM item");
%>

至於ResultSet要怎麼用嘛……就請你參考PostgreSQL 文檔 發出查詢和處理結果囉。


後記

DSpace儘管擁有相當完整的系統架構,但是資料庫連線這個問題卻是困擾我們已久,連DSpace的說明文件都說叫我們手動去砍掉那些死掉的連線(文件裡面把這些連線稱為「殭屍(zombie)」)。

今天總算用Proxool來改進這個問題,Proxool可以動態地踢掉沒動作的連線,比起DSpace內建的連線池還來得聰明的多,真是讓人感動!

最後,請不要問我為什麼不寫論文,而一直在研究DSpace,我只能告訴你這是教育部全國通識計畫要用的系統而已……<-- Post Catalog -->

(more...)

DSpace系統安裝及設定指南

布丁布丁吃布丁

2 Comments

DSpace系統安裝及設定指南

邱偉嘉
政大圖檔所百年圖書館數位典藏計畫的系統開發維護小組
(2008/1/14)

此文件內容由政大圖檔所百年圖書館數位典藏計畫的系統開發維護小組撰寫,內容包含DSpace所有需具備的環境、安裝程序、後續維護及系統開發建議。



一、管理及開發人員

DSpace網站管理人員需要基礎的linux操作技能,如果你已經具備UNIX-like OS的管理經驗更好。系統開發人員需要具備Java/Jsp/Tomcat/PostgreSQL(or other database)基礎知識,如果你已經具備OOP (Object-oriented programming)、MVC (Model-View-Controller,模型-視圖-控制器模式)、ORM (Object-Relational mapping)的知識更好。

二、DSpace安裝及設定

安裝環境需求

DSpace在UNIX-like OS上有比較好的支援度,也比較穩定,以下將以Step by step的方式說明關於DSpace的安裝及設定。DSpace安裝的環境需求包括:

  • UNIX-like OS (CentOS, RHEL, SUSE etc)
  • Java 1.4 或者更新的版本(SDK即可不需要J2EE)
  • Apache Ant 1.6.2或者更新的版本
  • PostgreSQL 7.3 或Oracle 9或者更新的版本
  • Jakarta Tomcat 4.x/5.x 或者其他類似的Jsp server像是Jetty或Caucho Resin

CentOS

在UNIX-like OS中,我們有很多的選擇,包CentOS (Community ENTerprise Operating System)、RHEL (Red Hat Enterprise Linux)、SUSE等,其中RHEL是需付費的企業用linux發行版,是受到肯定最穩定的distribution,而CentOS是Linux發行版之一,它是來自於Red Hat Enterprise Linux依照開放原始碼規定釋出的原始碼所編譯而成。由於出自同樣的原始碼,因此有些要求高度穩定性的伺服器以CentOS替代商業版的Red Hat Enterprise Linux使用 (維基百科, 2007)。兩者的不同,在於CentOS並不包含封閉原始碼軟體,也沒有Red Hat公司的服務,但是CentOS是不需要付費的。通常伺服器的RAID (Redundant Array of Independent Disks,磁碟陣列)都受到RHEL/CentOS的支援,而在我們的例子中支援RAID是必須的,以提高伺服器的安全機制,所以最終我們選擇CentOS作為我們的OS。

CentOS安裝說明

在下面幾個地方可以下載到安裝ISO檔。

到BIOS設定「使用光碟開機(Boot from CD-ROM)」,將光碟放入並且重新啟動電腦,即可進入安裝畫面,按照電腦的指示一步一步完成安裝。需要注意的是如果你也想要使用RAID來安裝你的伺服器,你必須選擇你的伺服器支援RAID的RHEL/CentOS版本,過高或過低都不行,否則可能會有驅動程式無法支援的問題。如果你需要更詳細的說明請參照 http://apt.nc.hcc.edu.tw/web/student_server_centos/student_server_centos.html

Java安裝

安裝完CentOS之後,一般都已經內建Java SDK,如果你想要升級你的Java到更新的版本,你可以透過CentOS帶的Yum指令來升級,指令如下。

yum update java

關於Yum指令更詳細的說明請參照http://linux.vbird.org/linux_server/0220upgrade.php#yum

你可以通過Java -version指令來檢查你的Java版本以及是否安裝成功。

Apache Ant

Ant是一套方便Java佈署應用程式的軟體,而DSpace便是採用Ant來佈署,這樣你就不必自己手動安裝佈署DSpace。

Apache Ant安裝及設定

  1. 你可以在http://ant.apache.org/下載到最新的binary版本。一般我們會將下載回來的檔案解壓縮至/opt。
  2. 設置環境變數。指令如下

    vim /etc/profile

    加入以下設定(請注意版本與路徑要以你的設定為主)

    ANT_HOME=/opt/apache-ant-1.7.0
    JAVA_HOME=/usr/java/jdk1.6.0_02
    PATH要加入$ANT_HOME/bin。

  3. 你可以通過ant -version指令來檢查你的ant版本以及是否安裝成功。
    更詳細的Ant安裝及使用說明可以參照http://ant.apache.org/

PostgreSQL

DSpace支援的資料庫有PostgreSQL及Oracle,而Oracle是需要付費的資料庫,PostgreSQL則不用,所以我們採用PostgreSQL作為DSpace的支援資料庫。

PostgreSQL安裝

CentOS已經內建PostgreSQL。如果你想要升級你的PostgreSQL到更新的版本,你可以透過CentOS帶的Yum指令來升級,指令如下。

yum update postgresql

PostgreSQL設定

安裝完PostgreSQL之後我們還需要做一些基本設定,才能夠讓DSpace使用PostgreSQL。

開啟PostgreSQL的TCP/IP的連線,編輯postgresql.conf通常會放在 (/var/lib/pgsql/data或者/usr/local/pgsql/data) Linux操作如下。

vim postgresql.conf
加入tcpip_socket = true
加入listen_addresses = 'localhost' #限定只能localhost連線
vim pg_hba.conf
加入host dspace dspace 127.0.0.1 255.255.255.255 md5

如果你想要開放所有的對外連線來查看你的資料庫(ex. pgAdmin),則你修改的內容必須更改為

vim postgresql.conf
加入tcpip_socket = true
加入listen_addresses = '*' #限定任何IP皆可連線
vim pg_hba.conf
加入host dspace dspace 127.0.0.1 0.0.0.0 md5

(注意,開放所有對外連線是不安全的,你必須明確的了解你做什麼事情。)

最後重新啟動PostgreSQL。

Tomcat

Java/Jsp有很多的伺服器選擇,其中Tomcat是最多人使用的伺服器,且將來若考慮到與Apache(在linux中穩定性最高的網頁伺服器)結合也有比較好的支援度。所以我們採以Tomcat作為我們的Java/Jsp伺服器。

Tomcat安裝

你可以在http://jakarta.apache.org/下載最新的Tomcat development版本,將它放到/opt底下,用tar解開。

tar zxvf apache-tomcat-deployer-6.0.13.tar.gz

執行後會安裝到/opt/apache-tomcat-6.0.13。一樣我們還要設置環境變數。

vim /etc/profile

加入

CATALINA_HOME=/opt/apache-tomcat-6.0.13
CLASSPATH=$CATALINA_HOME/common/lib/servlet-api.jar

將Java加入PATH路徑

PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$ANT_HOME/bin

export變數

export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE INPUTRC JAVA_HOME ANT_HOME CATALINA_HOME

設定以utf-8來啟動Tomcat,加入

JAVA_OPTS="-Xmx512M -Xms64M -Dfile.encoding=UTF-8"

編輯[tomcat]/ config/server.xml ([tomcat]是你tomcat的安裝路徑)

vim [tomcat]/ config/server.xml

在下面類似的位置加入URIEncoding="UTF-8"

<!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->
<Connector port="8080"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true"
URIEncoding="UTF-8" />

執行下面程式碼,來重新啟動Tomcat。

[tomcat]/bin/stratup.sh

DSpace快速安裝

前置作業及設定

  1. 在CentOS建立DSpace的帳號

    useradd -m dspace

  2. http://www.dspace.org/下載最新的DSpace source code(本計劃是使用DSpace 1.41版本),放在/opt,並解壓縮。

    gunzip -c dspace-source-1.x.tar.gz | tar -xf -

  3. 複製PostgreSQL JDBC driver (.jar file)到[DSpace-source]/lib,你可以在/usr/share/java/postgresql-jdbc2.jar找到,或者到PostgreSQL JDBC site下載你支援你PostgreSQL版本的JDBC2。
    1. JDBC:Java Database Connectivity,Java資料庫連接,請參考Wikipedia http://zh.wikipedia.org/w/index.php?title=JDBC&variant=zh-tw
    2. [DSpace-source]:你的DSpace原始安裝檔的位置
  4. 建立DSpace在PostgreSQL的帳號:

    createuser -U postgres -W -d -S -P dspace

  5. 建立一個名為DSpace的database:

    createdb -U dspace -E UNICODE dspace

  6. 然後輸入你要設定的密碼
  7. 編輯[DSpace-source]/config/dspace.cfg:

    vim [DSpace-source]/config/dspace.cfg

    下面是你會用到的欄位

    • dspace.dir -- must be set to the [DSpace] (installation) directory.
    • dspace.url -- complete URL of this server's DSpace home page.
    • dspace.hostname -- fully-qualified domain name of web server.
    • dspace.name -- "Proper" name of your server, e.g., "My Digital Library".
    • db.password -- the database password you entered in the previous step.
    • mail.server -- fully-qualified domain name of your outgoing mail server.
    • mail.from.address -- the "From:" address to put on email sent by DSpace.
    • feedback.recipient -- mailbox for feedback mail.
    • mail.admin -- mailbox for DSpace site administrator.
    • alert.recipient -- mailbox for server errors/alerts (not essential but very useful!)

使用Ant來佈署你的DSpace

  1. 在跟(root)目錄建立一個DSpace目錄

    cd /
    mkdir dspace

    (/dspace這個安裝路徑以下以[DSpace]取代)
  2. 變更DSpace目錄的擁有者(chown是改變擁有者的指令):

    chown dspace dspace

  3. 編譯並且安裝DSpace:

    cd [DSpace-source]
    ant fresh_install

  4. 複製DSpace Web application archives (.war files)到你的Tomcat目錄。

    cp [DSpace-source]/build/*.war [tomcat]/webapps

  5. 建立administrator(管理者)帳號:

    [DSpace]/bin/create-administrator

  6. 重新啟動Tomcat。透過URL檢查你安裝是否成功

    http://[伺服器的IP]:8080/dspace (8080 port是tomcat預設連接埠)

如果能夠看到DSpace初始畫面,恭喜你已經安裝成功,若否,請檢查你是否有遺漏的設定,或者聯絡:chiu.wei.jia@gmail.com

三、進階系統設定

在安裝DSpace之後你可能還會需要一些設定讓你的管理工作更輕鬆,以下介紹幾個簡單的功能。

cron-建立週期性自動執行指令

在linux上我們可以使用crontab指令來編輯我們所需要的例行性工作。以下是我們所建議的設定,你可以依據你的需求來建立屬於你的DSpace例行性任務。使用方法如下:

crontab -e

加入以下設定

# Run the media filter at 02:00 every day
0 2 * * * [DSpace]/bin/filter-media

你可以依照你的需求增加與修改,更多個功能情可以查看http://zh.wikipedia.org/w/index.php?title=JDBC&variant=zh-tw

開機時自動啟動服務

如果你想要開機就啟動一些功能(ex: Tomcat伺服器),那你可以修改/etc/rc.d/rc.local檔:

vim /etc/rc.d/rc.local

加入以下設定

#啟動tomcat
/opt/apache-tomcat-6.0.13/bin/startup.sh

備份

如果你想要讓DSpace可以有自動備份的機制,你可以寫一個shell檔(.sh file)裡面寫著備份的程式碼,然後再以先前介紹的方法加入周期性執行。Shell程式碼如下:

dt=`date +%Y%m%d`
mkdir /home/backup/dspace/$dt
chmod 755 /home/backup/*

#備份/DSpace
tar -zcvf /home/backup/dspace/$dt/dspace.tar.gz /dspace

#備份source code
tar -zcvf /home/backup/dspace/$dt/dspace-1.4.1-source.tar.gz /opt/dspace-1.4.1-source

#備份DSpace資料庫
pg_dump dspace > /home/backup/dspace/$dt/dspace_sql.backup

四、總結

上面所介紹的安裝及進階功能都可以依照你自己的需求及情況作調整,無需照抄。若有不明白的部分可以連絡我們。

五、參考資料與網站

(more...)

DSpace 1.5.0加入CKIP中文斷詞器 / Add CKIP Chinese Analyzer in DSpace 1.5.0

布丁布丁吃布丁

0 Comments

DSpace 1.5.0加入CKIP中文斷詞器 / Add CKIP Chinese Analyzer in DSpace 1.5.0

image

原本DSpace 1.5.0使用的是Lucene的ChineseAnalyzer,最近我需要把CKIP中文斷詞的服務加進DSpace當中,於是我自己做一個適合DSpace 1.5.0版本的CKIPChineseAnalyzer。這僅供需要研究斷詞器的人使用,一般使用者不建議更換斷詞器。以下介紹這個CKIPChineseAnalyzer的安裝步驟。

(more...)

談DSpace-DLLL系統

談DSpace-DLLL系統

image

政大圖檔所於2011年5越27日舉辦了「數位檔案加值與教學應用研討會」。早上的場次中,我的指導老師陳志銘教授(真‧教授囉!)參與主講「DSpace-DLLL開放源碼數位典藏系統建置與應用」,講述我們實驗室DSpace改良開發成DSpace-DLLL,並應用於臺灣百年圖書館史數位圖書館全國通識網課程資料庫的內容。

以下是演講時的投影片:(SkyDrive下載)


關於DSpace-DLLL

如果常常閱讀這個Blog的人,一定知道這個「DSpace-DLLL」就是我在寫書中做出來的系統。這個演講底下的意義是為了書的出版鋪路,老師也很有良心,不把研討會當廣告臺,所以演講中並沒有提到書的事情,而是私下詢問時才會透漏給有興趣的人知道。


由於之前我在準備博士班的考試,老師並沒有特別告知我要進行這篇的演講,而是在我博士班筆試都考完之後才請我做投影片,所以我是最近才知道這篇文章的內文在講些什麼。儘管作為一個熟悉者,可以看得出學弟妹整理的文章中有很多破綻,不過將自己做的作品分享出去這件事情,還是挺令人驕傲的。而且這篇文章的發表也能幫學弟妹抵免資格考,這對學生來說才是數位典藏系統的「加值應用」啊。

關於DSpace-DLLL的細節,我在書本中已經有詳細地說明。目前書本正在二校中,博士班筆試結束之後,我也比較能夠專心投入這塊。希望能儘快出版呢。


回應項潔館長的問題

在議程結束前的發問期間,項潔館長質疑我們做DSpace-DLLL是否有其必要性,應該是多跟臺大圖書館合作,這樣才是真正的「不必自行重複開發」。王梅玲老師認為小型團體仍有建置獨立數位典藏系統的需求,但項館長則反駁說,那應該是去購買雲端服務即可。而會議議程時間不夠,實際上的主講者陳老師卻沒有參與這個討論,就結束了這段議程。

說實話,我不是很清楚跟臺大合作的方式是如何。感覺上應該是類似企業外包委託案那種方式進行吧?如果真的要做大的話,臺大圖書館也的確可以用雲端運作模式來提供合作服務也說不定。

DSpace-DLLL的「不必重複建置數位典藏系統」,是真的只是在講這個技術平台。在數位典藏計畫裡面,技術平台只是一個環節,無法取代委託整個數位典藏計畫的作法。不過即使如此,對於很多人來說還是很有意義。舉例來說,教師在教數位典藏課程時,就能夠讓學生真的有一個系統平台可以去架設、管理、自行建置,讓他們真的體驗到「如何規劃、建置一個數位典藏計畫」的經驗。這種價值在政大圖檔所中也時常體現,我也從老師們教學使用上的回饋來修正DSpace-DLLL的缺點。另一方面,DSpace-DLLL可以成為想要獨自架設數位典藏系統的單位有一個比原始DSpace更好上手的起點。

尤其是後者,對我來說有著更深遠的含義。DSpace-DLLL是以開放原始碼的模式分享技術,我們站在DSpace這個巨人的肩膀上,往理想的方向踏出了一步,然後再成為其他人的基石,幫助他人更進一步。就像DSpace仍有許多缺點,而我們改善之後以DSpace-DLLL釋出一樣,未來也希望會有人再改進DSpace-DLLL,而釋出更好的開放原始碼數位典藏系統。

以一個系統開發者的角度來說,讓技術在開放原始碼的環境下自由地成長,這是很有意義的貢獻。


未來的方向

作為DSpace-DLLL的開發者,先不論老師在接手之後怎麼繼續改進,我必須補充一下老師在影片中尚未提及的幾個注意事項。

大前提是,這不是企業化、大團隊製作的成品,因此請勿期待不太會成為完美的方案,只能成為備選方案之一。

虛擬機器的限制
image

DSpace-DLLL封裝在虛擬機器VirtualBox進行安裝。我寫了一個Windows XP的安裝精靈,方便Windowx XP使用者來架設。而VirtualBox是跨平台的虛擬機器,因此熟悉的使用者也可以拿著硬碟檔架設在Linux平台上。

使用虛擬機器封裝的理由很單純:安裝簡易。DSpace的原始安裝方式是以Java Servlet來進行配置,但是技術難度高,而太多外部依賴元件需要額外下載。在DSpace-DLLL中加入的多媒體轉檔功能,也需要依賴非DSpace原始檔中的OpenOfficeFFmpegMEncoder套件。為了避免大家光是安裝就弄昏了頭,所以我把它通通打包在虛擬機器中,方便轉移與應用。這種概念可是雲端模式的基礎呢。

虛擬機器的意思是在電腦上再模擬一個電腦,對於效能不佳的電腦來說,虛擬機器的負荷很大,因此穩定度跟服務負荷量有待評估。

理論上,若將DSpace-DLLL裡面的系統移植到實體電腦上,應該是會有比較好的運作效能。不過各元件是否能夠順利搭配這點我就不敢保證,Linux最為人詬病的就是套件相依性太高,要完美地安裝一個系統,我一直都沒有確切的把握。

大量資料的處理

繼承前一點,虛擬機器的DSpace-DLLL在大量資料的存取下是否能夠正常運作,這是需要評估的。除此之外,在系統功能上也還有許多需要改進的地方。

對於數位典藏工作人員來說,最需要的就是批次匯入機制。DSpace有提供後台的匯入,但是批次檔的撰寫對一般人來說並不友善。同樣的,對於大量資料的修改、調整,DSpace也沒有批次修改的功能。因此在大量資料的管理上,還有很多的不便。

這是DSpace尚未達到的境界,而DSpace-DLLL也繼承下來,一樣有這種問題。

但是這並不是做不到:DSpace的批次匯入其實真的很好用,只是不太友善。有心要修改的人,應該是能夠輕易地將他實作成前臺網頁可以直接執行的功能吧。

權限控管與付費機制

這是非常重要的一個未解決問題,如果你的計畫想要以DSpace-DLLL作為起點,那麼權限是不得不考量的一個議題。

DSpace提供以帳號來控管權限功能,可以控制內容層級每一層的新增、遞交、編輯、刪除權限,控管上非常詳盡。但是就跟大量資料管理缺乏批次處理功能一樣,大量的權限設定也就變成一個難題。另一個問題,就是權限控管的需求不只有帳號這一種方式,常見的還有IP、國家等限制,而DSpace還沒考量到這點。

DSpace已經有良好的權限控管基礎,但仍需要有人進一步地改良,這也是DSpace-DLLL未解決的問題。

至於付費機制上,原本是為公開取用的DSpace就離這個功能更是八竿子打不著的關係,請營利公司自行加油吧。

分類與介面
image

作為一個泛用型典藏系統,DSpace的預設範本很樸實。我在DSpace-DLLL中修改了一些小細節,但是並沒有去調整他的設計,因此介面依然是很單調。

今天研討會時王老師提問說能不能更簡單地調整網頁的樣式,並且建立漂亮的分類架構。這也是她長久以來的一個疑惑。

用簡單的方式來修改網頁介面,例如拖曳、新增網頁元素、套用簡易範本,這在大多數CMS當中都已經是非常成熟的技術,不過DSpace在介面調整支援上,還不能簡單地使用。提示是XMLUI,但至今我仍未參透這個功能就是。

至於分類架構上,這可是一塊非常、非常、非常大的研究議題了。原本作為機構典藏之用的DSpace,將內容層級架構分成community、collection、item等層級,許多國外數位典藏系統也都是依照此架構在使用。但是數位典藏不像檔案管理有一套全宗原則,往往還是以客製化的方式設計分類架構為多。

這個客製化的要求並不是說DSpace的內容層級架構不能實作出來,事實上,像林巧敏老師負責的教育部中綱計畫成果資訊網就是一個很適合的例子。不過系統技術人員眼中的分類架構跟數位典藏人員來說有很大的差別。對於數位典藏人員來說,他們更在意的是對於每一種分類導覽功能的客製化。

image

以中綱計畫成果資訊網來說,在一個子計畫瀏覽中又可以依照藏品的類型來分類。若以DSpace實作,一個子計畫則是一個community,每一種藏品的類型是各自的collection,而底下每一筆資料則就是一筆item。那麼DSpace在介面呈現上能不能做到像中綱計畫成果資訊網一樣呈現?作為技術人員的我一定會跟你說:可以,但是要自己改。而這對許多不懂技術的人來說來說,其意義就與DSpace無法做到這種分類架構是同樣的答案。

總而言之,到頭來還是介面上的問題。而且這對泛用型典藏系統的DSpace來說,都已經快要是非戰之罪了。

然而,我認為DSpace在介面功能上的強大,對技術人員來說會有直接的感受。受惠於DSpace嚴謹地設計模式、詳盡的註解說明,技術人員可以輕易地利用DSpace的內容層級架構、瀏覽、搜尋功能做出各種客製化的分類架構,就像全國通識網課程資料庫一樣。我看過一些商用軟體的程式碼都還沒DSpace漂亮,這是真的。DSpace的程式非常嚴謹且靈活,不僅是容易使用,也很值得學習。


全文檢索的中文斷詞改善

在全文檢索的功能中,DSpace會依據檔案內文製作倒置索引檔,以此來達到全文檢索的功能。然而MIT與HP為西方人設計的DSpace並沒有很完美地考慮到各國的國情,中文斷詞只用二字詞的方式來切截。這會影響搜尋結果並不是十分漂亮。

中文斷詞全文檢索是一塊門檻不低的議題,DSpace-DLLL也沒有解決這個議題,但是未來仍可以從DSpace提供的plugin機制來進行修改,讓中文斷詞更為漂亮。


PDF的HTML網頁化

儘管現在PDF已經如此盛行,但我們真正要使用的時候,還是會希望他是以HTML網頁的方式呈現。只有網頁,才能讓我們能夠為他加上更多的服務,例如標註功能。

其實這幾乎脫離了DSpace原本的要求,但這也的確是我想做的一個功能。未來有機會的話再來挑戰看看吧。



DSpace-DLLL與我

如果有人欣賞DSpace-DLLL,那我會感到非常欣慰,辛苦努力總算有了代價。而DSpace-DLLL會不會像上述的未來發展一樣繼續改進,我希望會,但我也希望會有人來接手。至於我自己,也希望能有這個機緣能夠繼續改進。

機緣,那是很重要的條件。那就是有沒有繼續做下去的價值。也許很多人會認為「有!」,但那是站在使用者的立場來說的。作為開發者的我來說,有沒有價值,那又是另外一回事。


興趣來做的DSpace-DLLL,是以延畢換來的

我在政大圖檔所念了快要四年才完成學業,不瞞大家說,因為我都在玩──最後玩出這個DSpace-DLLL。

在幫忙百年圖書館史、教育部計畫、甚至是寫書的時候,儘管老師並沒有要求很多,但我卻仍會任性地挑戰自我。於是後設資料編輯功能出來了、虛擬機器出來了、更簡單的系統管理方式出來了、一本介紹DSpace的專書也快要出來了。

這並不是為了錢、也不是為了名聲、也不是為了他人(包括老師或是計畫助理),而是為了自己的興趣。那種「想看看跨越山丘之後的景色」是我持續前進的動力,這也往往讓我沉迷在此、樂在其中而不可自拔。

儘管學習的過程讓我感到開心,但我也知道,這其實並不是什麼值得驕傲的事情。當我每天在實驗室修改程式碼的時候,有些老師則是直接對我嘆氣:「唉,你怎麼都在搞這個,幹嘛還不畢業。」

我大概花了將近兩年的時間專搞DSpace,論文幾乎是寫完計劃書之後就放置不理了。每次陳老師想拿我來當學弟妹借鏡的時候,我內心其實都會為這種不顧學生畢業本分而玩樂於DSpace的行為感到汗顏。

在金錢上,因為幫忙計畫事宜,所以當時老師給的人事費仍可以補足日常生活所需。儘管如此,每學期的學雜住宿費仍得仰賴父母的支援。這對一個當時的年紀是應該在外面工作、養活自己的人來說,我這種為了興趣不顧一切的行為並不可取。


只是為了自我成就

DSpace-DLLL的發展並不像是國科會、教育部計畫一樣,是有實質的經費支援。它也不是我的畢業論文,對我的畢業也沒有實質的助力。這種基礎的系統建設並沒有太多學術上值得一提的研究價值,這也不是我想做的研究題目。

它就真的,只是我的興趣,只是一種自我成就,是幫忙計畫專案之後,學習了Java Servlet、AJAX、Linux、虛擬機器、自動安裝之後的一個成果產出與分享,就只是如此。


不能一直玩下去

人不去面對現實也要有個限度,至少這點我還是知道的。為自己設定一個折返點,做到一個程度就該收手,不能一輩子這樣玩下去。我想,這才是一個社會人應有的常識。

我玩得很開心,但是差不多了。

儘管如此,不會對DSpace-DLLL感到滿足的人總是會期望我或實驗室能夠繼續開發下去。我也希望如此,不過我更希望的是看到有人能接手,基於現在的基礎上,繼續深入開發。所以我寫書,我釋出系統,這是我的願望。

至於我自己能不能繼續做,那麼就要回歸現實問題,有沒有經費支援?對於我未來學業、工作上有沒有幫助?有多少經費做多少事情,這才是一種正常的運作模式。在沒有經費、憑著熱情來開發、供人自由取用的火柴人行為,這是可以拿來自己說笑的故事,但卻不是作為要求別人的條件。

所以誰誰誰快來聘我去工作吧,我就能名正言順地繼續玩DSpace了!!


結語

原本只是想記錄一下DSpace-DLLL的介紹投影片,沒想到越寫越多,就變成這樣落落長的一篇了。原本是打算花一個小時來寫,不過實際上似乎是寫了快三個小的樣子。都已經半夜兩點了,好睏。

那麼接下來就是努力讓DSpace-DLLL可以順利出版啦,加油~

(more...)

專書「DSpace開放源碼數位典藏系統建置理論與實務」出版了 / Developing an Open Source Digital Archive With DSpace: Theory and Practice

布丁布丁吃布丁

專書「DSpace開放源碼數位典藏系統建置理論與實務」出版了 / Developing an Open Source Digital Archive With DSpace: Theory and Practice

20140803-pudding-1-0001_結果

最近終於把以Dspace-DLLL建置數位典藏的專書「DSpace開放源碼數位典藏系統建置理論與實務」完成了。本書不僅可以作為教科書、計畫建置的指導手冊,書中更包含一個可以直接安裝後使用的DSpace-DLLL系統。以下介紹本書的基本資料,作為書本的簡單行銷宣傳。

Recently we had published our book “DSpace開放源碼數位典藏系統建置理論與實務” (Developing an Open Source Digital Archive With DSpace: Theory and Practice). Following are the introduction and table of contents of this book.


書目 / Bibliography

20140803-pudding-1-0001_結果20140803-pudding-1-0002_結果

DSpace開放源碼數位典藏系統建置理論與實務

  • 作者:陳志銘、陳勇汀(布丁布丁吃布丁)
  • 出版社:文華圖書館管理
  • 出版日期:2014/05
  • 語言:繁體中文
  • 附件:1片DVD,內含DSpace-DLLL系統
  • ISBN: 978-986-6182-12-9
  • 定價:850元整

APA引用格式為:

陳志銘、陳勇汀(2014)。DSpace開放源碼數位典藏系統建置理論與實務。臺北市:文華圖書館管理。

書本導讀 / Introduction

本書提供有意建立數位典藏的相關專業人員建置系統的起步,也是為了數位內容、數位典藏學程、圖書資訊及資訊傳播等相關科系進行數位典藏實作教學目的而撰寫。數位典藏發展至今,已有許多以開放源碼釋出的數位典藏自由軟體可供利用,本書所介紹修改自 DSpace 的 DSpace-DLLL 即為其中一種。但是數位典藏系統的建置背後涵蓋著相當廣泛的專業知識,非常需要有一本專業的書籍作為入門學習的基礎。

20140803-pudding-1-0003_結果

為了引導讀者全面性的掌握數位典藏專業知識,本書先揭櫫數位典藏相關理論,包括數位典藏專案規劃、網站資訊架構、後設資料觀念與設計、電子資源管理、電子資源授權與權限控管,讓讀者具備使用 DSpace-DLLL 數位典藏系統的基礎觀念;其次介紹 DSpace-DLLL 系統的安裝、使用與修改,讓讀者能夠以本書所提供的 DSpace-DLLL 系統為基礎,以小幅度修改程式碼方式自行發展成具有特色的數位典藏系統;最後介紹以修改 DSpace 數位典藏系統開發的各類型數位典藏建置成果,以實際的例子說明各類型數位典藏的建置過程。

基於上述考量,本書內容主要分為三個部分,分別是數位典藏導論、DSpace 數位典藏系統實作以及DSpace 數位典藏建置案例介紹,再依據這三個部分區分為若干章節進行詳細介紹,全書共計十三個章節。作者希望透過本書的介紹,能提升讀者以開放源碼系統建置數位典藏的專業能力。

DSC_0054 裁切

本書的附件光碟為作者自行開發的DSpace-DLLL系統,這是基於DSpace1.5 版本以及整合多個開放原始碼自由軟體而成(如表 B所示),且本書修改自 DSpace 的 DSpace-DLLL 系統也以柏克萊軟體套件授權條款(Berkeley Sof-tware Distribution,簡稱BSD)發行。讀者可以自行安裝、架設或修改DSpace-DLLL的原始碼而不須徵求本書作者的同意,我們由衷地期望讀者能夠利用本書的內容與 DSpace-DLLL 系統建立起屬於您自己的數位典藏系統。

目錄 / Table of Contents

本書分成三個部分,包括數位典藏導論、DSpace數位典藏系統實作與DSpace數位典藏系統建置案例。

第一部分 數位典藏導論
  • 第一章 數位典藏概論:介紹數位典藏的各種定義,釐清讀者對於數位典藏的概念,並引領讀者認識數位典藏的相關計畫。
  • 第二章 數位典藏專案規劃:介紹數位典藏發展需要的成員組合,引導讀者瞭解如何從後設資料規劃設計與系統分析,一步一步地規劃建置數位典藏專案的執行細節。
  • 第三章 網站資訊架構:介紹數位典藏系統網站的組織架構原則,包括組織系統、標籤命名系統、導覽系統、搜尋系統、後設資料與控制詞彙等觀念。
  • 第四章 後設資料設計:說明後設資料的原理以及重要性,並介紹數位典藏常用的後設資料規格與參考資源。
  • 第五章 電子資源授權與權限控管:介紹電子資源的授權方式、各種不同的授權條款以及數位典藏系統權限控管的規劃與建議。
第二部分 DSpace 數位典藏系統實作
  • 第六章 DSpace 介紹:介紹 DSpace 的發展背景,帶領讀者瞭解 DSpace 各種功能特色與網頁使用介面,並介紹以 DSpace 發展完成的相關數位典藏應用。
    第七章 DSpace-DLLL安裝與設定:詳細介紹 DSpace-DLLL 安裝與設定程序,讓讀者能夠利用自己的電腦架設 DSpace-DLLL 數位典藏系統。
  • 第八章 資料內容組織架構:介紹 DSpcae 的資料內容組織架構,針對社群(community)、類別(collection)、文件(item)、檔案集(bundle)到檔案(bitstream)各層級與管理進行介紹,讓讀者學習如何制訂典藏的數位內容。
  • 第九章 帳戶、群組與權限設定:介紹如何建立 DSpace 的使用者、群組,以及權限控管的設定。
  • 第十章 遞交作業與工作流程:介紹 DSpace-DLLL 中設計與建立數位典藏的遞交作業與工作流程,其中遞交作業涉及後設資料規範的設定。
  • 第十一章 系統架構與版面修改:剖析 DSpace 的系統架構,並說明如何閱讀 DSpace 系統採用的 Java 程式碼,然後教導讀者修改網頁使用介面。
第三部分 DSpace 數位典藏建置案例
  • 第十二章 臺灣百年圖書館史數位圖書館先導計畫:介紹由政治大學圖書資訊與檔案學研究所依據修改DSpace所發展的「臺灣百年圖書館史數位圖書館」。詳細地描述數位典藏專案的建置程序,以供讀者作為規劃的參考。
  • 第十三章 教育部臺灣通識網課程資料庫:介紹「教育部臺灣通識網」建置的開放式通識課程資料庫成果。臺灣通識網課程資料庫包含典藏完整的優質課程資料庫與集合國內各大專院校通識課程資訊的通識課程基本資料庫。

出版現況 / Publication Status

image

目前「DSpace開放源碼數位典藏系統建置理論與實務」這本書已經在國家圖書館的全國新書資訊網登錄,但這不是銷售平臺,只是為新書記錄而已。

由於本書採用POD (Print On Demand,按需求印製)的方式印製,印量並不大,目前尚未在各通路上架。如果要購買的話,請直接跟文華圖書出版公司聯絡:

印量少加上還有一片DVD的情況下,本書不僅較難在傳統通路上架,價格也比一般書籍昂貴許多。我試著跟文華出版談談看能不能在Google Play等電子書平臺上架,讓想要的讀者更容易拿到本書。

(more...)

DSpace新增input-type——安裝篇

布丁布丁吃布丁

DSpace新增input-type——安裝篇

DSpace新增input-type目錄

  1. 安裝篇
  2. TextEditor篇
  3. FileUpload篇
  4. XMLMetadata篇

目前的DSpace 1.4.2~1.5雖然提供了很多input-type,像onebox(單欄)、textarea(多行)、dropdown(下拉式),但是使用起來還是覺得不夠,尤其是資料儲存無法像樹狀結構一樣靈活,在設計Metadata的時候更是一個很大的限制(雖然很多架設DSpace的人都沒有更動過input-forms的樣子)。

為了改進DSpace的不足,我以DSpace 1.5版開發出以下三種輸入表單型態:

texteditor

把原本的textarea以FCKeditor取代,並設定自動隱藏工具列。最後輸出資料則是html程式碼。

image

fileupload

在edit-metadata的步驟中,透過AJAX技術直接使用DSpace上傳步驟的功能。

上傳前:image

上傳後:image

xmlmetadata

可允許樹狀結構的metadata,並允許特定節點重複,可更靈活地儲存資料。一樣具備onebox、dropdown、textarea、texteditor、fileupload等多種輸入類型。

image

此篇介紹如何修改DSpace 1.5的各個檔案,以呈現出以上的功能,使用與設定則留到其他篇再講吧!


安裝環境與路徑縮寫說明

DSpace 1.5版本跟之前的1.4.2版本各程式存放的路徑上有很大的差別,此處我以1.5版為主,安裝在CentOS 5 Final上,JSP伺服器是Apache Tomcat 6.0.16

以下說明各路徑的簡寫:

[dspace-source]:DSpace原始檔的位置,通常位於「/opt/dspace-1.5.0-src-release/」

[dspace-jspui-webapp]:JSPUI原始碼路徑,通常位於「[dspace-source]/dspace-jspui/dspace-jspui-webapp/src/main/webapp」之中。根據文件說明,修改之後存放的檔案請放到「[dspace-source]/dspace/modules/jspui/src/main/webapp/」之中。

[dspace-jspui-api]:JSPUI的API,通常位於「/opt/dspace-1.5.0-src-release/dspace-jspui/dspace-jspui-api/src/main/java/org/dspace/app/webui/」。

[dspace-api]:DSpace的API,許多核心程式都擺在這邊,通常位於「[dspace-source]/dspace-api/src/main/java/org/dspace/」

[dspace]:DSpace安裝完成之後的目的地,包括input-forms.xml設定檔都要在這邊修改。預設的位置是「/dspace/」。

[tomcat]:JSP伺服器的安裝位置,通常是在「/opt/apache-tomcat-6.0.16/」裡面。

那麼以下就是安裝與修改的步驟囉!我會列出「路徑與檔名」、「我的程式碼下載」、「程式碼說明」以方便大家修改或安裝。因為細節繁雜,所以有可能有所遺漏,請在下方的回應欄盡量批評吧!


[dspace-jspui-webapp]/extension/...... (extension.20081018.zip)

這是一個新開啟的資料夾,裡面擺放我撰寫的JavaScript、CSS程式,請下載附件extension.zip,並擺放到該路徑去。裡面又分成幾個部份:(以下檔案所指的根目錄就是「[dspace-jspui-webapp]/extension/」,之後其他敘述也同這個道理)

/jquery.js:JavaScript的函式庫jQuery,在此使用的是1.2.1版本,讓JavaScript程式撰寫起來輕鬆很多,我撰寫的JavaScript都會用到jQuery。

/fckeditor/FCKeditor 2.6.3版本,一種JavaScript的網頁內嵌所見即得編輯器,可用類似Word的操作介面、建立HTML程式碼。用於input-type: texteditor,可改善DSpace 1.5只能顯示單調文字的缺點。

/fckeditor/fckeditor_display_toggle.js:用來自動隱藏/顯示工具列的函式。

/ajaxfileupload/ajaxfileupload.js:jQuery的Plugin,我改寫成適合DSpace檔案上傳的版本。

/ajaxfileupload/ajaxfileupload_fn.js:供input-type: fileupload呼叫ajaxfileupload.js的函式,裡面可設定fileupload的參數,但照以下安裝步驟的話是不需要修改此設定的。

/ajaxfileupload/ajaxfileupload_fnXML.js:給XMLMetadata呼叫ajaxfileupload.js的函式。

/xmlmetadata/dspace-inputtype-xml.js:XMLMetadata主要的JavaScript函式程式碼。

/xmlmetadata/dspace-inputtype-xml.css:XMLMetadata在呈現表單跟表格時的樣式表。

/xmlmetadata/ui.datepicker.js:jQuery的UI:Datepicker,可以方便挑選日期,用於XMLMetdata的input-type: date,我想這應該比DSpace原本的date好用很多。

/xmlmetadata/flora.datepicker.css:Datepicker的樣式表。

/display-item/metadata-value-display.js:在Display Item頁面時,針對fileupload跟xmlmetadata兩種資料調整呈現的樣貌。

這些程式只用短短的半個月時間寫完,也還沒做嚴格的測試,如果之後有改良的版本,會直接在此更新,並加上日期版本。


[dspace-jspui-webapp]/submit/...... (submit.20081015.zip)

這邊我修改了在進行上傳步驟的程式碼,以下一一說明。

/edit-metadata.jsp

image

DSpace描述階段的程式碼,為了能夠新增這三種input-type,在這一頁下了很多功夫。以下是修改細項:

修改1:引用所需的JavaScript跟CSS檔案。 請在<dspace>標籤開始之後插入這些引用吧。(紅色部份是新增的程式碼)

<dspace:layout locbar="off" navbar="off" titlekey="jsp.submit.edit-metadata.title">

<script type="text/javascript" src="<%= request.getContextPath() %>/extension/jquery.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/fckeditor/fckeditor.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/ajaxfileupload/ajaxfileupload.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/ajaxfileupload/ajaxfileupload_fn.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/ajaxfileupload/ajaxfileupload_fnXML.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/fckeditor/fckeditor_display_toggle.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/xmlmetadata/dspace-inputtype-xml.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/xmlmetadata/ui.datepicker.js"></script>
<link rel="stylesheet" href="<%= request.getContextPath() %>/extension/xmlmetadata/flora.datepicker.css" type="text/css" media="screen" title="Flora (Default)">
<link rel="stylesheet" href="<%= request.getContextPath() %>/extension/xmlmetadata/dspace-inputtype-xml.css" type="text/css" media="screen">

修改2:定義要丟給新增函式的變數。包括workspaceItem的ID(目前遞交作業的ID)、nonInternalBistreams的ID(哪些是公開的檔案)、hasMultipleFiles(是否允許多檔案上傳)、defaultValue(XMLMetadata的預設呈現樣貌)。請加在設定inputType、label跟closedVocabulary的後面吧。(紅色部份是新增的程式碼)



       String label = inputs[z].getLabel();
       boolean closedVocabulary = inputs[z].isClosedVocabulary();

>     //For fileupload
     int workspaceItemID = si.getSubmissionItem().getID();
     Bitstream[] bitstreams = si.getSubmissionItem().getItem().getNonInternalBitstreams();
     ArrayList nonInternalBistreamsID = new ArrayList();  //new Int(bitstreams);
     for (int i = 0; i < bitstreams.length; i++)
         nonInternalBistreamsID.add(bitstreams[i].getID());
     boolean hasMultipleFiles = si.getSubmissionItem().hasMultipleFiles();
    
     //For XMLMetadata & other
     String defaultValue = inputs[z].getDefaultValue();

修改3:新增input-type呼叫函式的判斷式。在下方有if else的判斷式,程式會依照input-type的值不同,呼叫不同的函式,那麼我們就把這三個新增的函式也加入判斷之中,新增在list跟onebox之間吧。(紅色部份是新增的程式碼)

       else if (inputType.equals("list"))
       {
          doList(out, item, fieldName, dcSchema, dcElement, dcQualifier,
               repeatable, inputs[z].getPairs(), label);
       }
     else if (inputType.equals("texteditor"))
       {
         doTextEditor(out, item, fieldName, dcSchema, dcElement, dcQualifier,
                repeatable, fieldCountIncr, label, pageContext, vocabulary,
               closedVocabulary, request.getContextPath() + "/extension/fckeditor/");
       }
     else if (inputType.equals("fileupload"))
       {
         doFileUpload(out, item, fieldName, dcSchema, dcElement, dcQualifier,
              repeatable, fieldCountIncr, label, pageContext, vocabulary,
              closedVocabulary, workspaceItemID, nonInternalBistreamsID,
           hasMultipleFiles);
       }
     else if (inputType.equals("xmlmetadata"))
       {
         doXMLMetadata(out, item, fieldName, dcSchema, dcElement, dcQualifier,
                repeatable, fieldCountIncr, label, pageContext, vocabulary,
               closedVocabulary, workspaceItemID,
            nonInternalBistreamsID, hasMultipleFiles, defaultValue);
       }

       else
       {
         doOneBox(out, item, fieldName, dcSchema, dcElement, dcQualifier,
              repeatable, fieldCountIncr, label, pageContext, vocabulary,
              closedVocabulary);
       }

修改4:宣告doTextEditor()、doFileUpload()跟doXMLMetadata()。請寫在doList結尾之後吧(紅色的部份是新增的程式碼):

          }//end doList

    void doTextEditor(javax.servlet.jsp.JspWriter out, Item item,
      String fieldName, String schema, String element, String qualifier, boolean repeatable,
      int fieldCountIncr, String label, PageContext pageContext, String vocabulary, boolean closedVocabulary, String sBasePath)
      throws java.io.IOException
    {

      DCValue[] defaults = item.getMetadata(schema, element, qualifier, Item.ANY);
      int fieldCount = defaults.length + fieldCountIncr;
      StringBuffer sb = new StringBuffer();
      String val;

      if (fieldCount == 0)
         fieldCount = 1;

      for (int i = 0; i < fieldCount; i++)
      {
   if (i == 0)
      sb.append("<tr><td class=\"submitFormLabel\">")
        .append(label)
        .append("</td>");
   else
      sb.append("<tr><td> </td>");

         if (i < defaults.length)
           val = defaults[i].value;
         else
           val = "";

         sb.append("<td colspan=\"2\">");

         String textareaName = fieldName;
         if (repeatable && i>0)
            textareaName = textareaName + "_" + i;

         sb.append("<textarea name=\"")
           .append(fieldName);
         if (repeatable && i>0)
           sb.append("_").append(i);
         sb.append("\" rows=\"4\" cols=\"45\"")
           .append(hasVocabulary(vocabulary)&&closedVocabulary?" readonly=\"readonly\" ":"")
           .append("   >")
           .append(val)
     .append("</textarea>")
     .append(doControlledVocabulary(fieldName + (repeatable?"_" + i:""), pageContext, vocabulary));
      

       //FCKeditor ReplaceTextarea
      
       sb.append("<script type=\"text/javascript\">\n")
         .append("  var oFCKeditor = new FCKeditor( \"")
           .append(fieldName);
         if (repeatable && i>0)
           sb.append("_").append(i);
         sb.append("\" ) ;\n")
       .append("  oFCKeditor.Config[\"ToolbarStartExpanded\"] = false ;\n")
         .append("  oFCKeditor.BasePath  = \""+sBasePath+"\" ;\n")
         .append("  oFCKeditor.ReplaceTextarea() ;\n")
         .append("</script>\n");

     sb.append("</td>\n");

   if (repeatable && i < defaults.length)
   {
      // put a remove button next to filled in values
      sb.append("<td><input type=\"submit\" name=\"submit_")
        .append(fieldName)
        .append("_remove_")
        .append(i)
//        .append("\" value=\"Remove This Entry\"/> </td></tr>");
        .append("\" value=\"")
           .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.remove"))
           .append("\"/> </td></tr>");
   }
   else if (repeatable && i == fieldCount - 1)
   {
      // put a 'more' button next to the last space
      sb.append("<td><input type=\"submit\" name=\"submit_")
        .append(fieldName)
//        .append("_add\" value=\"Add More\"/> </td></tr>");
        .append("_add\" value=\"")
        .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.add"))
        .append("\"/> </td></tr>");
   }
   else
   {
      // put a blank if nothing else
      sb.append("<td> </td></tr>");
   }
      }

      out.write(sb.toString());
    }  //void doTextEditor()

    void doFileUpload(javax.servlet.jsp.JspWriter out, Item item,
      String fieldName, String schema, String element, String qualifier, boolean repeatable,
      int fieldCountIncr, String label, PageContext pageContext, String vocabulary, boolean closedVocabulary,
    int workspaceItemID, ArrayList nonInternalBistreamsID, boolean hasMultipleFiles)
      throws java.io.IOException
    {

      DCValue[] defaults = item.getMetadata(schema, element, qualifier, Item.ANY);
    fieldCountIncr = 1;
      int fieldCount = defaults.length + fieldCountIncr;
      StringBuffer sb = new StringBuffer();
      String val;
    
      if (fieldCount == 0)
         fieldCount = 1;

      for (int i = 0; i < fieldCount; i++)
      {
   if (i == 0)
      sb.append("<tr><td class=\"submitFormLabel\">")
        .append(label)
        .append("</td>");
   else
      sb.append("<tr><td> </td>");

         if (i < defaults.length)
           val = defaults[i].value.replaceAll("\"", """);
         else
           val = "";
      
    if (!val.equals(""))
    {
      String[] tempID = val.split("/");
      if (tempID.length > 2)
      {
        //String id = tempID[(tempID.length - 2)];
      int id = Integer.parseInt(tempID[(tempID.length - 2)]);
      
      int index = nonInternalBistreamsID.indexOf(id);
      if (index == -1)  continue;
      }
    }
      
         String inputName = fieldName;
         if (repeatable && i>0)
           inputName = inputName + "_" + i;


       sb.append("<td colspan=\"2\">");

    if (hasMultipleFiles == true)
    {
    sb.append("\n<input type=\"text\" id=\""+inputName+"\" name=\""+inputName+"\" value=\""+val+"\" style=\"display:none\"   />\n")
      .append("  <input type=\"file\" onchange=\"jQuery(this).nextAll('button.fileupload-do:first').click()\" />\n")
      .append("  <span></span>\n")
      .append("  <button class=\"fileupload-do\" onclick=\"return ajaxFileUpload(this, "+workspaceItemID+", '"+label+"');\" type=\"button\">Upload</button>\n");
      
       if (!val.equals(""))
       {
      sb.append("  <script type=\"text/javascript\">")
        .append("  ajaxFileUploadExist(\""+inputName+"\");")
        .append("</script>");
       }
    }  //if (hasMultipleFiles == true)
    else
    {
      sb.append("Please check \""+LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.initial-questions.elem3")+"\"! \n");
    }
       sb.append("</td>\n");
    

   if (repeatable && i < defaults.length)
   {
      // put a remove button next to filled in values
      sb.append("<td><input type=\"submit\" name=\"submit_")
        .append(fieldName)
        .append("_remove_")
        .append(i)
//        .append("\" value=\"Remove This Entry\"/> </td></tr>");
        .append("\" value=\"")
           .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.remove"))
           .append("\" style=\"display:none\" ")
      .append("/>")
      .append("<button type=\"button\" onclick=\"ajaxFileUploadRemove(this, "+workspaceItemID+")\">"+LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.remove")+"</button>")
      .append(" </td></tr>");
   }
   else if (repeatable && i == fieldCount - 1)
   {
      // put a 'more' button next to the last space
      sb.append("<td><input type=\"submit\" name=\"submit_")
        .append(fieldName)
//        .append("_add\" value=\"Add More\"/> </td></tr>");
        .append("_add\" value=\"")
        .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.add"))
        .append("\"/> </td></tr>");
   }
   else
   {
      // put a blank if nothing else
      sb.append("<td>")
      .append("  <button class=\"fileupload-cancel\" onclick=\"return ajaxFileUploadCancel(this, "+workspaceItemID+")\" style=\"display:none\" type=\"button\">"+LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.remove")+"</button>\n")
      .append(" </td></tr>");
   }
      }

      out.write(sb.toString());
    }  //end of doFileUpload

    void doXMLMetadata(javax.servlet.jsp.JspWriter out, Item item,
      String fieldName, String schema, String element, String qualifier, boolean repeatable,
      int fieldCountIncr, String label, PageContext pageContext, String vocabulary, boolean closedVocabulary,
    int workspaceItemID, ArrayList nonInternalBistreamsID, boolean hasMultipleFiles, String defaultValue)
      throws java.io.IOException
    {

      DCValue[] defaults = item.getMetadata(schema, element, qualifier, Item.ANY);
      int fieldCount = defaults.length + fieldCountIncr;
      StringBuffer sb = new StringBuffer();
      String val;

      if (fieldCount == 0)
         fieldCount = 1;

      for (int i = 0; i < fieldCount; i++)
      {
   if (i == 0)
      sb.append("<tr><td class=\"submitFormLabel\">")
        .append(label)
        .append("</td>");
   else
      sb.append("<tr><td> </td>");

         if (i < defaults.length)
           val = defaults[i].value;
         else if (defaultValue != null)
           val = defaultValue;
     else
       val = "";
    
         sb.append("<td colspan=\"2\">");

         String textareaName = fieldName;
         if (repeatable && i>0)
            textareaName = textareaName + "_" + i;

         sb.append("<textarea id=\""+textareaName+"\" name=\"")
           .append(fieldName);
         if (repeatable && i>0)
           sb.append("_").append(i);
         sb.append("\" rows=\"4\" cols=\"45\"")
           .append(hasVocabulary(vocabulary)&&closedVocabulary?" readonly=\"readonly\" ":"")
           .append("   >")
           .append(val)
     .append("</textarea>")
     .append(doControlledVocabulary(fieldName + (repeatable?"_" + i:""), pageContext, vocabulary));
      

       //XMLMetadata Form Creator
       if (!val.equals(""))
     {
       sb.append("\n\n<script type=\"text/javascript\">\n")
         .append("var xm = new XMLMetadata(\""+textareaName+"\");\n")
         .append("xm.workspaceItemID = "+workspaceItemID+";\n")
         .append("xm.fieldTitle = '"+label+"';\n");
       if (hasMultipleFiles == false)
       {
         sb.append("xm.hasMultipleFiles = false;\n");
        sb.append("xm.langNotHasMultipleFiles = \"Please check \'"+LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.initial-questions.elem3")+"\'!\";\n");
       }
      
       if (nonInternalBistreamsID.size() != 0)
       {
         String nIBIDary = "[";
         for (int b = 0; b < nonInternalBistreamsID.size(); b++)
         {
          if (b > 0)
            nIBIDary = nIBIDary + ",";
          nIBIDary = nIBIDary + nonInternalBistreamsID.get(b);
         }
         nIBIDary = nIBIDary + "]";
         sb.append("xm.nonInternalBistreamsID = "+nIBIDary+";\n");
       }
       sb.append("xm.createRootForm();\n");
       sb.append("</script>\n");
      }

     sb.append("</td>\n");

   if (repeatable && i < defaults.length)
   {
      // put a remove button next to filled in values
      sb.append("<td><input type=\"submit\" name=\"submit_")
        .append(fieldName)
        .append("_remove_")
        .append(i)
//        .append("\" value=\"Remove This Entry\"/> </td></tr>");
        .append("\" value=\"")
           .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.remove"))
           .append("\"/> </td></tr>");
   }
   else if (repeatable && i == fieldCount - 1)
   {
      // put a 'more' button next to the last space
      sb.append("<td><input type=\"submit\" name=\"submit_")
        .append(fieldName)
//        .append("_add\" value=\"Add More\"/> </td></tr>");
        .append("_add\" value=\"")
        .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.button.add"))
        .append("\"/> </td></tr>");
   }
   else
   {
      // put a blank if nothing else
      sb.append("<td> </td></tr>");
   }
      }

      out.write(sb.toString());
    }  //void doXMLMetadata()

%>

doTextEditor()跟doXMLMetadata()是參考doTextArea()而來的,而doFileUpload()則是參考doOneBox()。

修改5:移除doPersonalName()、doSeriesNumber()跟doTwoBox()的寬度限制。不知為何這些函式都把表格寬度鎖死了,他們裡面都會擁有類似的程式碼,我舉doPersonalName()的設定為例:(紅色為刪除寬度限制的程式碼,附近的註解則是原本有限制寬度的程式碼)

      //Width hints used here to affect whole table
      headers.append("<tr><td> </td>") //.append("<tr><td width=\"40%\"> </td>")
             //.append("<td class=\"submitFormDateLabel\" width=\"5%\">")
       .append("<td class=\"submitFormDateLabel\">")
//             .append("Last name<br>e.g. <strong>Smith</strong></td>")
       .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.lastname"))
       .append("</td>")
             //.append("<td class=\"submitFormDateLabel\" width=\"5%\">")
      .append("<td class=\"submitFormDateLabel\">")
//             .append("First name(s) + \"Jr\"<br> e.g. <strong>Donald Jr</strong></td>")
       .append(LocaleSupport.getLocalizedMessage(pageContext, "jsp.submit.edit-metadata.firstname"))
       .append("</td>")
             //.append("<td width=\"40%\"> </td>")
      .append("<td> </td>")
             .append("</tr>");
      out.write(headers.toString());

修改6:修改欄位寬度配置。每一列輸入欄位中間都會間隔一個調整欄位寬度用的列,我們可以藉由調整這一列來控制欄位大小,但前提是要先移除掉上面那幾個函式裡面的寬度限制。(紅色部份是修改過後的值,表示左右各保留10%留白,中間則是有80%的寬度。)

<%-- HACK: Using this line to give the browser hints as to the widths of cells --%>
       <tr>
         <td width="10%"> </td>
         <td colspan="2" width="80%"> </td>
         <td width="10%"> </td>
       </tr>
/upload-file-list.jsp跟/get-file-format.jsp

image upload-file-list.jsp

image get-file-format.jsp

前者上傳檔案之後的列表、後者是當上傳檔案格式不明時會提示使用者選擇檔案格式的網頁。當這些網頁被讀取時,我想要轉換成以json格式呈現剛剛上傳檔案的資料(json是JavaSciprt常用的輕量級資料語言)。要讓upload-file-list.jsp跟get-file-format.jsp正常運作,必需要傳送以下參數:

  • workspace_item_id:遞交階段的ID,可由si.getSubmissionItem().getID()取得,這段程式碼寫在edit-metadata的修改2當中。
  • step:上傳檔案的階段,預設是「3」,以下教學會將之改成「2」。可在ajaxfileupload_fn.js裡面設定。
  • page:上傳檔案只會有1頁,輸入「1」即可。可在ajaxfileupload_fn.js裡面設定。
  • jsp:預設是由choose-file.jsp傳送過去,輸入值為「/submit/choose-file.jsp」即可。可在ajaxfileupload_fn.js裡面設定。
  • description:上傳檔案的敘述,可空白或不傳,我在fileupload的設定是會傳label的值。可在ajaxfileupload.js裡面設定。
  • submit_upload:要有值程式才知道是上傳的步驟,我則是固定填入「&gt;」。可在ajaxfileupload.js裡面設定。

修改:用網址設定呈現時轉址到顯示json的網頁。如果網址中包含「action=json」,那麼我就轉到另一個顯示json的網頁去。轉址的這段程式碼插入在<dspace>標籤前後,細節如下:(紅色部份為<dspace>標籤之外新增的程式碼)

  String action = (String) UIUtil.getOriginalURL(request);
if (action.indexOf("action=json") != -1)
{
   JSPManager.showJSP(request, response,
                    "/submit/upload-file-json.jsp");
}
else
{
%>

<dspace:layout locbar="off" navbar="off" titlekey="jsp.submit.get-file-format.title" nocache="true">

……

</dspace:layout>
<%
}  
%>
/upload-file-json.jsp

上面的修改之後會轉到這個網頁,這是我另外撰寫的程式。該程式會去取得最新上傳的檔案(專門指bitstream),回傳filename、url、format等參數。程式也很簡單,只有22行,原始碼如下:

<%@ page contentType="text/html; charset=utf-8" language="java" import="java.sql.*" errorPage="" %>
<%@ page import="org.dspace.core.Context" %>
<%@ page import="org.dspace.app.webui.servlet.SubmissionController" %>
<%@ page import="org.dspace.app.util.SubmissionInfo" %>
<%@ page import="org.dspace.content.Bitstream" %>
<%@ page import="org.dspace.app.webui.util.UIUtil" %>
<%
    // Obtain DSpace context
    Context context = UIUtil.obtainContext(request);    

  //get submission information object
    SubmissionInfo subInfo = SubmissionController.getSubmissionInfo(context, request);

Bitstream[] bitstreams = subInfo.getSubmissionItem().getItem().getNonInternalBitstreams();
Bitstream json_bitsream = bitstreams[0];  //bitstreams[(bitstreams.length-1)];  
%>
{
  error: '',
  filename: '<%= bitstreams[(bitstreams.length-1)].getName() %>',
  url: '<%= request.getContextPath() %>/retrieve/<%= bitstreams[(bitstreams.length-1)].getID() %>/<%= org.dspace.app.webui.util.UIUtil.encodeBitstreamName(bitstreams[(bitstreams.length-1)].getName()) %>',
  format: '<%= bitstreams[(bitstreams.length-1)].getFormatDescription() %>'
}

[dspace-jspui-webapp]/display-item.jsp (display-item.20081015.zip)

image

這是顯示item的頁面。我希望顯示的值能夠動態地調整成指定的樣式,而不是只有文字。像是XMLMetadata我希望能以樹狀結構呈現、fileupload則是呈現檔名。

修改1:引用JavaScript跟CSS檔案。作法同edit-metdata.jsp的修改1,程式碼如下:(紅色部份為新增的程式碼)

<dspace:layout title="<%= title %>">

<%
    if (handle != null)
    {
%>

<script type="text/javascript" src="<%= request.getContextPath() %>/extension/jquery.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/display-item/metadata-value-display.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/fckeditor/fckeditor.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/ajaxfileupload/ajaxfileupload.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/ajaxfileupload/ajaxfileupload_fn.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/ajaxfileupload/ajaxfileupload_fnXML.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/fckeditor/fckeditor_display_toggle.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/xmlmetadata/dspace-inputtype-xml.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/extension/xmlmetadata/ui.datepicker.js"></script>
<link rel="stylesheet" href="<%= request.getContextPath() %>/extension/xmlmetadata/flora.datepicker.css" type="text/css" media="screen" title="Flora (Default)">
<link rel="stylesheet" href="<%= request.getContextPath() %>/extension/xmlmetadata/dspace-inputtype-xml.css" type="text/css" media="screen">



    <table align="center" class="miscTable">

修改2:呼叫metadataValueDisplay()。全部網頁顯示之後的最後,呼叫metadataValueDisplay()以動態調整內容的顯示。程式碼如下:(紅色的部份為新增的程式碼)

<%
    }
%>
    <p class="submitFormHelp"><fmt:message key="jsp.display-item.copyright"/></p>
  <script type="text/javascript">
  metadataValueDisplay("<%= request.getContextPath() %>");
  </script>

</dspace:layout>

老實說,這並不是很好的作法。建議能從JSP顯示的時候就能呈現檔案或XML的樹狀結構,而不是等網頁讀出來再用JavaScript動態修改。但這部份程式得花另一番功夫了。


[dspace-api]/...... (dspace-api.20081015.zip)

API的三個檔案,不小心改錯的話很容易讓整個DSpace都毀掉喔。

/submit/step/DescribeStep.java

此程式會依照input-type來決定資料的儲存方式,由於我新增的三種input-type都是跟著onebox、textarea來跑的,所以就跟他們一樣的設定即可。程式碼如下:(紅色部份為新增的程式)

            else if ((inputType.equals("onebox"))
                    || (inputType.equals("twobox"))
                    || (inputType.equals("textarea"))
          || (inputType.equals("texteditor"))
          || (inputType.equals("xmlmetadata"))
          || (inputType.equals("fileupload")))

            {
                readText(request, item, schema, element, qualifier, inputs[j]
                        .getRepeatable(), "en");
            }
/app/util/DCInput.java

DSpace並沒有預設值的功能,item template文件樣板只是預設該collection底下的item會擁有固定的值,但這跟我們一般認知的預設值:「每次新增欄位時,某些值是被預先載入的」有段差異,尤其是XMLMetadata是將表單的形式預先寫入才能運作,因此我們要幫他新增取得default-value的功能。

修改1:宣告內用變數defaultValue:(紅色部份是新增的程式碼)

    /** is the entry closed to vocabulary terms? */
    private boolean closedVocabulary = false;
    
    /**  for XMLMetadata  */
    private String defaultValue = null;

修改2:用get取得裡面的資料:(紅色部份是新增的程式碼)

        vocabulary = (String) fieldMap.get("vocabulary");
        //for XMLMetadata
        defaultValue = (String) fieldMap.get("default-value");

        String closedVocabularyStr = (String) fieldMap.get("closedVocabulary");

修改3:宣告公用函式getDefaultValue():(紅色部份是新增的程式碼)

  public boolean isClosedVocabulary() {
    return closedVocabulary;
  }
  
  /** for XMLMetadata */
  public String getDefaultValue()
    {
        return defaultValue;
    }

/core/Utils.java

原本DSpace在display item的頁面中,如果你存的資料裡面有用到一些特殊字元的時候,會被轉換成顯示於HTML的脫逸字元(Escape characters,都會以「&」開頭、「;」結尾),以避免這些可能是HTML程式碼的字元影響版面呈現或程式運作。這種脫逸字元跟C、Java、PHP等會用反斜線「\」來脫逸的方式不同,是HTML獨特的編碼。

在DSpace當中,將會被脫逸掉的字元如下:

  • 「&」→「&amp;」
  • 「"」→「&quot;」
  • 「<」→「&lt;」
  • 「>」→「&gt;」」

※HTML的拖曳字元,可以參考網頁:HTML Escape Characters: Complete List

由於我們用到了由FCKeditor轉換而來的HTML,也會使用XMLMetdata來儲存、顯示資料,我們就不能讓他把這些字元脫逸。接著我們就是addEntities()的內容,程式碼如下:(紅色表示註解掉字元脫逸的程式)

    public static String addEntities(String value)
    {
        if (value==null || value.length() == 0)
            return value;
        
        //value = value.replaceAll("&", "&");
        //value = value.replaceAll("\"", """);

        // actually, &apos; is an XML entity, not in HTML.
        // that's why it's commented out.
        // value = value.replaceAll("'", "&apos;");
        //value = value.replaceAll("<", "<");
        //value = value.replaceAll(">", ">");


        return value;
    }

由於存在同名字檔案在/app/util/Utils.java,千萬要小心不要覆蓋錯囉!


[dspace]/config/...... (config.20081015.zip)

調整DSpace的設定。

/input-forms.dtd

此文件定義了input-forms.xml的格式,我們在此處加上default-value的宣告。

修改1:宣告field裡面的default-value是可選擇性的:(紅色部份為新增的程式碼)

<!ATTLIST page number NMTOKEN #REQUIRED>
<!ELEMENT field (dc-schema, dc-element, dc-qualifier?, repeatable?, label, input-type, hint, required?, vocabulary?, default-value?) >
<!ELEMENT dc-schema (#PCDATA) >

修改2:宣告default-value這個元素:(紅色部份為新增的程式碼)

<!ELEMENT form-value-pairs (value-pairs)* >
<!ELEMENT value-pairs (pair)+ >

<!ELEMENT default-value (#PCDATA) >
/item-submission.xml

此處調整敘述階段(describe)跟上傳階段(upload)的順序。原本的順序如下

  1. 取得開始的資訊:他會問你一些問題,像是是否要上傳多個檔案、是否擁有多個標題之類的,你要打勾來決定。
  2. 敘述資料:填寫由input-forms.xml規定好的表單。
  3. 上傳檔案:上傳bitstream檔案。DSpace的設計理念是前面的敘述階段在敘述這個檔案,例如論文的作者、出版資訊等等。預設此階段是一定上傳資料,不過可以在dspace.cfg裡面取消這個設定。
  4. 確認資料:網頁會列出你之前填寫及上傳的資訊,並讓你回頭去修正。
  5. 授權條款的確認

修改:敘述階段與上傳階段互換。如果要在敘述階段上傳檔案,即使我們在上傳階段並不上傳資料,還是必須先經過上傳階段才行。因此我們將敘述階段跟上傳階段調換過來,程式碼如下:(紅色部份為兩個互換的階段)

     <!--Step 1 will be to gather initial information-->
    <step>
      <heading>submit.progressbar.initial-questions</heading>
    <processing-class>org.dspace.submit.step.InitialQuestionsStep</processing-class>
    <jspui-binding>org.dspace.app.webui.submit.step.JSPInitialQuestionsStep</jspui-binding>         
    <xmlui-binding>org.dspace.app.xmlui.aspect.submission.submit.InitialQuestionsStep</xmlui-binding>
      <workflow-editable>true</workflow-editable>
    </step>  

      <!--Step 2 will be to Upload the item-->
      <step>
        <heading>submit.progressbar.upload</heading>
        <processing-class>org.dspace.submit.step.UploadStep</processing-class>
        <jspui-binding>org.dspace.app.webui.submit.step.JSPUploadStep</jspui-binding>
    <xmlui-binding>org.dspace.app.xmlui.aspect.submission.submit.UploadStep</xmlui-binding>
        <workflow-editable>true</workflow-editable>
      </step>
          
      <!--Step 3 will be to Describe the item.-->
      <step>
        <heading>submit.progressbar.describe</heading>
        <processing-class>org.dspace.submit.step.DescribeStep</processing-class>
        <jspui-binding>org.dspace.app.webui.submit.step.JSPDescribeStep</jspui-binding>
    <xmlui-binding>org.dspace.app.xmlui.aspect.submission.submit.DescribeStep</xmlui-binding>
        <workflow-editable>true</workflow-editable>
      </step>
   

    <!--Step 4 will be to Verify/Review everything -->
    <step>
        <heading>submit.progressbar.verify</heading>
        <processing-class>org.dspace.submit.step.VerifyStep</processing-class>
      <jspui-binding>org.dspace.app.webui.submit.step.JSPVerifyStep</jspui-binding>
    <xmlui-binding>org.dspace.app.xmlui.aspect.submission.submit.ReviewStep</xmlui-binding>
    <workflow-editable>true</workflow-editable>
      </step>
/dspace.cfg
此處有很多DSpace的設定,也包含了是否限制一定要上傳檔案的設定。修改的程式碼如下:(紅色部份為新增的設定) ##### Settings for Submission Process #####

# Should the submit UI block submissions marked as theses?
webui.submit.blocktheses = false

# Whether or not we REQUIRE that a file be uploaded
# during the 'Upload' step in the submission process
# Defaults to true; If set to 'false', submitter has option to skip upload
#webui.submit.upload.required = true
webui.submit.upload.required = false


DSpace操作步驟說明

以上修改完畢之後,還需要進行重新安裝的步驟。

DSpace 1.5版重新安裝:出處在說明文件DSpace System Documentation: Configuration and Customization ([dspace-source]/dspace/docs/configure.html#jspui,dspace.1.5.0.configure.zip),大致上步驟如下:

  1. 進入指令模式
  2. 移動到你的[dspace-source]/dspace/目錄
    > cd [dspace-source]/dspace/
  3. 執行ant編譯
    > mvn package
  4. 移動到你的[dspace-source]/dspace/target/dspace-[version].dir目錄
    > [dspace-source]/dspace/target/dspace-[version].dir
  5. 執行ant打包
    > ant -Dconfig=[dspace]/config/dspace.cfg update
  6. 停止你的Tomcat伺服器
    > cd [tomcat]/bin/
    > ./shutdown.sh
  7. 複製新的.war檔案到Tomcat的webpapps目錄:
    > \cp -rf [dspace]/webapps/* [tomcat]/webapps/
  8. 重新啟動Tomcat伺服器
    > cd [tomcat]/bin/
    > ./startup.sh


參考資料


本來想要一篇之內寫完的,沒想到內容已經長到令人看不下去的地步,連Windows Live Writer連帶的都變得很慢,這就是警告我該換一篇寫了。繼續努力!

(more...)