Compiling Spring's MVC url pattern regex - java

I need to be able to turn a list of Spring MVC URL pattern matching expressions into actual Regex Patterns so I can match strings against them.
Example Spring pattern :
/actionGroups/{action-group-id:\d+}
At the moment if I compile the above string I get a PatternSyntaxException because of illegal repetition due to the { characters.
Does Spring expose the mechanism it uses to compile/match these string into actual Regex Patterns? It would be cool if I could reuse their functionality.
Else I suppose I'd need to escape the { characters manually and strip out the pattern name.. action-group-id in this example.

There is nothing wrong with what you are trying to do, check your pattern syntax :
#RequestMapping("/actionGroups/{action-group-id:\\d+}")
public void test(#PathVariable(value = "action-group-id") Integer id) {
// you can use id ...
}

Looks like it was the AntPathMatcher class i was looking for as per Sotirios Delimanolis's comment above.
This class has a matches method which takes a URL Pattern like the one above and an URL string and returns true/false depending on if the string matches.

Related

How to serialize a java.util.regex.Pattern using protobuf?

I have an object that I want to serialize using Protocol Buffers and store in redis. The object contains a java.util.regex.Pattern that is complied when the object is instantiated.
public class SerializableEntity {
private Pattern pattern;
private List<String> options;
}
This pattern is used to validate inputs to a certain api. Since compiling the pattern each time is expensive, I'm compiling the pattern once during instantiation and then reusing the same pattern instance each time the api is invoked. How do I serialize this compile Pattern field in the following schema so I when I de-serialize the object, I can use it without compiling the pattern again?
message SerializableEntityProto {
repeated string option = 1;
// compiled pattern
}
Thanks.
java.util.regex.Pattern does not have encode and decode proto functions implemented in itself. However, you can implement that yourself pretty easy (as Andy Turner suggests). Something like this:
Proto
syntax = "proto2";
package termin4t0r;
option java_package = "com.example.termin4t0r";
// Proto for java.util.regex.Pattern
message RegexPatternProto {
// See Pattern.pattern()
optional string pattern = 1;
// See Pattern.flags()
optional int64 flags = 2;
}
Java encode and decode functions
class RegexpPatternProtos {
public static RegexPatternProto encode(java.util.regex.Pattern pattern) {
return RegexPatternProto.newBuilder()
.setPattern(pattern.pattern())
.setFlags(pattern.flags())
.build();
}
public static java.util.regex.Pattern decode(RegexPatternProto patternProto) {
return new RegexPatternProto(
patternProto.getPattern(), patternProto.getFlags());
}
}
I leave the unittests as an exercise :) I even find serializing this way preferable as protocol buffers have forward and backward compatibility, whereas java serialization has problems with that.
I think this is a case of square peg and round hole, protobuf and serialization is not meant to be used that way.
Anyway it seems like you initialize a regex with every API call. I don't know how your app decides which Regex to use for a particular API, but you must start out with a Regex string to compile.
Instead of trying to serializing the pattern, store it in memory in a HashMap<String,Pattern>( Regex string as a key and the compiled pattern as value). And then get the pattern when you need it.

Do path parameters in jax-rs #Path expression need to be slash-separated?

I'm inspecting some code in a JAX-RS springboot microservice that I'm starting to work on. I saw the following (modified):
#POST
#Path("{foo: ([^/]+?)?}{bar: (/[^/]+?)?}")
public Response doit(
#PathParam("foo") String foo,
#PathParam("bar") String bar,
#RequestBody UpdateRequest updateRequest, #Context HttpHeaders httpHeaders);
That #Path value looks odd. Instead of having explicit "/" markers in the string, it's trying to do it through the regex. I'm guessing this can work, because this is existing code, but is this really advisable? Is there any reason that this would be necessary?
I suppose a similar questionable example would be this:
#Path("foo{bar: (/[^/]+?)?}")
Is there any reason this is better than the simpler:
#Path("foo/{bar}")
The JAX-RS specification, specifically the “URI Templates” section under the Resources chapter, has the answer:
Template parameters can optionally specify the regular expression used to match their values. The default value matches any text and terminates at the end of a path segment but other values can be used to alter this behavior, e.g.:
#Path("widgets/{path:.+}")
public class Widget {
...
}
In the above example the Widget resource class will be matched for any request whose path starts with widgets and contains at least one more path segment; the value of the path parameter will be the request path following widgets. E.g. given the request path widgets/small/a the value of path would be small/a.
So, if you don’t provide a customn regex, the default boundary is the /.
Therefore, that complex regex is unnecessary. #Path("{foo}/{bar}" is fine.
Technically, it’s not exactly the same; the regex forces {bar} to include the leading /. Is it worth the complexity of regexes that need extra visual analysis? Not in my opinion.
If you just used
#Path("foo/{bar}")
then calling /foo would lead to a 404, because the / is static and it would require requesting /foo/. But when it's in the regex of bar, it makes it optional. So the example
#Path("foo{bar: (/[^/]+?)?}")
allows you to access the parent resource and the sub resource from the same resource method. As a more realistic example say you have
#Path("customers{id: (/[^/]+?)?}")
With this, we would have a resource method that could handle both accessing a collection resource and an single resource. As opposed to having two separate resource methods, one for each case. For example
#GET
#Path("customers{id: (/[^/]+?)?}")
public Response get(#PathParam("id") String id) {
if (id == null) {
return collection customers collection
} else {
fetch custom by id and return customer.
}
}
That's the only real benefit I can see in this situation. Would probably need more context, maybe some documenting comments from the author as to what they were trying to accomplish. Overall though, IMO the code looks unnecessarily over complicated.

How to escape SpEL dollar signs in Spring YAML configuration?

In a Spring YAML configuration file, I need to have a parameter like
csv:
file:
pattern: /some/path/${app-name}.csv
where the ${app-name} is dynamically replaced in run time by the Java code, and I do not want Spring to replace it at the startup.
To achieve this, I need to escape the $ character so Spring does not interpret it as SpEL.
The following answers do not work in YAML:
\$ How to escape EL dollar signs?
#{'$'} Escape property reference in Spring property file
Could not read properties if it contains dollar symbol (${var})
I tried all the combinations, like
pattern: /some/path/\${app-name}.csv
pattern: "/some/path/\${app-name}.csv"
pattern: /some/path/#{'$'}{app-name}.csv
pattern: "/some/path/#{'$'}{app-name}.csv"
and none of them produces the variable containing the requested string, with the dollar sign but without the escape characters.
Please notice that it is YAML configuration. In YAML files, # is the line comment character, everything from this character on is ignored. And if I use \#, the \ is then passed to the string.
ADDED: There has been an Spring project open issue 9628 open since 25.06.2008:
There is presently no way to inject a ${...} expression that won't be picked up by PropertyPlaceholderConfigurer. Ideally we should be able to inject a string that contains ${...} for later use in its target bean without involvement from PropertyPlaceholderConfigurer.
I had the same problem, i just found dumb clever solution
define a property named dollarSign or ds for short.
ds: "$"
then use it like so, ${ds} will be replace by $ at runtime.
csv:
file:
pattern: /some/path/${ds}{app-name}.csv
it was kind of funny when it worked.
Spring currently does not offer an escaping mechanism for property placeholders, there is an open issue (opened on 25.06.2008). In the comments, this workaround is mentioned (I am not sure whether it works with YAML):
csv:
file:
pattern: /some/path/#{'$'}{app-name}.csv
Note that when used after whitespace or at the beginning of a line, # in YAML starts a comment.
I've encountered a same problem. So you can resolve this by using yaml literal style symbol "|" , or by using literal_strip "|-" like following example.
application.yml
csv:
file:
pattern: |-
/some/path/${app-name}.csv
Actually My problem is config a formula in yml and then dynamic resolve the expression in java. Sharing the solution here.
I choose spring el solution and use spring version 5.0.9.RELEASE.
I define a formular in yml,
score:
formula: |-
10 * #x + #y
Then in a spring component bean,
#Value("${score.formula}")
String scoreFormula;
At last by using spring el,
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("x", 1);
context.setVariable("y", 1);
Integer score = parser.parseExpression(scoreFormula).getValue(context,Integer.class);
reference
yaml-multi-line
The answer really depends on how exactly you inject values into your code. Since you haven't provided it yet, I'll just list the various working techniques.
You can use #Value annotation to inject your property. Since, unlike #ConfigurationProperties, #Value does SpEL evaluation, you have to escape your value.
application.yml:
csv:
file:
pattern: /some/path/#{'$'}{app-name}.csv
TestController.java:
#RestController
public class TestController {
#Value("${csv.file.pattern}") private String pattern;
#GetMapping("pattern") public ResponseEntity<String> getPattern() {
return ResponseEntity.ok(pattern);
}
}
A GET to /pattern would result in an output /some/path/#{app-name}.csv, just as you needed
You can use #ConfigurationProperties, and here it really depends on how you structure your configuration objects. Generally, though, #ConfigurationProperties should require no escaping, as they don't support SpEL by default. The following works, though, so if your setup is different, modify it:
application.yml:
csv:
file:
pattern: /some/path/#{app-name}.csv
Config.java:
#ConfigurationProperties(prefix = "csv.file")
public class Config {
private String pattern;
public String getPattern() { return pattern; }
public void setPattern(String pattern) { this.pattern = pattern; }
}
TestController.java:
#RestController
public class TestController {
#Autowired private Config config;
#GetMapping("pattern") public ResponseEntity<String> getPattern() {
return ResponseEntity.ok(config.getPattern());
}
}
Again, a GET to /pattern would result in an output /some/path/#{app-name}.csv
What you most likely have is some different structure in your Config.java (post the relevant code, maybe?), and this could cause the property to not be processed properly.
Why not try using ${sys:$} which is ugly but effective. I think no one will use $ as the key.
Actually none of the answers worked for me. However, adding a double dollar sign worked for me fine:
csv:
file:
pattern: /some/path/$${app-name}.csv
You need to use #{'$'} and as you use yaml you need to surround the value with double quotes:
csv:
file:
pattern: "/some/path/#{'$'}{app-name}.csv"
Use a combination of empty key and dollar sign $ as default value:
csv:
file:
pattern: /some/path/${:$}{app-name}.csv

Mockito ArgumentMatchers doesNotMatch?

ArgumentMatchers.matches( String regex ) exists... and it is possible to devise regexes which don't match a given String. But it is far from trivial (several threads in SO).
Is it wrong of me (or wrong-headed) to think it might be a nice idea to request the Mockito designers to take the heavy-lifting out of this and add it as a feature? It just seems that, in the context of mocking and so forth, it is a far-from-exceptional use case...
PS also, I'm not clear with ArgumentMatchers.matches how you go about saying "this may be a multiline String we're matching against, don't worry about it"... wouldn't it be better to have a Pattern rather than a simple String?
later
Feature request "enhanced" at Mockito HQ (on Github). "bric3" there says one should use Jeff Bowman's technique for "does not match". But she/he seems to think the Pattern idea is worth thinking about.
Re not(): Mockito's own documentation says "Use additional matchers very judiciously because they may impact readability of a test. It is recommended to use matchers from Matchers and keep stubbing and verification simple."
Also I find I must "possible dupe" my own question: How to write a matcher that is not equal to something. Searching with hindsight is always easier...!
later still
Many thanks to Brice for adding this so quickly. Updated my gradle.build and... new 4.1 core downloaded from Maven Central and immediately available for use.
No need for a request: You can compose what you want using AdditionalMatchers.not.
when(yourComponent.acceptString(not(matches("foo|ba[rz]"))))
.thenThrow(new IllegalArgumentException());
If you want to match a Pattern, you might need to write your own ArgumentMatcher subclass, but it's quite easy from there:
public class MatchesPattern implements ArgumentMatcher<String> {
private final Pattern pattern;
public MatchesPattern(Pattern pattern) { this.pattern = pattern; }
#Override public boolean matches(String string) {
return pattern.matcher(string).matches();
}
#Override public String toString() {
return "[string matching /" + pattern.toString() + "/]";
}
/** Optional. */
public static MatchesPattern matchesPattern(Pattern pattern) {
return new MatchesPattern(pattern);
}
}
You can then consume that class using:
when(yourComponent.acceptString(not(argThat(new MatchesPattern(yourPattern)))
.thenThrow(new IllegalArgumentException());
// or with the static factory method:
when(yourComponent.acceptString(not(argThat(matchesPattern(yourPattern)))
.thenThrow(new IllegalArgumentException());
For future readers, Mockito 2.4.1 has been released with support of the Pattern class :
Now you should be abble to write :
when(yourComponent.acceptString(not(matches(Pattern.compile(...)))
.thenThrow(new IllegalArgumentException());

Resteasy optional Path element

Is it possible to use RestEasy's Path annotation to get the following string:
/items.json
I was thinking something like this: /items{(\.)?format}, where format could be json, xml etc.
I would then have a method with an argument like: #PathParam("format") String format.
Thanks.
I managed to make the following work with my use case: item{format:(\.(json|xml))?}
I chose to make the reg exp restrictive so as not to have to handle unsupported or invalid formats inside the actual service method, but if one prefers a more general approach I think that instead of (json|xml) one can add \S+.
you might want to create two methods, one for the default type and one for the optional types but yes, your logic should work:
#Path(items.{format})
public Response getItems(#PathParam("format") String format) {
}
#Path(items)
public Response getItems() {
return getItems("json");
}

Categories