Spring Boot 作為 Spring Framework 的入門套件,底下可以抽換不同的 RESTful 架構,預設雖然會使用 Spring MVC,但其實需要的話也可以抽換成像是 Jersey 或者其他的架構。在這裡要紀錄的就是使用 Jersey 作為 RESTful 架構的方法。
Software entities (class, modules, functions, etc.) should be open for extension, but closed for modification. Junior programmers create simple solutions to simple problems. Senior programmers create complex solutions to complex problems. Great programmers find simple solutions to complex problems. 註1:本部落格的範例程式碼在 2015 年以前的文章中,大多是以全型空白做縮排。如需服用,請自行用文字編輯器的取代功能把全型空白取代成半型空白。
- Bertrand Meyer
- Charles Connell
註2:本部落格的內容授權請參閱部落格底部的授權宣告。
2018年12月12日 星期三
2017年12月12日 星期二
Jersey Test Framework 在 JUnit 5 的暫時解法
本來 Jersey Test Framework 是對應 JUnit 4 使用的 Jersey 測試環境
如果開發環境升級到 JUnit 5 的話,就會出現像下述這樣的 NullPointerException 的狀況。
java.lang.NullPointerException at org.glassfish.jersey.test.JerseyTest.target(JerseyTest.java:564) at org.glassfish.jersey.test.JerseyTest.target(JerseyTest.java:578)
2017年1月23日 星期一
在 Jersey Test Framework 中測試 Multipart Form Post 的環境設定
在 Jersey 上要使用 Multipart,需要額外做一點事情,包括引用 Multipart 的函式庫,並且設定要 Jersey 載入
不過當要使用 Jersey Test Framework 時,這個問題又稍微更麻煩了一點
因為 Jersey Test Framework 也是使用 Jersey,然後因此在 Client 端也額外需要做設定
除了 Jersey Test Framework 啟動的 Web Server 上需要動態註冊 Multipart 以外
還有 Jersey Test Framework 的 Client 也需要註冊 Multipart。(好麻煩 XD)
2016年7月2日 星期六
對 Jersey 製作的 RESTful API 做單元測試(二):啟用 Servlet
Jersey 中使用 Jersey Test Framework 做測試時
如果遇到需要使用 Servlet 的狀況,例如使用了 @Context HttpServletRequest request
可能就會注意到,HttpServletRequest 這類應該要自動被注入的東西,在測試時都沒有被注入
因此會發生像是 NullPointerException 等等的錯誤。
2016年6月15日 星期三
對 Jersey 製作的 RESTful API 做單元測試(一)
在比較完整的專案中,一般都會至少要包含自動化的單元測試
不過 Application 還好,如果是 RESTful API 的話,應該要怎麼測試呢?
原先我自己的想法是使用 JUnit,然後可能是使用內嵌式的 Jetty
讓測試程式執行時,自動打開一台本地端的 Jetty、把專案佈署上去後開始執行測試。
但既有找到的方法(例如 [1]) 感覺整合性都不太高…。
最後想說,也許其實有更簡易的方式,於是變更了關鍵字之後,發現…真的有 XD。
Jersey 原來本身就有涵蓋了對應的測試架構了。
2016年6月6日 星期一
使用 Maven 建立 Jersey 專案
當想要新建一個使用 Jersey 的 RESTful 的專案時,可以利用 Maven 直接產生範例 [1]
mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 \ -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \ -DgroupId=com.example -DartifactId=simple-service -Dpackage=com.example \ -DarchetypeVersion=2.23從上述的官方範例中,執行後會自動產生 groupId 為 com.example、artifactId 是 simple-service
並且會先建好 com.example 這個 package 的檔案(包含 src 目錄以及 pom.xml)
之後以 eclipse 來說就直接 import 到 eclipse 裡面,就可以開始增加自己需要的東西了。
另外紀錄一件小事,因為最近在 Windows 上習慣用 PowerShell,但上述的語法卻一直不能使用
研究了一段時間之後才發現,錯誤的並不是語法本身,而是在 PowerShell 上必須加上引號 [2]
例如:
mvn archetype:generate "-DarchetypeArtifactId=jersey-quickstart-grizzly2" \ "-DarchetypeGroupId=org.glassfish.jersey.archetypes" "-DinteractiveMode=false" \ "-DgroupId=com.example" "-DartifactId=simple-service" "-Dpackage=com.example" \ "-DarchetypeVersion=2.23"
參考資料:
2015年10月14日 星期三
使用 Jersey client 送出 HTTP 要求
不過在我們的環境中,遇到了一點奇妙的需求,Apache 的套件有點不合用
剛好又發現有網友提到,Jersey 本身就有提供 Jersey client 可以作為送出 HTTP 的方法
而且我們本來就用 Jersey 來提供 RESTful 介面,不需要另外引用函式庫,所以就拿來試試看了。
2014年7月18日 星期五
java.lang.ArrayIndexOutOfBoundsException: 48188
嚴重: StandardWrapper.Throwable java.lang.ArrayIndexOutOfBoundsException: 48188 at org.objectweb.asm.ClassReader.readClass(Unknown Source) at org.objectweb.asm.ClassReader.accept(Unknown Source) at org.objectweb.asm.ClassReader.accept(Unknown Source) at com.sun.jersey.spi.scanning.AnnotationScannerListener.onProcess(AnnotationScannerListener.java:136) at com.sun.jersey.core.spi.scanning.JarFileScanner.scan(JarFileScanner.java:97) at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner$1.f(WebAppResourcesScanner.java:94) at com.sun.jersey.core.util.Closing.f(Closing.java:71) at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:92) at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:79) at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:80) at com.sun.jersey.api.core.servlet.WebAppResourceConfig.init(WebAppResourceConfig.java:102) at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:89) at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:74) at com.sun.jersey.spi.container.servlet.WebComponent.getWebAppResourceConfig(WebComponent.java:668) at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:415) at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:582) at com.sun.jersey.spi.container.servlet.WebServletConfig.getDefaultResourceConfig(WebServletConfig.java:87) at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:699) at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:674) at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203) at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:374) at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:557) at javax.servlet.GenericServlet.init(GenericServlet.java:158) at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1284) at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1197) at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1087) at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5210) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5493) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:632) at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1083) at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1880) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:744)
找來找去找不太到相關的資料,好不容易找到 [1],顯示錯誤原因似乎是使用 Maven 時加上了 Jaxen 這個 dependency。
不過這個 dependency 是 dom4j 在操作 XPath 時要用的 ~"~
先記錄一下,也許可能的解法是不要用 XPath 來存取 XML 內容....。
參考資料:
1、较少遇到的错误—严重: StandardWrapper.Throwable—java.lang.ArrayIndexOutOfBoundsException: 48188
2013年11月22日 星期五
使用 Jersey + Multi-part 接受檔案上傳(二):實作即時上傳
依據 Jersey 的實作方法,實際上會先在伺服器上把使用者上傳的檔案全部接收、暫存到暫存的資料夾內,然後才會進入使用者定義的 Multi-part 的介面,並獲得代表檔案內容的 FormDataMultiPart。
也就是說實際上如果檔案是要存到伺服器上,伺服器對這樣一個檔案上傳的動作,會花費兩次的磁碟 I/O 時間來接收和儲存這個檔案。
2013年11月21日 星期四
WADL:描述網站的 REST 介面的文件
網址是 http://<host_name>/<project_name>/<prefix>/application.wadl
其中 HOST_NAME 是網站的網域名稱、PROJECT_NAME 是專案名稱、PREFIX 是在 web.xml 裡面定義的 REST 介面的入口。
參考資料:
1、Chapter 15. WADL Support
2013年10月29日 星期二
2013年10月14日 星期一
攔截 Jersey 的 Exception 時略過 Jersey 自身吐出的 Exception
因此會導致像是透過 GET 去存取應該用 POST 存取的 URL 時,應該要吐的 405 Method Not Allowed 一起被攔截並轉成指定的 Response 了。
而單獨略過 WebApplicationException 的方法可以參考 [1]。
2013年10月11日 星期五
使用 Jersey + Multi-part 接受檔案上傳
不過在佈署 Jersey 時可能會有一些奇怪的問題~
2013年10月9日 星期三
在 HTTP header 放入特殊字元或文字
Words of *TEXT MAY contain characters from character sets other than ISO-8859-1 [22] only when encoded according to the rules of RFC 2047 [14].因此 HTTP header 在不使用編碼的情況下,預設是以 ISO-8859-1 編碼。
而要放入 ISO-8859-1 不支援的字元時,必須使用 MIME 編碼才行。
2013年8月25日 星期日
在 Jersey 1.17 利用 LogginFilter 攔截 Request 的開始與結束
可以偷偷利用 LoggingFilter 作為攔截的手段。
2013年8月23日 星期五
Jersey 2 的事件監聽
可以參考 [1] 的官方文件描述。
不過專案用的是 Jersey 1.x......Orz
所以先暫存起來,但先不實際測試它 =..=
2013年4月16日 星期二
在 JAX-RS 取得 Get 參數
可以參考 [1] 的範例,以下轉錄 [1] 的範例的部份內容。
在 JAX-RS 中取得 Header 資訊
因為沒有特別需要修改的地方,所以以下的範例程式碼基本上是直接從 [1] 複製過來。
2013年4月11日 星期四
利用 JAX-RS 以 Stream 產出檔案下載
但該方法實現的方式是把檔案讀取成一個 byte 陣列,再丟給 Response 去回傳。
這會產生一個明顯的問題:這種作法要把整個檔案全部放進記憶體後才能產出,但如果檔案是 10GB 的超大檔案怎麼辦?
因此這次嘗試尋找在 JAX-RS 環境要怎麼辦到以 Stream 來吐檔案下載。