Skip to content

Commit ef63148

Browse files
committed
sitemap generator fix #342
1 parent 7e41355 commit ef63148

15 files changed

Lines changed: 1144 additions & 25 deletions

File tree

coverage-report/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
<source>${project.parent.basedir}/jooby-guava-cache/src/main/java</source>
6868
<source>${project.parent.basedir}/jooby-executor/src/main/java</source>
6969
<source>${project.parent.basedir}/jooby-querydsl/src/main/java</source>
70+
<source>${project.parent.basedir}/jooby-sitemap/src/main/java</source>
7071
</sources>
7172
</configuration>
7273
</execution>
@@ -113,6 +114,7 @@
113114
<source>${project.parent.basedir}/jooby-guava-cache/src/test/java</source>
114115
<source>${project.parent.basedir}/jooby-executor/src/test/java</source>
115116
<source>${project.parent.basedir}/jooby-querydsl/src/test/java</source>
117+
<source>${project.parent.basedir}/jooby-sitemap/src/test/java</source>
116118
</sources>
117119
</configuration>
118120
</execution>
@@ -267,6 +269,12 @@
267269
<version>${project.version}</version>
268270
</dependency>
269271

272+
<dependency>
273+
<groupId>org.jooby</groupId>
274+
<artifactId>jooby-sitemap</artifactId>
275+
<version>${project.version}</version>
276+
</dependency>
277+
270278
<dependency>
271279
<groupId>org.jooby</groupId>
272280
<artifactId>jooby-jedis</artifactId>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.jooby.issues;
2+
3+
import java.util.Arrays;
4+
import java.util.stream.Collectors;
5+
6+
import org.jooby.sitemap.Sitemap;
7+
import org.jooby.sitemap.WebPageProvider;
8+
import org.jooby.test.ServerFeature;
9+
import org.junit.Test;
10+
11+
import cz.jiripinkas.jsitemapgenerator.WebPage;
12+
13+
public class Issue342 extends ServerFeature {
14+
15+
{
16+
use(new Sitemap().with(r -> {
17+
if (r.pattern().startsWith("/tags")) {
18+
// expand tags
19+
return Arrays.asList("foo", "bar").stream()
20+
.map(tag -> {
21+
WebPage page = WebPageProvider.SITEMAP.apply(r).get(0);
22+
page.setName(r.reverse(tag));
23+
return page;
24+
}).collect(Collectors.toList());
25+
}
26+
return WebPageProvider.SITEMAP.apply(r);
27+
}));
28+
29+
get("/", () -> "x");
30+
31+
get("/tags", () -> "x");
32+
33+
get("/tags/:name", () -> "x");
34+
35+
post("/ignored", () -> "x");
36+
}
37+
38+
@Test
39+
public void sitemap() throws Exception {
40+
request().get("/sitemap.xml")
41+
.expect("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
42+
"<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n" +
43+
"<url>\n" +
44+
"<loc>http://localhost:9999/</loc>\n" +
45+
"</url>\n" +
46+
"<url>\n" +
47+
"<loc>http://localhost:9999/tags</loc>\n" +
48+
"</url>\n" +
49+
"<url>\n" +
50+
"<loc>http://localhost:9999/tags/bar</loc>\n" +
51+
"</url>\n" +
52+
"<url>\n" +
53+
"<loc>http://localhost:9999/tags/foo</loc>\n" +
54+
"</url>\n" +
55+
"</urlset>")
56+
.header("Content-Type", "application/xml;charset=utf-8");
57+
}
58+
59+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.jooby.issues;
2+
3+
import org.jooby.sitemap.Sitemap;
4+
import org.jooby.test.ServerFeature;
5+
import org.junit.Test;
6+
7+
public class Issue342b extends ServerFeature {
8+
9+
{
10+
use(new Sitemap().filter(r -> false));
11+
12+
get("/", () -> "x");
13+
14+
get("/tags/:name", () -> "x");
15+
16+
post("/ignored", () -> "x");
17+
}
18+
19+
@Test
20+
public void sitemap() throws Exception {
21+
request().get("/sitemap.xml")
22+
.expect("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
23+
"<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n" +
24+
"</urlset>")
25+
.header("Content-Type", "application/xml;charset=utf-8");
26+
}
27+
28+
}

jooby-sitemap/pom.xml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4+
5+
<parent>
6+
<groupId>org.jooby</groupId>
7+
<artifactId>jooby-project</artifactId>
8+
<version>0.17.0-SNAPSHOT</version>
9+
</parent>
10+
11+
<modelVersion>4.0.0</modelVersion>
12+
<artifactId>jooby-sitemap</artifactId>
13+
14+
<name>sitemap module</name>
15+
16+
<build>
17+
<plugins>
18+
<!-- sure-fire -->
19+
<plugin>
20+
<groupId>org.apache.maven.plugins</groupId>
21+
<artifactId>maven-surefire-plugin</artifactId>
22+
<configuration>
23+
<includes>
24+
<include>**/*Test.java</include>
25+
<include>**/*Feature.java</include>
26+
<include>**/Issue*.java</include>
27+
</includes>
28+
</configuration>
29+
</plugin>
30+
31+
</plugins>
32+
</build>
33+
34+
<dependencies>
35+
<!-- Jooby -->
36+
<dependency>
37+
<groupId>org.jooby</groupId>
38+
<artifactId>jooby</artifactId>
39+
<version>${project.version}</version>
40+
</dependency>
41+
42+
<dependency>
43+
<groupId>cz.jiripinkas</groupId>
44+
<artifactId>jsitemapgenerator</artifactId>
45+
</dependency>
46+
47+
<!-- Test dependencies -->
48+
<dependency>
49+
<groupId>org.jooby</groupId>
50+
<artifactId>jooby</artifactId>
51+
<version>${project.version}</version>
52+
<scope>test</scope>
53+
<classifier>tests</classifier>
54+
</dependency>
55+
56+
<dependency>
57+
<groupId>junit</groupId>
58+
<artifactId>junit</artifactId>
59+
<scope>test</scope>
60+
</dependency>
61+
62+
<dependency>
63+
<groupId>org.easymock</groupId>
64+
<artifactId>easymock</artifactId>
65+
<scope>test</scope>
66+
</dependency>
67+
68+
<dependency>
69+
<groupId>org.powermock</groupId>
70+
<artifactId>powermock-api-easymock</artifactId>
71+
<scope>test</scope>
72+
</dependency>
73+
74+
<dependency>
75+
<groupId>org.powermock</groupId>
76+
<artifactId>powermock-module-junit4</artifactId>
77+
<scope>test</scope>
78+
</dependency>
79+
80+
<dependency>
81+
<groupId>com.fasterxml.jackson.core</groupId>
82+
<artifactId>jackson-databind</artifactId>
83+
<scope>test</scope>
84+
</dependency>
85+
86+
<dependency>
87+
<groupId>org.jacoco</groupId>
88+
<artifactId>org.jacoco.agent</artifactId>
89+
<classifier>runtime</classifier>
90+
<scope>test</scope>
91+
</dependency>
92+
93+
</dependencies>
94+
95+
</project>
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.jooby.internal.sitemap;
20+
21+
import static java.util.Objects.requireNonNull;
22+
import static javaslang.API.Case;
23+
import static javaslang.API.Match;
24+
25+
import java.util.List;
26+
import java.util.Optional;
27+
import java.util.function.Consumer;
28+
import java.util.function.Predicate;
29+
30+
import org.jooby.Env;
31+
import org.jooby.Jooby;
32+
import org.jooby.Route;
33+
import org.jooby.sitemap.WebPageProvider;
34+
35+
import com.google.inject.Binder;
36+
import com.google.inject.Key;
37+
import com.google.inject.name.Names;
38+
import com.typesafe.config.Config;
39+
40+
import cz.jiripinkas.jsitemapgenerator.WebPage;
41+
import javaslang.Function1;
42+
43+
@SuppressWarnings("rawtypes")
44+
public abstract class JSitemap<T extends JSitemap> implements Jooby.Module {
45+
46+
protected static final String SITEMAP = "/sitemap.xml";
47+
48+
static final Predicate<Route.Definition> NOT_ME = r -> {
49+
return !r.pattern().equals(SITEMAP);
50+
};
51+
52+
static final Predicate<Route.Definition> GET = r -> r.method().equals("GET");
53+
54+
private static final String SITEMAP_BASEURL = "sitemap.url";
55+
56+
private String path;
57+
58+
private Consumer<Binder> wpp;
59+
60+
private Predicate<Route.Definition> filter = GET;
61+
62+
private Optional<String> baseurl;
63+
64+
public JSitemap(final String path, final Optional<String> baseurl, final WebPageProvider wpp) {
65+
this.path = path;
66+
this.baseurl = baseurl;
67+
this.wpp = binder -> binder
68+
.bind(Key.get(WebPageProvider.class, Names.named(path))).toInstance(wpp);
69+
}
70+
71+
@SuppressWarnings("unchecked")
72+
public T filter(final Predicate<Route.Definition> filter) {
73+
this.filter = requireNonNull(filter, "Filter is required.");
74+
return (T) this;
75+
}
76+
77+
@SuppressWarnings("unchecked")
78+
public T with(final WebPageProvider wpp) {
79+
requireNonNull(wpp, "WebPageProvider is required.");
80+
this.wpp = binder -> binder.bind(Key.get(WebPageProvider.class, Names.named(path)))
81+
.toInstance(wpp);
82+
return (T) this;
83+
}
84+
85+
@SuppressWarnings("unchecked")
86+
public T with(final Class<? extends WebPageProvider> wpp) {
87+
requireNonNull(wpp, "WebPageProvider is required.");
88+
this.wpp = binder -> binder.bind(Key.get(WebPageProvider.class, Names.named(path)))
89+
.to(wpp);
90+
return (T) this;
91+
}
92+
93+
@Override
94+
public void configure(final Env env, final Config conf, final Binder binder) {
95+
String baseurl = this.baseurl.orElseGet(() -> Match(conf.hasPath(SITEMAP_BASEURL)).of(
96+
Case(true, () -> conf.getString(SITEMAP_BASEURL)),
97+
Case(false, () -> {
98+
Config $ = conf.getConfig("application");
99+
return "http://" + $.getString("host") + ":" + $.getString("port") + $.getString("path");
100+
})));
101+
102+
wpp.accept(binder);
103+
env.routes().get(path, new SitemapHandler(path, NOT_ME.and(filter), gen(baseurl)));
104+
}
105+
106+
protected abstract Function1<List<WebPage>, String> gen(String baseurl);
107+
108+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.jooby.internal.sitemap;
20+
21+
import java.util.List;
22+
import java.util.Set;
23+
import java.util.function.Predicate;
24+
import java.util.stream.Collectors;
25+
26+
import org.jooby.MediaType;
27+
import org.jooby.Request;
28+
import org.jooby.Results;
29+
import org.jooby.Route;
30+
import org.jooby.sitemap.WebPageProvider;
31+
32+
import cz.jiripinkas.jsitemapgenerator.WebPage;
33+
import javaslang.Function1;
34+
35+
class SitemapHandler implements Route.OneArgHandler {
36+
37+
private String name;
38+
39+
private Predicate<Route.Definition> filter;
40+
41+
private Function1<List<WebPage>, String> gen;
42+
43+
public SitemapHandler(final String name, final Predicate<Route.Definition> filter,
44+
final Function1<List<WebPage>, String> gen) {
45+
this.name = name;
46+
this.filter = filter;
47+
this.gen = gen;
48+
}
49+
50+
@Override
51+
public Object handle(final Request req) throws Throwable {
52+
Set<Route.Definition> routes = req.require(Route.KEY);
53+
WebPageProvider provider = req.require(name, WebPageProvider.class);
54+
List<WebPage> pages = routes.stream()
55+
.filter(filter)
56+
.flatMap(r -> provider.apply(r).stream())
57+
.map(page -> {
58+
String name = page.getName();
59+
if (name.startsWith("/")) {
60+
name = name.substring(1);
61+
}
62+
page.setName(name);
63+
return page;
64+
}).collect(Collectors.toList());
65+
return Results.ok(gen.apply(pages)).type(MediaType.xml);
66+
}
67+
68+
}

0 commit comments

Comments
 (0)