Skip to content

Commit 8be72f8

Browse files
authored
v1.2.0 (#10)
* update test dependency versions * add explicit plugin versions * update javadoc plugin version, fix javadoc errors * update readme, close #9 fix #8 fix #7
1 parent 1dd0369 commit 8be72f8

19 files changed

+706
-130
lines changed

README.md

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Servlet Url Mapper
22

3-
This small and non-invasive library is intended to be used in servlet-only environments where for some reasons developers are left without any frameworks like Spring MVC or Jax-RS.
3+
This small and non-invasive library is intended to be used in servlet-only environments where for some reasons developers are forced to use
4+
Servlet API directly (like in Atlassian plugin SDK).
5+
46
In such servlet-only environments, this library helps to handle nested URL structures in one servlet.
57

68
_Non-invasive_: You don't need to use `servlet url mapper` in every servlet, you can use only in some of them.
@@ -32,17 +34,39 @@ You can use different methods for different URLs in the same servlet. You can ev
3234
| /products | POST | ProductsServlet#create |
3335

3436

35-
## Usage
37+
## Quick start
3638

37-
1. You need to extend your servlet from `MappingServlet`
39+
1. Add the dependency
40+
41+
You should use [jitpack](https://jitpack.io) to add this library as a dependency for Maven or Gradle (or others).
42+
43+
```xml
44+
<repositories>
45+
<repository>
46+
<id>jitpack.io</id>
47+
<url>https://jitpack.io</url>
48+
</repository>
49+
</repositories>
50+
```
51+
```xml
52+
<dependency>
53+
<groupId>com.github.kodgemisi</groupId>
54+
<artifactId>servlet-url-mapper</artifactId>
55+
<version>1.2.0</version>
56+
</dependency>
57+
```
58+
59+
See https://jitpack.io/#kodgemisi/servlet-url-mapper
60+
61+
2. Extend your servlet from `MappingServlet`
3862

3963
```java
4064
public class MyServlet extends MappingServlet {
4165
//...
4266
}
4367
```
4468

45-
2. Register your url mappings via `protected urlMappingRegistrar` field from `MappingServlet`. You can do this either in `constructor` or in `init` method.
69+
3. Register your url mappings via `protected urlMappingRegistrar` field from `MappingServlet`. You can do this either in `constructor` or in `init` method.
4670

4771
```java
4872
@WebServlet(urlPatterns = {"/products", "/products/"})
@@ -83,36 +107,13 @@ public class MyServlet extends MappingServlet {
83107

84108
**Java 8 usage remainder**: Note that (assuming you have an `AddressHelper` class in your project) `AddressHelper::addAddress` usage implies that `addAddress` is a static method. You can use non-static methods by providing an object instead of Class name like `addressHelper::addAddress` assuming `addressHelper` is an object of `AddressHelper` class.
85109

86-
87-
## Dependency Management
88-
89-
You can use [jitpack](https://jitpack.io) to add this library as a dependency for maven, gradle and others.
90-
91-
```
92-
<repositories>
93-
<repository>
94-
<id>jitpack.io</id>
95-
<url>https://jitpack.io</url>
96-
</repository>
97-
</repositories>
98-
```
99-
```
100-
<dependency>
101-
<groupId>com.github.kodgemisi</groupId>
102-
<artifactId>servlet-url-mapper</artifactId>
103-
<version>1.0.0</version>
104-
</dependency>
105-
```
106-
107-
See https://jitpack.io/#kodgemisi/servlet-url-mapper
108-
109110
## License and Copyright
110111

111-
© Kod Gemisi Ltd.
112+
© 2017 - 2020 Kod Gemisi Ltd.
112113

113114
All material in this library's repository is copyrighted by [Kod Gemisi Ltd](http://kodgemisi.com/) unless stated otherwise.
114115

115-
This library is is subject to the terms of the Mozilla Public License, v. 2.0.
116+
This library is subject to the terms of the Mozilla Public License, v. 2.0.
116117
You can find full license in `license.txt` file or at [http://mozilla.org/MPL/2.0/](http://mozilla.org/MPL/2.0/) adress.
117118

118119
There is also an [easy to understand version](https://tldrlegal.com/license/mozilla-public-license-2.0-(mpl-2)) of the license.

pom.xml

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
<groupId>com.github.kodgemisi</groupId>
66
<artifactId>servlet-url-mapper</artifactId>
7-
<version>1.1.0</version>
7+
<version>1.2.0</version>
88
<packaging>jar</packaging>
99

1010
<organization>
1111
<name>Kod Gemisi</name>
12-
<url>http://kodgemisi.com/</url>
12+
<url>https://kodgemisi.com</url>
1313
</organization>
1414

1515
<name>Servlet Url Mapper</name>
@@ -36,20 +36,56 @@
3636
<scope>provided</scope>
3737
</dependency>
3838

39+
<dependency>
40+
<groupId>org.jetbrains</groupId>
41+
<artifactId>annotations</artifactId>
42+
<version>19.0.0</version>
43+
<optional>true</optional>
44+
</dependency>
45+
3946
<dependency>
4047
<groupId>org.junit.jupiter</groupId>
4148
<artifactId>junit-jupiter-engine</artifactId>
42-
<version>5.3.1</version>
49+
<version>5.6.2</version>
50+
<scope>test</scope>
51+
</dependency>
52+
53+
<dependency>
54+
<groupId>org.junit.jupiter</groupId>
55+
<artifactId>junit-jupiter-params</artifactId>
56+
<version>5.6.2</version>
4357
<scope>test</scope>
4458
</dependency>
4559

4660
<dependency>
4761
<groupId>org.mockito</groupId>
4862
<artifactId>mockito-core</artifactId>
49-
<version>2.23.0</version>
63+
<version>3.3.3</version>
5064
<scope>test</scope>
5165
</dependency>
5266

67+
<dependency>
68+
<groupId>org.projectlombok</groupId>
69+
<artifactId>lombok</artifactId>
70+
<version>1.18.10</version>
71+
<scope>test</scope>
72+
</dependency>
73+
74+
<dependency>
75+
<groupId>org.jodd</groupId>
76+
<artifactId>jodd-json</artifactId>
77+
<version>5.1.4</version>
78+
<scope>test</scope>
79+
</dependency>
80+
81+
<!--
82+
Latest working version pairs:
83+
logback-core & logback-classic 1.3.0-alpha4 version with
84+
slf4j-api 1.8.0-beta4
85+
86+
As of logback 1.3.0-alpha5 it requires slf4j-api version 2.0.x
87+
See http://logback.qos.ch/news.html (11th of October, 2019, Release of version 1.3.0-alpha5)
88+
-->
5389
<dependency>
5490
<groupId>ch.qos.logback</groupId>
5591
<artifactId>logback-core</artifactId>
@@ -71,6 +107,7 @@
71107
<plugin>
72108
<groupId>org.apache.maven.plugins</groupId>
73109
<artifactId>maven-source-plugin</artifactId>
110+
<version>3.2.1</version>
74111
<executions>
75112
<execution>
76113
<id>attach-sources</id>
@@ -83,6 +120,7 @@
83120
<plugin>
84121
<groupId>org.apache.maven.plugins</groupId>
85122
<artifactId>maven-javadoc-plugin</artifactId>
123+
<version>3.2.0</version>
86124
<executions>
87125
<execution>
88126
<id>attach-javadocs</id>
@@ -95,7 +133,7 @@
95133
<plugin>
96134
<groupId>org.apache.maven.plugins</groupId>
97135
<artifactId>maven-surefire-plugin</artifactId>
98-
<version>2.22.1</version>
136+
<version>3.0.0-M4</version>
99137
</plugin>
100138
</plugins>
101139
</build>

src/main/java/com/kodgemisi/servlet_url_mapping/ServletRequestHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
import java.io.IOException;
1313

1414
/**
15-
* <p>Servlets extending {@link MappingServlet} provide methods matching {@link ServletRequestHandler#handleRequest} signature so that those methods
16-
* can be used as request handlers.</p> <p>See {@link ServletUrlPatternRegistrar} for examples.</p>
15+
* <p>Servlets extending {@link MappingServlet} should provide methods matching {@link ServletRequestHandler#handleRequest} signature so that those methods
16+
* can be used as request handlers.</p> <p>See {@link ServletUrlPattern} for examples.</p>
1717
*
1818
* @author destan
1919
*/

src/main/java/com/kodgemisi/servlet_url_mapping/ServletUrl.java

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66

77
package com.kodgemisi.servlet_url_mapping;
88

9+
import org.jetbrains.annotations.NotNull;
10+
import org.jetbrains.annotations.Nullable;
11+
12+
import java.math.BigDecimal;
913
import java.util.*;
1014
import java.util.regex.Matcher;
1115
import java.util.regex.Pattern;
1216

1317
/**
1418
* <p>
15-
* Used to represent a request parsing result.<br>
19+
* This class represents a request URL parsing result.<br>
1620
* i.e {@code ServletUrl servletUrl = servletUrlPattern.parse(request)}<br>
1721
* i.e {@code ServletUrl servletUrl = servletUrlPatternRegistrar.parse(request)}
1822
* </p>
@@ -33,6 +37,8 @@ public class ServletUrl {
3337

3438
private static Pattern pathVariablePattern = Pattern.compile("\\{[A-Za-z_$]\\w*\\}");
3539

40+
static final Class<?> DEFAULT_PATH_VARIABLE_TYPE = String.class;
41+
3642
static {
3743
NOT_FOUND = new ServletUrl(NOT_FOUND_404, "<not applicable>", new Class[0], null);
3844
}
@@ -58,17 +64,19 @@ public class ServletUrl {
5864
* @param urlPattern
5965
* @param types
6066
*/
61-
ServletUrl(String name, String urlPattern, Class<?>[] types, ServletRequestHandler requestHandler) {
67+
ServletUrl(@Nullable String name, @NotNull String urlPattern, @NotNull Class<?>[] types, ServletRequestHandler requestHandler) {
6268
this.name = name;
6369
this.variableTypes = Collections.unmodifiableList(Arrays.asList(types));
6470
this.pathVariables = Collections.emptyMap();// prevent accidental use
6571
this.requestHandler = requestHandler;
6672

67-
List<String> names = new ArrayList<String>();
73+
final List<String> names = new ArrayList<>();
6874
this.pattern = Pattern.compile(urlPatternToRegex(urlPattern, names, variableTypes));
6975

70-
this.hasTrailingSlash = urlPattern.length() > 0 && urlPattern.substring(urlPattern.length() - 1, urlPattern.length()).equals("/");
76+
this.hasTrailingSlash = urlPattern.endsWith("/");
7177
this.variableNames = Collections.unmodifiableList(names);
78+
79+
//TODO check if variableNames & variableTypes sizes are consistent (only if variableTypes is not empty)
7280
}
7381

7482
ServletUrl(ServletUrl copy) {
@@ -79,40 +87,47 @@ public class ServletUrl {
7987
this.variableNames = copy.variableNames;
8088
this.pathVariables = new HashMap<>();
8189
this.requestHandler = copy.requestHandler;
90+
this.index = copy.index;
8291
}
8392

8493
private static Object getObjectAs(Class<?> clazz, String value) {
8594

8695
if (String.class.equals(clazz)) {
8796
return value;
8897
}
89-
else if (Number.class.isAssignableFrom(clazz)) {
98+
if (Integer.class.isAssignableFrom(clazz) || int.class.isAssignableFrom(clazz)) {
9099
return Integer.valueOf(value);
91100
}
92-
else if (Boolean.class.equals(clazz)) {
101+
if (Long.class.isAssignableFrom(clazz) || long.class.isAssignableFrom(clazz)) {
102+
return Long.valueOf(value);
103+
}
104+
if (BigDecimal.class.isAssignableFrom(clazz)) {
105+
return new BigDecimal(value);
106+
}
107+
if (Boolean.class.equals(clazz) || boolean.class.equals(clazz)) {
93108
return Boolean.valueOf(value);
94109
}
95110

96111
throw new IllegalArgumentException("Unsupported Type " + clazz.getName());
97112
}
98113

99114
/**
100-
* <strong>Caveat</strong> <p>Prefer primitive wrapper classes like Integer, Long etc. to avoid {@link NullPointerException} when the value is
101-
* null.</p> <p>Note that casting {@code null} to {@code int} throws {@link NullPointerException} however casting {@code null} to {@link Integer}
102-
* doesn't!</p>
115+
* <strong>Caveat</strong>
116+
* <p>Prefer primitive wrapper classes like Integer, Long etc. to avoid {@link NullPointerException} when the value might be null.</p>
117+
* <p>Note that casting {@code null} to {@code int} throws {@link NullPointerException} however casting {@code null} to {@link Integer} doesn't!</p>
103118
*
104-
* @param variable
105-
* @param <T>
106-
* @return
119+
* @param variable path variable name
120+
* @return T
107121
*/
108122
public <T> T variable(String variable) {
109-
return (T) pathVariables.get(variable);
123+
return (T) pathVariables.get(variable);//TODO return optional
110124
}
111125

112126
void addVariable(String value) {
113-
//TODO size validation
114127

115-
Class<?> clazz = index >= variableTypes.size() ? Integer.class : variableTypes.get(index);//default is Integer
128+
// Cannot use simply variableTypes.isEmpty() because user may give only first parameter type out of total two
129+
// E.g. servletUrlPattern.register("example", "/users/{id}/addresses/{addrId}", Long.class) here the second parameter is String (the default)
130+
final Class<?> clazz = index >= variableTypes.size() ? DEFAULT_PATH_VARIABLE_TYPE : variableTypes.get(index);
116131
pathVariables.put(variableNames.get(index++), getObjectAs(clazz, value));
117132
}
118133

@@ -131,22 +146,27 @@ private String urlPatternToRegex(final String urlPattern, List<String> names, Li
131146
String variable = matcher.group();
132147
names.add(variable.substring(1, variable.length() - 1));// get rid of {}
133148

134-
Class<?> clazz = names.size() > types.size() ? Integer.class : types.get(names.size() - 1);//default is Integer
149+
final Class<?> clazz = names.size() > types.size() ? DEFAULT_PATH_VARIABLE_TYPE : types.get(names.size() - 1);
135150
result = result.replace(variable, getRegexGroupByType(clazz));
136151
}
137152
return result;
138153
}
139154

140155
private String getRegexGroupByType(Class<?> clazz) {
156+
141157
if (String.class.equals(clazz)) {
142-
return "(\\w+)";
158+
return "([^/]+)";
143159
}
144-
if (Number.class.isAssignableFrom(clazz)) {
160+
if (Integer.class.isAssignableFrom(clazz) || int.class.isAssignableFrom(clazz) || Long.class.isAssignableFrom(clazz) || long.class.isAssignableFrom(clazz)) {
145161
return "(\\d+)";
146162
}
147-
if (Boolean.class.equals(clazz)) {
163+
if (BigDecimal.class.isAssignableFrom(clazz)) {
164+
return "(\\d+\\.*\\d*)";
165+
}
166+
if (Boolean.class.equals(clazz) || boolean.class.equals(clazz)) {
148167
return "(true|false|True|False|TRUE|FALSE)";
149168
}
169+
150170
throw new IllegalArgumentException("Unsupported Type " + clazz.getName());
151171
}
152172

0 commit comments

Comments
 (0)