source: josm/trunk/test/unit/org/openstreetmap/josm/testutils/PluginServer.java@ 17275

Last change on this file since 17275 was 17195, checked in by simon04, 4 years ago

see #15102 - see #16637 - Use WireMockServer.url()

  • Property svn:eol-style set to native
File size: 9.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.testutils;
3
4import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
5
6import java.io.File;
7import java.io.IOException;
8import java.nio.file.Path;
9import java.util.Arrays;
10import java.util.HashMap;
11import java.util.List;
12import java.util.Map;
13import java.util.Objects;
14import java.util.jar.JarFile;
15import java.util.stream.Collectors;
16import java.util.stream.StreamSupport;
17
18import org.junit.runner.Description;
19import org.junit.runners.model.Statement;
20import org.openstreetmap.josm.TestUtils;
21import org.openstreetmap.josm.tools.Logging;
22
23import com.github.tomakehurst.wiremock.WireMockServer;
24import com.github.tomakehurst.wiremock.client.MappingBuilder;
25import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
26import com.github.tomakehurst.wiremock.client.WireMock;
27import com.github.tomakehurst.wiremock.core.Options;
28import com.github.tomakehurst.wiremock.junit.WireMockRule;
29
30public class PluginServer {
31 public static class RemotePlugin {
32 private final File srcJar;
33 private final Map<String, String> attrOverrides;
34 private final String pluginName;
35 private final String pluginURL;
36
37 public RemotePlugin(
38 final File srcJar
39 ) {
40 this(srcJar, null, null, null);
41 }
42
43 public RemotePlugin(
44 final File srcJar,
45 final Map<String, String> attrOverrides
46 ) {
47 this(srcJar, attrOverrides, null, null);
48 }
49
50 public RemotePlugin(
51 final File srcJar,
52 final Map<String, String> attrOverrides,
53 final String pluginName
54 ) {
55 this(srcJar, attrOverrides, pluginName, null);
56 }
57
58 public RemotePlugin(
59 final File srcJar,
60 final Map<String, String> attrOverrides,
61 final String pluginName,
62 final String pluginURL
63 ) {
64 this.srcJar = srcJar;
65 this.attrOverrides = attrOverrides;
66 this.pluginName = pluginName;
67 this.pluginURL = pluginURL;
68 }
69
70 @Override
71 public int hashCode() {
72 return Objects.hash(this.srcJar, this.attrOverrides, this.pluginName, this.pluginURL, this.getClass());
73 }
74
75 public String getRemotePluginsListManifestSection() {
76 final Map<String, String> attrs = new HashMap<>();
77 JarFile jarFile = null;
78
79 if (srcJar != null) {
80 try {
81 jarFile = new JarFile(srcJar, false);
82 jarFile.getManifest().getMainAttributes().entrySet().forEach(
83 entry -> attrs.put(entry.getKey().toString(), entry.getValue().toString())
84 );
85 } catch (IOException e) {
86 Logging.warn(
87 "Failed to open {0} as a jar file. Using empty initial manifest. Error was: {1}",
88 srcJar,
89 e
90 );
91 } finally {
92 if (jarFile != null) {
93 try {
94 jarFile.close();
95 } catch (IOException e) {
96 Logging.warn(
97 "Somehow failed to close jar file {0}. Error was: {1}",
98 srcJar,
99 e
100 );
101 }
102 }
103 }
104 }
105
106 if (this.attrOverrides != null) {
107 attrs.putAll(this.attrOverrides);
108 }
109
110 return attrs.entrySet().stream().filter(entry -> entry.getValue() != null).map(
111 entry -> String.format("\t%s: %s\n", entry.getKey(), entry.getValue())
112 ).collect(Collectors.joining());
113 }
114
115 private String getJarPathBeneathFilesDir() {
116 if (this.srcJar != null) {
117 final Path jarPath = this.srcJar.toPath().toAbsolutePath().normalize();
118 final Path filesRootPath = new File(TestUtils.getTestDataRoot()).toPath().toAbsolutePath().resolve("__files").normalize();
119
120 if (jarPath.startsWith(filesRootPath)) {
121 // would just use .toString() but need to force use of *forward slash* path separators on all platforms
122 final Path path = filesRootPath.relativize(jarPath);
123 return StreamSupport.stream(path.spliterator(), false)
124 .map(Path::toString)
125 .collect(Collectors.joining("/"));
126 }
127 }
128 return null;
129 }
130
131 protected String getPluginURLPath() {
132 final String jarPathBeneathFilesDir = this.getJarPathBeneathFilesDir();
133
134 if (jarPathBeneathFilesDir != null) {
135 return "/" + jarPathBeneathFilesDir;
136 }
137
138 return String.format("/%h/%s.jar", this.hashCode(), pluginName != null ? pluginName : Integer.toHexString(this.hashCode()));
139 }
140
141 public String getPluginURL(WireMockServer wireMockServer) {
142 if (this.pluginURL != null) {
143 return this.pluginURL;
144 } else if (wireMockServer != null && this.getJarPathBeneathFilesDir() != null) {
145 return wireMockServer.url(this.getPluginURLPath());
146 }
147 return "http://example.com" + this.getPluginURLPath();
148 }
149
150 public String getName() {
151 if (this.pluginName != null) {
152 return this.pluginName;
153 } else if (this.srcJar != null) {
154 return this.srcJar.getName().split("\\.", 2)[0];
155 }
156 return Integer.toHexString(this.hashCode());
157 }
158
159 public String getRemotePluginsListSection(WireMockServer wireMockServer) {
160 return String.format(
161 "%s.jar;%s\n%s",
162 this.getName(),
163 this.getPluginURL(wireMockServer),
164 this.getRemotePluginsListManifestSection()
165 );
166 }
167
168 public MappingBuilder getMappingBuilder() {
169 final String jarPathBeneathFilesDir = this.getJarPathBeneathFilesDir();
170
171 if (jarPathBeneathFilesDir != null) {
172 return WireMock.get(WireMock.urlMatching(this.getPluginURLPath()));
173 }
174 return null;
175 }
176
177 public ResponseDefinitionBuilder getResponseDefinitionBuilder() {
178 final String jarPathBeneathFilesDir = this.getJarPathBeneathFilesDir();
179
180 if (jarPathBeneathFilesDir != null) {
181 return WireMock.aResponse().withStatus(200).withHeader("Content-Type", "application/java-archive").withBodyFile(
182 jarPathBeneathFilesDir
183 );
184 }
185 return null;
186 }
187 }
188
189 protected final List<RemotePlugin> pluginList;
190
191 public PluginServer(RemotePlugin... remotePlugins) {
192 this.pluginList = Arrays.asList(remotePlugins);
193 }
194
195 public void applyToWireMockServer(WireMockServer wireMockServer) {
196 // first add the plugins list
197 wireMockServer.stubFor(
198 WireMock.get(WireMock.urlEqualTo("/plugins")).willReturn(
199 WireMock.aResponse().withStatus(200).withHeader("Content-Type", "text/plain").withBody(
200 this.pluginList.stream().map(
201 remotePlugin -> remotePlugin.getRemotePluginsListSection(wireMockServer)
202 ).collect(Collectors.joining())
203 )
204 )
205 );
206
207 // now add each file that we're able to serve
208 for (final RemotePlugin remotePlugin : this.pluginList) {
209 final MappingBuilder mappingBuilder = remotePlugin.getMappingBuilder();
210 final ResponseDefinitionBuilder responseDefinitionBuilder = remotePlugin.getResponseDefinitionBuilder();
211
212 if (mappingBuilder != null && responseDefinitionBuilder != null) {
213 wireMockServer.stubFor(
214 remotePlugin.getMappingBuilder().willReturn(remotePlugin.getResponseDefinitionBuilder())
215 );
216 }
217 }
218 }
219
220 public PluginServerRule asWireMockRule() {
221 return this.asWireMockRule(
222 options().dynamicPort().usingFilesUnderDirectory(TestUtils.getTestDataRoot()),
223 true
224 );
225 }
226
227 public PluginServerRule asWireMockRule(Options ruleOptions, boolean failOnUnmatchedRequests) {
228 return new PluginServerRule(ruleOptions, failOnUnmatchedRequests);
229 }
230
231 public class PluginServerRule extends WireMockRule {
232 public PluginServerRule(Options ruleOptions, boolean failOnUnmatchedRequests) {
233 super(ruleOptions, failOnUnmatchedRequests);
234 }
235
236 public PluginServer getPluginServer() {
237 return PluginServer.this;
238 }
239
240 @Override
241 public Statement apply(Statement base, Description description) {
242 return super.apply(new Statement() {
243 @Override
244 public void evaluate() throws Throwable {
245 PluginServer.this.applyToWireMockServer(PluginServerRule.this);
246 base.evaluate();
247 }
248 }, description);
249 }
250 }
251}
Note: See TracBrowser for help on using the repository browser.