[Spring-Framework] 19. Spring MVC - Spring Framework 5 REST, Jackson, Commons-FileUpload - (1)
프로젝트 위주로 불필요한 라이브러리는 전부 다 제거하고 순정으로 REST를 이해할 수 있도록 프로젝트를 기획하게 되었다.
REST를 제대로 소개하는 분들이 조금 적어서 간단하면서도 알기 쉽게 소개하려고 한다.
Spring Boot 위주의 REST가 많이 있는데, Spring Framework 5로 구현해도 무방하다.
결론부터 이야기하자면, 매우 잘 된다고 할 수 있다.
REST 이론부터 프로젝트까지 실질적으로 소개하려고 한다.
[사전 배경 지식]
- HTML, 간단한 폼 전송 방식 이해
- 자료구조(Data Structures)
(DB 이런 거 전부 제거함.)
[기타]
복잡하게 SOA 등 어렵고 관련 없는 주제는 다 제거하였다.
실질적으로 사용될 수 있는 수준의 REST에 대해서 소개해보려고 한다.
작업 환경
- IDE: Spring-tool-suite 4-4.7.2. Releases. / 최신(2020-09-29)
- Maven 3.6.3/1.16.0.20200610-1735 / 최신(2020-09-29)
- Java Version: 14이상 (OpenJDK 15) / 최신(2020-09-29)
pom - maven
- Spring Framework 5.2.9. RELEASES. / 최신(2020-09-29)
- javax.servlet-api 4.0.1 (2018-04-20) / 최신(2020-09-29)
https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api/4.0.1
- jackson-core 2.11.2 - XML (2020-08-02) / 최신(2020-09-29)
- jackson-databind 2.11.2 - XML (2020-08-02) / 최신(2020-09-29)
- commons-fileupload 1.4 (Apache Commons, 2018-12-24) / 최신(2020-09-29)
- WAS: Apache-tomcat-9.0.37-windows-x64 / 최신(2020-09-29)
1. 실험 결과
(1차)
- Maven Project (webapp)
Resteasy(JBoss 기반)
-> 웹 브라우저로 출력을 시도했을 때, XML 변환이 안 됨. (GET, POST, PUT, PATCH, DELETE)는 동작함.
(자료가 많이 부족함.)
[첨부(Attachments)] (영어)
resteasy-reference-guide-en-US.pdf
결론: 동작은 되나 XML 변환 도구를 찾을 수 없음. (실패)
(2차)
- Maven Project (webapp)
Jersay (4~5개 이상 POM 요구됨.)
과거에는 호환성이 나름대로 있었다고 하나, 현재에는 버전이 전부 제각각이고 사용하기에 부적합하다고 주장함.
(2020-09-28)
- 참고: 과거에는 com.sun..... jersey 형태의 패키지에서 현재는 "Jersey"가 독립 프로젝트로 분리되었음.
[첨부(Attachments)] (영어)
jersey-documentation-3.0.0-M1-user-guide.pdf
(3차)
- Spring Framework 5.4
- Spring Legacy Project -> Spring MVC Project
-> Pom(Maven) :
Spring Framework 5.4 - 2020-09-28일 기준 최신
Servlet 4.0.1 - 2020-09-28일 기준 최신
Jackson(core, bind) = 용도: XML Parser 2.11.2 - 2020-09-28일 기준 최신
Apache Commons-FileUpload
2. REST - 구현 관점에서 이론 소개
복잡하게 다른 걸 알 필요는 없다고 보고, HTTP 프로그램 작성해봤으면 POST/GET 이걸 다뤄본 경험들이 있을 것이다.
해외에 있는 "(Ph.D / 공학/이학 계통의 박사) 로이 필딩이 발표하신 학위 논문이 전 세계에 널리 알려진 거라고 보는 게 좋겠다.
관심이 있는 분들은 로이 필딩 박사 홈페이지에서 대략적인 이력을 살펴봐도 무방하다.
로이 필딩 박사의 주요 업적은 "Apache Web Server", "REST(학위 논문)" 두 가지라고 보면 된다.
웹 분야에서는 매우 권위있는 분 중 하나라고 생각하면 된다.
전송 방식의 문제인데 "POST/GET"만 웹에서는 가능하다. (HTML5 현재까지도)
REST의 약자인 REST(Representational State Transfer)는 GET/POST의 한계를 현실세계의 상태 개념과 접목 시켜서 고민을 한 모양이다.
(필자는 비록 논문을 읽어보진 못했지만, 작업을 하면서 느껴본 견해이다.)
핵심을 요약하면,
표 1. 다양한 전송방식(REST)
번호 |
작업 |
전송 방식 |
1 |
Create |
POST |
2 |
Read |
GET |
3 |
Update |
PUT 또는 Patch |
4 | Delete | DELETE |
REST는 4가지 전송 방식으로 확장되었다.
* 자바스크립트의 (Ajax)로 REST를 전송할 수 있는가?
-> 구현되었다고 주장하는 시중 코드들이 있을 수는 있겠으나 안 될 수도 있다.
(= 국제 웹 관련 표준기구(W3C) REST를 GET, POST 이외에도 표준으로 인정해주지 않는 이상 어렵다고 본다.)
표 2. 회원(Member) 자원에 대한 전송 방식 정의
작업 |
전송방식 |
URI |
생성 |
POST |
/member/new |
조회(전체) |
GET |
/member |
조회(특정) | GET | /member/{id} |
수정 |
PUT or PATCH |
/member/{id} |
삭제 | DELETE | /member/{id} |
꼭 이런 정의가 반드시 표준이라는 건 아니다. 일부 책들을 많이 대조해보고, 인터넷 검색 등을 해봤으나 "이런 형태로 쓸 수 있다." 이렇게 생각하면 좋겠다.
도메인이 달라지면, 달라지는 데로 바꿔서 사용해야 한다.
3. REST - 웹 표준 관점에서 무엇이 문제가 되는가?
코드 구현으로 이러한 기술적 문제를 소개해주려고 한다.
<form method="PUT">
~~~(중략)
</form>
코드 1) 이론적으로는 이렇게 되어야 함. (미지원)
<form method="POST">
<input type="hidden" name="_method" value="PUT">
~~~(중략)
</form>
코드 2) 과거 잠시 표준 규격에서 예외를 해주었던 규격 (미지원)
코드 2 형태로 구현하면, 동작될 수도 있었던 잠시 과거가 있긴 있었다.
표준 기구 등에서 제한에 걸어서 동작하지 않은 코드들이다.
(슈도 코드)
<script>
.....
ajax{
contents/json
method = "PUT" action=......
sucessed{
}
failed{
}
}
</script>
~~~(중략)
<form method="POST">
<input type="hidden" name="_method" value="PUT">
~~~(중략)
</form>
코드 3) ajax 자바스크립트 - 슈도 코드 (미지원)
이러한 코드 형태도 태스트해본 결과로는 동작하지 않는다.
그림 1. RequestMethod.PATCH, RequstMethod.PUT (Spring-Framework 5.4)
그림 1의 코드 형태로 정의했을 때, 전송이 되냐는 것이다.
결론부터 이야기하면, 안 된다.
다 안 된다고 적었으니, 그러면 "REST는 전혀 사용할 수 없는 건가요?"라는 물음이 들 수 있다.
이야기하면, 사용할 수 있다.
그런데 사용할 수 있는 용도가 있다는 것이다.
예1) 안드로이드 - 게시판 개발,
예2) 각종 규격 문서 또는 메시지 전달
(이런 부분에서 사용하면 적합하다고 본다.)
-> 원격지에서의 파일 전송과 내려받기는 괜찮을까요?
결론부터 놓고 보면, 안 된다. (원격지 REST를 활용해서 전송할 수 있는가?)
이론적으로는 Multipart로 해서 하면 될 것 같은데 POST에서는 처리될 수도 있겠으나, 어려울 수 있다.
= 표 2의 명세에서는 "등록(POST 방식)", "수정(PUT)" 방식을 소개하고 있는데, 다소 어렵다고 하는 이야기가 이런 부분이다.
굳이 어렵게 "전송 해더(Header)" 찾고 등의 고도의 작업을 시도해도 안 되는 이유가 보안이라는 주제 때문에 웹 브라우저 등에서 제약이 되어 버린다.
[호기심 자극하기]
안드로이드 어플을 사용하다 보면, 업로드 기능이 있는 경우에는 순수한 안드로이드 기술로 개발한 게 아니라 다른 웹 사이트를 호출해서 사용한 것이다.
개발 관점에서는 안 된다가 타당하다고 본다.
JSP, PHP, ASPX, ASP 등의 개발이 되니깐 그걸 그냥 그대로 활용한 것이다.
4. URI, URL에 대해서
* URI(Uniform Resource Identifier) : 자원의 식별자라는 의미
* URL(Uniform Resource Location) : 주소
URL은 URI의 하위개념이므로 혼용해도 무방할 수 있다.
이론적으로도 비슷하게 취급될 여지도 있어서 크게 용어를 따로 이중으로 외워둘 필요까진 없겠다.
5. REST의 실질적인 기능 (서버, 클라이언트 관계)
REST는 XML-RPC처럼 통신 계열로 보는 게 적합하다고 본다.
태스트를 하다보면, 육감적으로 왜 그렇게 보는 게 타당한지 이해할 수 있을 것으로 보인다.
그림 2. REST 전용 태스트 도구 - 브라우저 내 추가한 앱스(YARC! Yet Another REST Client)
가장 사용하기에 좋은 REST 태스트 도구라고 본다.
이 방식을 흔히 많이 소개하고 있는데, 코드로도 서버를 하나 구축해서 사용할 수 있다.
그림 3. RestTemplate를 이용한 Server 만들기 - 자바(스프링 프레임워크 5.4)
그림 4. Spring MVC Controller를 이용한 Client 만들기(RestController 등) - 자바(스프링 프레임워크 5.4)
정리를 해보면, 아래의 도식처럼 요약해볼 수 있다.
그림 5. REST Client와 Server 관계
이런 느낌으로 사용하게 된다.
이런 관계인 이유는 메시지 형태로 데이터를 반환하면, "객체, 메시지" 등의 정보를 송/수신 할 수가 있다.
어노테이션 |
기능 |
@RestController |
Controller가 REST 방식을 처리하기 위해 명시한 것 |
@ResponseBody |
일반적인 JSP와 같은 뷰로 전달되는 게 아니라 데이터 자체를 전달하기 위한 용도 |
@PathVariable |
URL 경로에 있는 값을 파라미터로 추출하려고 할 떄 사용 |
@CrossOrigin |
Ajax의 크로스 도메인 문제를 해결해주는 어노테이션 |
@RequestBody |
JSON 데이터를 원하는 타입으로 바인딩 처리 |
실질적으로 @RestController는 필수 정의이다.
@PathVariable과 함께 조합해서 가장 많이 사용할 것으로 보인다.
운이 나쁘면, 크로스 오리진 등은 접할 수도 있고, 안 접할 수도 있다.
6. 프로젝트 - 소개
복잡한 주제는 조금 정리하고 실질적으로 꼭 필요한 형태로 이러한 기능들을 모두 담고 있는 종합적인 REST만 전문으로 하는 프로젝트를 작성하였다.
그림 6. 프로젝트 구성도
다른 프로젝트 등에 비해서 매우 깔끔하고 간단한 형태로 REST를 취급할 수 있도록 신경을 조금 써서 작성한 것이다.
데이터베이스 지식, MyBatis(iBatis) 등 그런 거 일절 신경 안 쓰고 다뤄볼 수 있으니 참고하면 되겠다.
그림 7. 결과 1(client/listMapDelete/{id})
그림 8. 결과 2(/v1/api/board)의 JSON
그림 9. 결과 3(/v1/api/board)의 JSON (결과 출력)
그림 10. 결과 4(/v1/api/board/new)의 JSON(POST 전송)
그림 11. REST 전송 방식들
굉장히 많은 전송 방식이 있다는 것을 알 수 있다.
그림 12. 다중 업로드 프로젝트 (1) - REST 활용
그림 13. 다중 업로드 프로젝트 (2) - REST 활용
그림 14. 다중 업로드 프로젝트 (3) - 업로드 반응
그림 15. 다중 업로드 프로젝트 (4) - 업로드 반응
그림 15. 다중 업로드 프로젝트 (4) - 실제 업로드 모습
7. 프로젝트 생성
프로젝트 생성부터 단계적으로 아주 친절하게 하도록 하겠다.
[선수 준비되어야 할 부분]
- 필자의 글을 보면, 이 부분이 아주 자세히 소개되고 있는데 잊어버리기 쉬워서 다시 계속 강조하는 것이다.
한 번 셋팅해놓으면 크게 무방한데, 안 될 경우 등 문제를 충분히 고민한 후에 포함시킨 것이니깐 이해 해주었으면 좋겠다.
Help-> Eclipse Marketplace -> STS 검색 -> Spring-Tool Add-on
그림 16. Help의 Eclipse Marketplace
그림 17. Help의 Eclipse Marketplace
Spring Tools 3 Add-On for Spring Tools 4 3.9.14 Release를 선택한 후 Install을 해준다.
-> Spring Framework 3.2인가 "Spring Legacy Project" 기능을 최신 버전에서도 사용할 수 있도록 도와주는 도구이다.
Spring Legacy Project만 동작되어도 기본 셋팅하는 데 있어서 매우 수월해진다.
그림 18. Eclipse의 파일 메뉴 모습
File->New->Spring Legacy Project를 클릭한다.
그림 19. Spring MVC Project 생성하기 - Spring Legacy Project 기능
Spring MVC Project를 선택한다.
Project Name을 입력한다.
Next를 누른다.
그림 20. 패키지 정보 입력 - Spring MVC Project 생성하기
패키지명을 입력한다.
Finish를 누른다.
8. 자바 버전 바꾸기 - 프로젝트 Properties의 Build Path, Project-Factes
자바 버전을 바꿔줘야 한다.
이유는 Spring Legacy Project의 기본 셋팅이 하위버전으로 되어있기 때문이다.
그림 21. 프로젝트의 Properties 클릭하기 (프로젝트 마우스 오른쪽 버튼의 메뉴 모습)
프로젝트를 선택한다.
마우스 오른쪽 버튼을 클릭한다.
"Properties"를 클릭한다.
그림 22. Properties - Build Path
JRE System Library를 선택한다.
Edit를 누른다.
JRE를 클릭한다.
14버전으로 바꿔준다.
Apply를 누른다.
그림 23. Properties - Project Factes
Java의 버전을 14버전으로 바꿔준다.
Apply를 누른다.
Apply and Close를 누른다.
9. pom.xml 환경설정하기
꼭 열어봐야 할 사이트가 있다.
http://mvnrespository.com 사이트에 접속해서 버전 등을 확인해줘야 한다.
검색 방법을 잊어버린 사람들을 위해서 특별히 다시 언급하겠다.
그림 24. Spring Framework 검색 결과 - mvnrepository
Spring Framework를 클릭한다.
그림 25. Spring Framework 검색 결과 - mvnrepository
5.2.9.RELEASE를 선택한다.
그림 26. Spring Framework 검색 결과 - mvnrepository
Maven의 POM 배포 코드를 복사한다.
그림 27. pom.xml 수정작업(1) - 자바 버전과 스프링 프레임워크 버전 업그레이드
자바 버전과 Spring-Framework 버전을 변경해준다.
참고로 Spring-Framework의 경우에는 일명 "깔맞춤"으로 자동 버전 셋팅이 되는 경우가 있다.
다 되는 건 아니니깐 찾아보는 작업을 잊지 않아야 한다.
그림 28. pom.xml 수정작업(2)
javax.servlet 버전도 업그레이드 해준다.
그림 29. pom.xml 수정작업(3)
jackon-core, jackson-databind, commons-fileupload를 각각 검색해서 "복사, 붙여넣기"를 해준다.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>restexample2</artifactId>
<name>restExample2</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<!-- 자바 버전(14로 작성) -->
<java-version>14</java-version>
<!-- 스프링 프레임워크 - 최신 버전 적용함 -->
<org.springframework-version>5.2.9.RELEASE</org.springframework-version>
<org.aspectj-version>1.6.10</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
파일명: pom.xml
[첨부(Attachments)]
10. web.xml - 환경설정하기
web.xml 파일 셋팅은 무척 중요하다고 볼 수 있다.
반드시 해야 하는 작업이다.
경로명: /src/main/webapp/web.xml
그림 30. web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/appServlet/servlet-context.xml
/WEB-INF/spring/spring-file-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet>
<description></description>
<display-name>RestController</display-name>
<servlet-name>RestController</servlet-name>
<servlet-class>com.example.restexample2.controller.RestController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RestController</servlet-name>
<url-pattern>/RestController</url-pattern>
</servlet-mapping>
</web-app>
파일명: web.xml
[첨부(Attachments)]
11. spring-file-config.xml - 파일 생성하기
생성경로: /src/main/webapp/WEB-INF/spring/spring-file-config.xml
왜 작업을 해주냐면, Apache Commons-Fileupload를 사용하기 위해서이다.
그림 31. spring 폴더의 오른쪽 버튼 메뉴 모습
spring 폴더를 마우스 오른쪽 버튼으로 클릭한다.
New-> File을 클릭한다.
"spring-file-config.xml" 파일을 만들어준다.
그림 32. spring-file-config.xml - 작업 모습
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/>
<property name="maxUploadSizePerFile" value="10485760"/>
<property name="maxInMemorySize" value="0"/>
</bean>
</beans>
파일명: spring-file-config.xml
[첨부(Attachments)]
* 2부에서 만나요.
2부에서는 Controller와 Model, View, Util의 주제를 다뤄보겠다.
1. [Spring-Framework] 19. Spring MVC - Spring Framework 5 REST, Jackson, Commons-FileUpload - (1), Accessed by 2020-09-28