segments = copy.getSegments();
87 |
88 | if (!segments.isEmpty()) {
89 | final int lastSegmentIndex = segments.size() - 1;
90 | final String filename = segments.get(lastSegmentIndex);
91 |
92 | if (!Strings.isEmpty(filename)) {
93 | final ResourceUrl resourceUrl = new ResourceUrl(filename, new PageParameters());
94 |
95 | cachingStrategyProvider.get().undecorateUrl(resourceUrl);
96 |
97 | if (Strings.isEmpty(resourceUrl.getFileName())) {
98 | throw new IllegalStateException(
99 | "caching strategy returned empty name for "
100 | + resourceUrl);
101 | }
102 |
103 | segments.set(lastSegmentIndex, resourceUrl.getFileName());
104 | }
105 | }
106 |
107 | return copy.toString();
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/request/resource/IWebjarsResourceReference.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.request.resource;
2 |
3 | /**
4 | * Marker interface for webjars resource references.
5 | *
6 | * @author miha
7 | */
8 | public interface IWebjarsResourceReference {
9 |
10 | /**
11 | * @return original name of webjars resource before resolving it
12 | */
13 | String getOriginalName();
14 | }
15 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/request/resource/WebjarsCssResourceReference.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.request.resource;
2 |
3 | import static de.agilecoders.wicket.webjars.util.Helper.prependWebjarsPathIfMissing;
4 | import static de.agilecoders.wicket.webjars.util.WebjarsVersion.useRecent;
5 |
6 | import java.util.Locale;
7 |
8 | import org.apache.wicket.request.resource.CssResourceReference;
9 |
10 | /**
11 | * Static resource reference for webjars css resources. The resources are filtered (stripped comments and
12 | * whitespace) if there is registered compressor.
13 | *
14 | * You are able find out how a specific path looks like on http://www.webjars.org/.
15 | *
16 | *
17 | * @author miha
18 | */
19 | public class WebjarsCssResourceReference extends CssResourceReference implements IWebjarsResourceReference {
20 |
21 | private final String originalName;
22 |
23 | /**
24 | * Construct.
25 | *
26 | * @param name The webjars path to load
27 | */
28 | public WebjarsCssResourceReference(final String name) {
29 | super(WebjarsCssResourceReference.class, useRecent(prependWebjarsPathIfMissing(name)));
30 |
31 | this.originalName = name;
32 | }
33 |
34 | /**
35 | * {@inheritDoc}
36 | */
37 | @Override
38 | public final String getOriginalName() {
39 | return originalName;
40 | }
41 |
42 | @Override
43 | public final Locale getLocale() {
44 | return null;
45 | }
46 |
47 | @Override
48 | public final String getStyle() {
49 | return null;
50 | }
51 |
52 | @Override
53 | public final String getVariation() {
54 | return null;
55 | }
56 |
57 | @Override
58 | public String toString() {
59 | return "[webjars css resource] " + getOriginalName() + " (resolved name: " + getName() + ")";
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/request/resource/WebjarsJavaScriptResourceReference.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.request.resource;
2 |
3 | import static de.agilecoders.wicket.webjars.util.Helper.prependWebjarsPathIfMissing;
4 | import static de.agilecoders.wicket.webjars.util.WebjarsVersion.useRecent;
5 |
6 | import java.util.Locale;
7 |
8 | import org.apache.wicket.request.resource.JavaScriptResourceReference;
9 |
10 | /**
11 | * Static resource reference for javascript webjars resources. The resources are filtered (stripped comments
12 | * and whitespace) if there is a registered compressor.
13 | *
14 | * You are able find out how a specific name looks like on http://www.webjars.org/.
15 | *
16 | *
17 | * @author miha
18 | */
19 | public class WebjarsJavaScriptResourceReference extends JavaScriptResourceReference implements IWebjarsResourceReference {
20 |
21 | private final String originalName;
22 |
23 | /**
24 | * Construct.
25 | *
26 | * @param name The webjars path to load
27 | */
28 | public WebjarsJavaScriptResourceReference(final String name) {
29 | super(WebjarsJavaScriptResourceReference.class, useRecent(prependWebjarsPathIfMissing(name)));
30 |
31 | this.originalName = name;
32 | }
33 |
34 | /**
35 | * {@inheritDoc}
36 | */
37 | @Override
38 | public final String getOriginalName() {
39 | return originalName;
40 | }
41 |
42 | @Override
43 | public final Locale getLocale() {
44 | return null;
45 | }
46 |
47 | @Override
48 | public final String getStyle() {
49 | return null;
50 | }
51 |
52 | @Override
53 | public final String getVariation() {
54 | return null;
55 | }
56 |
57 | @Override
58 | public String toString() {
59 | return "[webjars js resource] " + getOriginalName() + " (resolved name: " + getName() + ")";
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/request/resource/WebjarsPackageResourceReference.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.request.resource;
2 |
3 | import static de.agilecoders.wicket.webjars.util.Helper.prependWebjarsPathIfMissing;
4 | import static de.agilecoders.wicket.webjars.util.WebjarsVersion.useRecent;
5 |
6 | import java.util.Locale;
7 |
8 | import org.apache.wicket.request.resource.PackageResourceReference;
9 |
10 | /**
11 | * Static resource reference for webjars resources.
12 | *
13 | * You are able find out how a specific path looks like on http://www.webjars.org/.
14 | *
15 | *
16 | * @author Erik Geletti
17 | */
18 | public class WebjarsPackageResourceReference extends PackageResourceReference implements IWebjarsResourceReference {
19 |
20 | private final String originalName;
21 |
22 | /**
23 | * Construct.
24 | *
25 | * @param name The webjars path to load
26 | */
27 | public WebjarsPackageResourceReference(final String name) {
28 | super(WebjarsPackageResourceReference.class, useRecent(prependWebjarsPathIfMissing(name)));
29 |
30 | this.originalName = name;
31 | }
32 |
33 | /**
34 | * {@inheritDoc}
35 | */
36 | @Override
37 | public final String getOriginalName() {
38 | return originalName;
39 | }
40 |
41 | @Override
42 | public final Locale getLocale() {
43 | return null;
44 | }
45 |
46 | @Override
47 | public final String getStyle() {
48 | return null;
49 | }
50 |
51 | @Override
52 | public final String getVariation() {
53 | return null;
54 | }
55 |
56 | @Override
57 | public String toString() {
58 | return "[webjars package resource] " + getOriginalName() + " (resolved name: " + getName() + ")";
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/settings/IWebjarsSettings.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.settings;
2 |
3 | import java.time.Duration;
4 | import java.util.regex.Pattern;
5 |
6 | import de.agilecoders.wicket.webjars.collectors.AssetPathCollector;
7 |
8 | /**
9 | * Settings interface for all webjars depended settings
10 | *
11 | * @author miha
12 | */
13 | public interface IWebjarsSettings {
14 |
15 | /**
16 | * @return {@link ResourceStreamProvider} to use to load resources
17 | */
18 | ResourceStreamProvider resourceStreamProvider();
19 |
20 | /**
21 | * @return a set of {@link AssetPathCollector} instances to use to find
22 | * resources
23 | */
24 | AssetPathCollector[] assetPathCollectors();
25 |
26 | /**
27 | * @return the webjars package path (e.g. "META-INF.resources.webjars")
28 | */
29 | String webjarsPackage();
30 |
31 | /**
32 | * @return the path where all webjars are stored (e.g. "META-INF/resources/webjars")
33 | */
34 | String webjarsPath();
35 |
36 | /**
37 | * @return classloaders to use
38 | */
39 | ClassLoader[] classLoaders();
40 |
41 | /**
42 | * @return the pattern to filter accepted webjars resources
43 | */
44 | Pattern resourcePattern();
45 |
46 | /**
47 | * @return the full path pattern. The pattern must contain 3 groups: prefix, version, filename
48 | */
49 | Pattern webjarsPathPattern();
50 |
51 | /**
52 | * @return placeholder for recent version (e.g. current)
53 | */
54 | String recentVersionPlaceHolder();
55 |
56 | /**
57 | * @return timeout which is used when reading from cache (Future.get(timeout))
58 | */
59 | Duration readFromCacheTimeout();
60 |
61 | /**
62 | * @return true, if the resources for the webjars should be loaded from a CDN network
63 | */
64 | boolean useCdnResources();
65 |
66 | /**
67 | * @return base URL of the webjars CDN
68 | */
69 | String cdnUrl();
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/settings/ResourceStreamProvider.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.settings;
2 |
3 | import de.agilecoders.wicket.webjars.util.ClassLoaderResourceStreamProvider;
4 | import de.agilecoders.wicket.webjars.util.IResourceStreamProvider;
5 | import de.agilecoders.wicket.webjars.util.UrlResourceStreamProvider;
6 | import org.apache.wicket.util.string.Strings;
7 |
8 | /**
9 | * A ResourceStreamProvider is responsible for creating resource streams. There are several
10 | * implementations that
11 | *
12 | * @author miha
13 | */
14 | public abstract class ResourceStreamProvider {
15 |
16 | /**
17 | * The ClassLoader provider uses {@link ClassLoader#getResourceAsStream(String)} with a custom
18 | * {@link org.apache.wicket.util.resource.AbstractResourceStream} implementation.
19 | */
20 | public static final ResourceStreamProvider ClassLoader = new ResourceStreamProvider() {
21 | @Override
22 | public IResourceStreamProvider newInstance(ClassLoader... classLoaders) {
23 | return new ClassLoaderResourceStreamProvider(classLoaders);
24 | }
25 | };
26 |
27 | /**
28 | * The Url provider uses a {@link org.apache.wicket.core.util.resource.UrlResourceStream} to load
29 | * a resource. This provider can't be used on GAE, because it uses {@link java.net.URL#openConnection()}.
30 | */
31 | public static final ResourceStreamProvider Url = new ResourceStreamProvider() {
32 | @Override
33 | public IResourceStreamProvider newInstance(ClassLoader... classLoaders) {
34 | return new UrlResourceStreamProvider(classLoaders);
35 | }
36 | };
37 |
38 | /**
39 | * creates a new {@link de.agilecoders.wicket.webjars.util.IResourceStreamProvider} instance according to
40 | * this instance.
41 | *
42 | * @param classLoaders the class loaders to use to load/find resources
43 | * @return new {@link de.agilecoders.wicket.webjars.util.IResourceStreamProvider} instance
44 | */
45 | public abstract IResourceStreamProvider newInstance(ClassLoader... classLoaders);
46 |
47 | /**
48 | * @return best fitting {@link de.agilecoders.wicket.webjars.settings.ResourceStreamProvider}
49 | */
50 | public static ResourceStreamProvider bestFitting() {
51 | if (Strings.isEmpty(System.getProperty("com.google.appengine.runtime.environment"))) {
52 | return ClassLoader;
53 | } else {
54 | return Url;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/settings/WebSphereWebjarsSettings.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.settings;
2 |
3 | import de.agilecoders.wicket.webjars.collectors.AssetPathCollector;
4 | import de.agilecoders.wicket.webjars.collectors.FileAssetPathCollector;
5 | import de.agilecoders.wicket.webjars.collectors.VfsAssetPathCollector;
6 | import de.agilecoders.wicket.webjars.collectors.WebSphereClasspathAssetPathCollector;
7 |
8 | /**
9 | * {@link IWebjarsSettings} which should be used when deploying on IBM WebSphere
10 | *
11 | * Make sure to add dependency on edu.emory.mathcs.util:emory-util-classloader to the classpath!
12 | *
13 | * @see WebSphereClasspathAssetPathCollector
14 | */
15 | public class WebSphereWebjarsSettings extends WebjarsSettings{
16 |
17 | public WebSphereWebjarsSettings() {
18 | super();
19 |
20 | // WebSphere needs a trailing slash to list resources with ClassLoader#getResources(String)
21 | webjarsPath(webjarsPath() + "/");
22 |
23 | //Adding custom AssetPathCollector
24 | AssetPathCollector[] webSphereAssetPathCollectors = new AssetPathCollector[] {
25 | new WebSphereClasspathAssetPathCollector(),
26 | new VfsAssetPathCollector(),
27 | new FileAssetPathCollector(webjarsPath())
28 | };
29 |
30 | assetPathCollectors(webSphereAssetPathCollectors);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/settings/WebjarsSettings.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.settings;
2 |
3 | import java.time.Duration;
4 | import java.util.Arrays;
5 | import java.util.regex.Pattern;
6 |
7 | import org.apache.wicket.util.lang.Args;
8 |
9 | import de.agilecoders.wicket.webjars.collectors.AssetPathCollector;
10 | import de.agilecoders.wicket.webjars.collectors.ClasspathAssetPathCollector;
11 | import de.agilecoders.wicket.webjars.collectors.FileAssetPathCollector;
12 | import de.agilecoders.wicket.webjars.collectors.VfsAssetPathCollector;
13 | import de.agilecoders.wicket.webjars.util.Helper;
14 | import de.agilecoders.wicket.webjars.util.WebJarAssetLocator;
15 |
16 | /**
17 | * default {@link de.agilecoders.wicket.webjars.settings.IWebjarsSettings} implementation.
18 | *
19 | * @author miha
20 | */
21 | public class WebjarsSettings implements IWebjarsSettings {
22 |
23 | /**
24 | * The default base url of the WebJars CDN.
25 | */
26 | private static final String DEFAULT_WEBJAR_CDN = "//cdn.jsdelivr.net:80/webjars/org.webjars";
27 |
28 | private Duration readFromCacheTimeout;
29 | private ResourceStreamProvider resourceStreamProvider;
30 | private String recentVersionPlaceHolder;
31 | private AssetPathCollector[] assetPathCollectors;
32 | private String webjarsPackage;
33 | private String webjarsPath;
34 | private Pattern resourcePattern;
35 | private Pattern webjarsPathPattern;
36 | private boolean useCdnResources;
37 | private String cdnUrl;
38 |
39 | /**
40 | * Construct.
41 | */
42 | public WebjarsSettings() {
43 | this.resourceStreamProvider = ResourceStreamProvider.bestFitting();
44 | this.webjarsPackage = "META-INF.resources.webjars";
45 | this.webjarsPath = this.webjarsPackage.replace('.', '/');
46 | this.resourcePattern = Pattern.compile("META-INF/resources/webjars/.*");
47 | //META-INF/resources/webjars/projectname/
48 | this.webjarsPathPattern = Pattern.compile(Helper.PATH_PREFIX + "([^\\/]*)\\/([^\\/]*)\\/(.*)");
49 | this.recentVersionPlaceHolder = "current";
50 | this.readFromCacheTimeout = Duration.ofSeconds(3);
51 | this.useCdnResources = false;
52 | this.cdnUrl = DEFAULT_WEBJAR_CDN;
53 |
54 | this.assetPathCollectors = new AssetPathCollector[] {
55 | new ClasspathAssetPathCollector(),
56 | new VfsAssetPathCollector(),
57 | new FileAssetPathCollector(webjarsPath)
58 | };
59 | }
60 |
61 | @Override
62 | public ResourceStreamProvider resourceStreamProvider() {
63 | return resourceStreamProvider;
64 | }
65 |
66 | @Override
67 | public AssetPathCollector[] assetPathCollectors() {
68 | return assetPathCollectors;
69 | }
70 |
71 | @Override
72 | public String webjarsPackage() {
73 | return webjarsPackage;
74 | }
75 |
76 | @Override
77 | public String webjarsPath() {
78 | return webjarsPath;
79 | }
80 |
81 | @Override
82 | public ClassLoader[] classLoaders() {
83 | return new ClassLoader[] {
84 | Thread.currentThread().getContextClassLoader(),
85 | WebJarAssetLocator.class.getClassLoader(),
86 | getClass().getClassLoader()
87 | };
88 | }
89 |
90 | @Override
91 | public Pattern resourcePattern() {
92 | return resourcePattern;
93 | }
94 |
95 | @Override
96 | public Pattern webjarsPathPattern() {
97 | return webjarsPathPattern;
98 | }
99 |
100 | @Override
101 | public String recentVersionPlaceHolder() {
102 | return recentVersionPlaceHolder;
103 | }
104 |
105 | @Override
106 | public Duration readFromCacheTimeout() {
107 | return readFromCacheTimeout;
108 | }
109 |
110 | @Override
111 | public boolean useCdnResources() {
112 | return useCdnResources;
113 | }
114 |
115 | @Override
116 | public String cdnUrl() {
117 | return cdnUrl;
118 | }
119 |
120 | public WebjarsSettings readFromCacheTimeout(Duration readFromCacheTimeout) {
121 | this.readFromCacheTimeout = readFromCacheTimeout;
122 | return this;
123 | }
124 |
125 | public WebjarsSettings recentVersionPlaceHolder(String recentVersionPlaceHolder) {
126 | this.recentVersionPlaceHolder = recentVersionPlaceHolder;
127 | return this;
128 | }
129 |
130 | public WebjarsSettings resourcePattern(Pattern resourcePattern) {
131 | this.resourcePattern = resourcePattern;
132 | return this;
133 | }
134 |
135 | public WebjarsSettings webjarsPath(String webjarsPath) {
136 | this.webjarsPath = Args.notEmpty(webjarsPath, "webjarsPath");
137 | return this;
138 | }
139 |
140 | public WebjarsSettings webjarsPackage(String webjarsPackage) {
141 | this.webjarsPackage = Args.notEmpty(webjarsPackage, "webjarsPackage");
142 | return this;
143 | }
144 |
145 | public WebjarsSettings resourceStreamProvider(ResourceStreamProvider resourceStreamProvider) {
146 | this.resourceStreamProvider = Args.notNull(resourceStreamProvider, "resourceStreamProvider");
147 | return this;
148 | }
149 |
150 | public WebjarsSettings assetPathCollectors(AssetPathCollector... assetPathCollectors) {
151 | this.assetPathCollectors = Args.notNull(assetPathCollectors, "assetPathCollectors");
152 | return this;
153 | }
154 |
155 | public WebjarsSettings useCdnResources(boolean useCdnResources) {
156 | this.useCdnResources = useCdnResources;
157 | return this;
158 | }
159 |
160 | public WebjarsSettings cdnUrl(String cdnUrl) {
161 | this.cdnUrl = cdnUrl;
162 | return this;
163 | }
164 |
165 | @Override
166 | public String toString() {
167 | return "WebjarsSettings{" +
168 | "readFromCacheTimeout=" + readFromCacheTimeout +
169 | ", resourceStreamProvider=" + resourceStreamProvider +
170 | ", recentVersionPlaceHolder='" + recentVersionPlaceHolder + '\'' +
171 | ", assetPathCollectors=" + Arrays.toString(assetPathCollectors) +
172 | ", webjarsPackage='" + webjarsPackage + '\'' +
173 | ", webjarsPath='" + webjarsPath + '\'' +
174 | ", resourcePattern=" + resourcePattern +
175 | ", webjarsPathPattern=" + webjarsPathPattern +
176 | ", useCdnResources=" + useCdnResources +
177 | ", cdnUrl='" + cdnUrl + '\'' +
178 | '}';
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/util/ClassLoaderResourceStreamProvider.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.util;
2 |
3 | import org.apache.wicket.util.io.IOUtils;
4 | import org.apache.wicket.util.resource.AbstractResourceStream;
5 | import org.apache.wicket.util.resource.IFixedLocationResourceStream;
6 | import org.apache.wicket.util.resource.IResourceStream;
7 | import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import java.io.IOException;
12 | import java.io.InputStream;
13 |
14 | /**
15 | * Loads a resource by calling {@link ClassLoader#getResourceAsStream(String)}
16 | *
17 | * @author miha
18 | */
19 | public class ClassLoaderResourceStreamProvider implements IResourceStreamProvider {
20 | private static final Logger LOG = LoggerFactory.getLogger("wicket-webjars");
21 |
22 | private final ClassLoader[] classLoaders;
23 |
24 | /**
25 | * Construct.
26 | *
27 | * @param classLoaders the class loaders to use to find/load resources
28 | */
29 | public ClassLoaderResourceStreamProvider(ClassLoader... classLoaders) {
30 | this.classLoaders = classLoaders;
31 | }
32 |
33 | @Override
34 | public IResourceStream newResourceStream(String path) {
35 | for (ClassLoader loader : classLoaders) {
36 | try {
37 | InputStream resource = loader.getResourceAsStream(path);
38 |
39 | if (resource != null) {
40 | return new InputStreamResourceStream(path, resource);
41 | }
42 | } catch (RuntimeException e) {
43 | LOG.warn("can't load resource: {}", e.getMessage());
44 | }
45 | }
46 |
47 | return null;
48 | }
49 |
50 | private static final class InputStreamResourceStream extends AbstractResourceStream implements
51 | IFixedLocationResourceStream {
52 |
53 | private final String path;
54 | private final InputStream inputStream;
55 |
56 | private InputStreamResourceStream(String path, InputStream inputStream) {
57 | this.path = path;
58 | this.inputStream = inputStream;
59 | }
60 |
61 | @Override
62 | public String locationAsString() {
63 | return path;
64 | }
65 |
66 | @Override
67 | public InputStream getInputStream() throws ResourceStreamNotFoundException {
68 | return inputStream;
69 | }
70 |
71 | @Override
72 | public void close() throws IOException {
73 | IOUtils.closeQuietly(inputStream);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/util/ClasspathUrlStreamHandler.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.util;
2 |
3 | import java.io.IOException;
4 | import java.net.URL;
5 | import java.net.URLConnection;
6 | import java.net.URLStreamHandler;
7 |
8 | /**
9 | * A {@link URLStreamHandler} that handles resources on the classpath.
10 | *
11 | * @author miha
12 | */
13 | public class ClasspathUrlStreamHandler extends URLStreamHandler {
14 |
15 | /**
16 | * The classloaders to find resources from.
17 | */
18 | private final ClassLoader[] classLoaders;
19 |
20 | /**
21 | * Construct.
22 | *
23 | * @param classLoaders The classloaders to find resources from.
24 | */
25 | public ClasspathUrlStreamHandler(ClassLoader... classLoaders) {
26 | this.classLoaders = classLoaders;
27 | }
28 |
29 | @Override
30 | protected URLConnection openConnection(URL url) throws IOException {
31 | for (ClassLoader classLoader : classLoaders) {
32 | final URL resourceUrl = classLoader.getResource(url.getPath());
33 |
34 | if (resourceUrl != null) {
35 | return resourceUrl.openConnection();
36 | }
37 | }
38 |
39 | throw new IOException("can't find resource with url: " + url);
40 | }
41 | }
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/util/Helper.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.util;
2 |
3 | import org.apache.wicket.util.lang.Args;
4 | import org.apache.wicket.util.string.Strings;
5 |
6 | /**
7 | * some helper methods
8 | *
9 | * @author miha
10 | */
11 | public final class Helper {
12 |
13 | public static final String PATH_PREFIX = "/webjars/";
14 |
15 | /**
16 | * prepends the webjars path if missing
17 | *
18 | * @param path the file name to check
19 | * @return file name that starts with "/webjars/"
20 | */
21 | public static String prependWebjarsPathIfMissing(final String path) {
22 | final String cleanedName = prependLeadingSlash(Args.notEmpty(path, "path"));
23 |
24 | if (!path.contains(PATH_PREFIX)) {
25 | return "/webjars" + cleanedName;
26 | }
27 |
28 | return path;
29 | }
30 |
31 | /**
32 | * prepends a leading slash if there is none.
33 | *
34 | * @param path the path
35 | * @return path with leading slash
36 | * @deprecated Use {@link #prependLeadingSlash(String)}
37 | */
38 | @Deprecated
39 | public static String appendLeadingSlash(final String path) {
40 | return prependLeadingSlash(path);
41 | }
42 |
43 | /**
44 | * prepends a leading slash if there is none.
45 | *
46 | * @param path the path
47 | * @return path with leading slash
48 | */
49 | public static String prependLeadingSlash(final String path) {
50 | return path.charAt(0) == '/' ? path : '/' + path;
51 | }
52 |
53 | /**
54 | * Removes the leading slash if there is one.
55 | *
56 | * @param path the path
57 | * @return path without leading slash
58 | */
59 | public static String removeLeadingSlash(final String path) {
60 | return path.charAt(0) == '/' ? path.substring(1) : path;
61 | }
62 |
63 | /**
64 | * Make paths like aa/bb/cc = cc/bb/aa.
65 | *
66 | * @param assetPath the path to revert
67 | * @return reverted path
68 | */
69 | public static String reversePath(String assetPath) {
70 | final String[] assetPathComponents = Strings.split(assetPath, '/');
71 | final StringBuilder reversedAssetPath = new StringBuilder();
72 | for (int i = assetPathComponents.length - 1; i >= 0; --i) {
73 | if (reversedAssetPath.length() > 0) {
74 | reversedAssetPath.append('/');
75 | }
76 | reversedAssetPath.append(assetPathComponents[i]);
77 | }
78 | return reversedAssetPath.toString();
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/util/IFullPathProvider.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.util;
2 |
3 | /**
4 | * full path to a webjars provider
5 | *
6 | * @author miha
7 | */
8 | public interface IFullPathProvider {
9 | /**
10 | * Given a distinct path within the WebJar index passed in return the full path of the resource.
11 | *
12 | * @param partialPath the path to return e.g. "jquery.js" or "abc/someother.js". This must be a distinct path within the index passed in.
13 | * @return a fully qualified path to the resource.
14 | */
15 | String getFullPath(String partialPath);
16 | }
17 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/util/IResourceStreamProvider.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.util;
2 |
3 | import org.apache.wicket.util.resource.IResourceStream;
4 |
5 | /**
6 | * Creates a new {@link org.apache.wicket.util.resource.IResourceStream} that points to a given path.
7 | *
8 | * @author miha
9 | */
10 | public interface IResourceStreamProvider {
11 |
12 | /**
13 | * Creates a new {@link org.apache.wicket.util.resource.IResourceStream} that points to a given path.
14 | *
15 | * @param path the path to load
16 | * @return new {@link org.apache.wicket.util.resource.IResourceStream} instance
17 | */
18 | IResourceStream newResourceStream(final String path);
19 | }
20 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/util/RecentVersionCallable.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.util;
2 |
3 | import java.util.concurrent.Callable;
4 | import java.util.concurrent.FutureTask;
5 |
6 | import de.agilecoders.wicket.webjars.WicketWebjars;
7 | import de.agilecoders.wicket.webjars.collectors.AssetsMap;
8 |
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | /**
13 | * Callable that loads the recent version string of given webjars resource
14 | *
15 | * @author miha
16 | */
17 | public class RecentVersionCallable implements Callable {
18 |
19 | private static final Logger LOG = LoggerFactory.getLogger(RecentVersionCallable.class);
20 |
21 | /**
22 | * creates a new future recent version collector
23 | *
24 | * @param partialPath the resource path
25 | * @return recent version as future
26 | */
27 | public static FutureTask createFutureTask(final String partialPath) {
28 | return new FutureTask<>(new RecentVersionCallable(partialPath));
29 | }
30 |
31 | private final String partialPath;
32 |
33 | /**
34 | * Construct.
35 | *
36 | * @param partialPath Path to webjars resource
37 | */
38 | private RecentVersionCallable(final String partialPath) {
39 | this.partialPath = partialPath;
40 | }
41 |
42 | @Override
43 | public String call() throws Exception {
44 | return collectRecentVersionFor(partialPath);
45 | }
46 |
47 | /**
48 | * collects recent version string of given webjars resource from classpath.
49 | *
50 | * @param partialPath The webjars resource path
51 | * @return recent version string
52 | */
53 | private static String collectRecentVersionFor(final String partialPath) {
54 | return Holder.recentVersionProvider.findRecentVersionFor(partialPath);
55 | }
56 |
57 | static final class Holder {
58 | static final AssetsMap recentVersionProvider = new AssetsMap(WicketWebjars.settings());
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/util/UrlResourceStreamProvider.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.util;
2 |
3 | import org.apache.wicket.core.util.resource.UrlResourceStream;
4 | import org.apache.wicket.util.resource.IResourceStream;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import java.net.MalformedURLException;
9 | import java.net.URL;
10 |
11 | /**
12 | * Loads a resource by using {@link de.agilecoders.wicket.webjars.util.ClasspathUrlStreamHandler}.
13 | *
14 | * @author miha
15 | */
16 | public class UrlResourceStreamProvider implements IResourceStreamProvider {
17 | private static final Logger LOG = LoggerFactory.getLogger("wicket-webjars");
18 |
19 | private final ClasspathUrlStreamHandler urlHandler;
20 |
21 | public UrlResourceStreamProvider(ClassLoader... classLoaders) {
22 | this.urlHandler = new ClasspathUrlStreamHandler(classLoaders);
23 | }
24 |
25 | @Override
26 | public IResourceStream newResourceStream(String path) {
27 | try {
28 | return new UrlResourceStream(new URL(null, "classpath:" + path, urlHandler));
29 | } catch (MalformedURLException e) {
30 | LOG.warn("can't create URL to resource: {}", e.getMessage());
31 | }
32 |
33 | return null;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/util/WebJarAssetLocator.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.util;
2 |
3 | import de.agilecoders.wicket.webjars.collectors.AssetsMap;
4 | import de.agilecoders.wicket.webjars.collectors.IAssetProvider;
5 | import de.agilecoders.wicket.webjars.settings.IWebjarsSettings;
6 | import org.apache.wicket.util.lang.Args;
7 |
8 | import java.io.PrintStream;
9 | import java.io.PrintWriter;
10 | import java.util.Iterator;
11 | import java.util.Map.Entry;
12 | import java.util.Set;
13 | import java.util.SortedMap;
14 |
15 | import static de.agilecoders.wicket.webjars.util.Helper.reversePath;
16 |
17 | /**
18 | * Locate WebJar assets. The class is thread safe.
19 | */
20 | public class WebJarAssetLocator implements IAssetProvider, IFullPathProvider {
21 | private final AssetsMap assetMap;
22 | private final String recentVersionPlaceHolder;
23 |
24 | /**
25 | * Convenience constructor that will form a locator for all resources on the current class path.
26 | */
27 | public WebJarAssetLocator(final IWebjarsSettings settings) {
28 | this.assetMap = RecentVersionCallable.Holder.recentVersionProvider;
29 | this.recentVersionPlaceHolder = "/" + settings.recentVersionPlaceHolder() + "/";
30 | }
31 |
32 | private String throwNotFoundException(final String partialPath) {
33 | throw new ResourceException(partialPath, partialPath + " could not be found. Make sure you've added the "
34 | + "corresponding WebJar and please check for typos.");
35 | }
36 |
37 | private String throwMultipleMatchesException(final String partialPath) {
38 | throw new ResourceException(partialPath, "Multiple matches found for " + partialPath
39 | + ". Please provide a more specific path, for example by including a version number.");
40 | }
41 |
42 | @Override
43 | public String getFullPath(String partialPath) {
44 | partialPath = Args.notEmpty(partialPath, "partialPath").contains(recentVersionPlaceHolder) ?
45 | partialPath.replace(recentVersionPlaceHolder,
46 | "/" + assetMap.findRecentVersionFor(partialPath) + "/") :
47 | partialPath;
48 |
49 | final String reversePartialPath = reversePath(partialPath);
50 | final SortedMap fullPathTail = assetMap.getFullPathIndex().tailMap(reversePartialPath);
51 |
52 | if (fullPathTail.size() == 0) {
53 | throwNotFoundException(partialPath);
54 | }
55 |
56 | final Iterator> fullPathTailIter = fullPathTail.entrySet().iterator();
57 | final Entry fullPathEntry = fullPathTailIter.next();
58 | if (!fullPathEntry.getKey().startsWith(reversePartialPath)) {
59 | throwNotFoundException(partialPath);
60 | }
61 | final String fullPath = fullPathEntry.getValue();
62 |
63 | if (fullPathTailIter.hasNext() && fullPathTailIter.next().getKey().startsWith(reversePartialPath)) {
64 | throwMultipleMatchesException(reversePartialPath);
65 | }
66 |
67 | return fullPath;
68 | }
69 |
70 | @Override
71 | public SortedMap getFullPathIndex() {
72 | return assetMap.getFullPathIndex();
73 | }
74 |
75 | @Override
76 | public Set listAssets(final String folderPath) {
77 | return assetMap.listAssets(folderPath);
78 | }
79 |
80 | public void reindex() {
81 | assetMap.reindex();
82 | }
83 |
84 | /**
85 | * resource exception without stacktrace.
86 | */
87 | public static class ResourceException extends RuntimeException {
88 |
89 | private final String resource;
90 |
91 | /**
92 | * Construct.
93 | *
94 | * @param resource the resource
95 | * @param message error message
96 | */
97 | public ResourceException(String resource, String message) {
98 | super(message);
99 |
100 | this.resource = resource;
101 | }
102 |
103 | public String resource() {
104 | return resource;
105 | }
106 |
107 | @Override
108 | public synchronized Throwable getCause() {
109 | return null;
110 | }
111 |
112 | @Override
113 | public synchronized Throwable initCause(Throwable cause) {
114 | return this;
115 | }
116 |
117 | @Override
118 | public void printStackTrace() {
119 | // nothing to do
120 | }
121 |
122 | @Override
123 | public void printStackTrace(PrintStream s) {
124 | printStackTrace();
125 | }
126 |
127 | @Override
128 | public void printStackTrace(PrintWriter s) {
129 | printStackTrace();
130 | }
131 |
132 | @Override
133 | public synchronized Throwable fillInStackTrace() {
134 | return this;
135 | }
136 |
137 | @Override
138 | public StackTraceElement[] getStackTrace() {
139 | return new StackTraceElement[] { };
140 | }
141 |
142 | @Override
143 | public void setStackTrace(StackTraceElement[] stackTrace) {
144 | // nothing to do
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/util/WebjarsVersion.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.util;
2 |
3 |
4 | import de.agilecoders.wicket.webjars.WicketWebjars;
5 | import de.agilecoders.wicket.webjars.settings.IWebjarsSettings;
6 | import org.apache.wicket.util.lang.Args;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import java.time.Duration;
11 | import java.util.concurrent.ConcurrentHashMap;
12 | import java.util.concurrent.ConcurrentMap;
13 | import java.util.concurrent.ExecutionException;
14 | import java.util.concurrent.FutureTask;
15 | import java.util.concurrent.TimeUnit;
16 | import java.util.concurrent.TimeoutException;
17 |
18 |
19 | /**
20 | * Collects recent versions of webjars resources.
21 | *
22 | * @author miha
23 | */
24 | public final class WebjarsVersion {
25 | private static final Logger LOG = LoggerFactory.getLogger(WicketWebjars.class);
26 | private static final ConcurrentMap> VERSIONS_CACHE = new ConcurrentHashMap<>();
27 |
28 | private static final class Holder {
29 | private static final IWebjarsSettings settings = WicketWebjars.settings();
30 |
31 | private static final String recentVersionPattern = Helper.PATH_PREFIX + "[^/]*/" + settings.recentVersionPlaceHolder() + "/.*";
32 | private static final String replacePattern = "/" + settings.recentVersionPlaceHolder() + "/";
33 | private static final Duration timeout = settings.readFromCacheTimeout();
34 | }
35 |
36 | /**
37 | * replaces the version string "current" with the recent available version
38 | *
39 | * @param path the full resource path
40 | * @return The version of webjars resource
41 | */
42 | public static String useRecent(String path) {
43 | Args.notEmpty(path, "path");
44 |
45 | if (path.matches(Holder.recentVersionPattern)) {
46 | return path.replaceFirst(Holder.replacePattern, "/" + recentVersion(path) + "/");
47 | }
48 |
49 | return path;
50 | }
51 |
52 | /**
53 | * returns recent version of given dependency (from internal versions cache)
54 | *
55 | * @param partialPath the path of dependency
56 | * @return recent version
57 | */
58 | public static String recentVersion(final String partialPath) {
59 | if (!VERSIONS_CACHE.containsKey(partialPath)) {
60 | final FutureTask futureTask = RecentVersionCallable.createFutureTask(partialPath);
61 | final FutureTask prevFutureTask = VERSIONS_CACHE.putIfAbsent(partialPath, futureTask);
62 |
63 | if (prevFutureTask == null) {
64 | futureTask.run();
65 | }
66 | }
67 |
68 | try {
69 | return VERSIONS_CACHE.get(partialPath).get(Holder.timeout.toMillis(), TimeUnit.MILLISECONDS);
70 | } catch (InterruptedException | ExecutionException | TimeoutException e) {
71 | LOG.error("can't collect recent version of {}; {}", partialPath, e.getMessage());
72 | }
73 |
74 | throw new WebJarAssetLocator.ResourceException(partialPath, "there is no webjars dependency for: " +
75 | partialPath);
76 | }
77 |
78 | public static void reset() {
79 | VERSIONS_CACHE.clear();
80 | }
81 |
82 | private WebjarsVersion() {
83 | throw new UnsupportedOperationException();
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/library/src/main/java/de/agilecoders/wicket/webjars/util/file/WebjarsResourceFinder.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.util.file;
2 |
3 | import de.agilecoders.wicket.webjars.request.resource.IWebjarsResourceReference;
4 | import de.agilecoders.wicket.webjars.settings.IWebjarsSettings;
5 | import de.agilecoders.wicket.webjars.util.Helper;
6 | import de.agilecoders.wicket.webjars.util.IFullPathProvider;
7 | import de.agilecoders.wicket.webjars.util.IResourceStreamProvider;
8 | import de.agilecoders.wicket.webjars.util.WebJarAssetLocator;
9 | import de.agilecoders.wicket.webjars.util.WebjarsVersion;
10 |
11 | import org.apache.wicket.util.file.IResourceFinder;
12 | import org.apache.wicket.util.resource.IResourceStream;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 |
16 | /**
17 | * Knows how to find webjars resources.
18 | *
19 | * @author miha
20 | */
21 | public class WebjarsResourceFinder implements IResourceFinder {
22 | private static final Logger LOG = LoggerFactory.getLogger("wicket-webjars");
23 |
24 | private final IFullPathProvider locator;
25 | private final IResourceStreamProvider resourceStreamProvider;
26 | private final IWebjarsSettings settings;
27 | private final int hashCode;
28 |
29 | /**
30 | * Construct.
31 | *
32 | * @param settings the webjars settings to use
33 | */
34 | public WebjarsResourceFinder(IWebjarsSettings settings) {
35 | this.settings = settings;
36 | this.locator = newFullPathProvider();
37 | this.resourceStreamProvider = settings.resourceStreamProvider().newInstance(settings.classLoaders());
38 |
39 | int _hashCode = locator != null ? locator.hashCode() : 0;
40 | this.hashCode = 31 * (_hashCode + settings.hashCode());
41 | }
42 |
43 | public void reindex() {
44 | if (locator instanceof WebJarAssetLocator) {
45 | WebJarAssetLocator webJarAssetLocator = (WebJarAssetLocator) locator;
46 | webJarAssetLocator.reindex();
47 | }
48 | }
49 |
50 | /**
51 | * @return new resource locator instance
52 | */
53 | protected IFullPathProvider newFullPathProvider() {
54 | return new WebJarAssetLocator(settings);
55 | }
56 |
57 | /**
58 | * Looks for a given path name along the webjars root path
59 | *
60 | * @param clazz The class requesting the resource stream
61 | * @param pathName The filename with possible path
62 | * @return The resource stream
63 | */
64 | @Override
65 | public IResourceStream find(final Class> clazz, final String pathName) {
66 | IResourceStream stream = null;
67 |
68 | if (clazz != null && IWebjarsResourceReference.class.isAssignableFrom(clazz)) {
69 | // pathname as extracted by wicket is a classpath resource path with no leading '/'
70 | // historically, webjars file locator works with /webjars/ prefixed path
71 | // prepend '/' and resolve version if needed
72 | String versionnedName = "/" + pathName;
73 | versionnedName = WebjarsVersion.useRecent(versionnedName);
74 | final int pos = versionnedName != null ? versionnedName.lastIndexOf(Helper.PATH_PREFIX) : -1;
75 |
76 | if (pos > -1) {
77 | try {
78 | final String webjarsPath = locator.getFullPath(versionnedName.substring(pos));
79 |
80 | LOG.debug("webjars path: {}", webjarsPath);
81 |
82 | stream = newResourceStream(webjarsPath);
83 | } catch (Exception e) {
84 | LOG.debug("can't locate resource for: {} (actual name {}); {}", pathName, versionnedName, e.getMessage());
85 | }
86 |
87 | if (stream == null) {
88 | LOG.debug("there is no webjars resource for: {} (actual name {})", pathName, versionnedName);
89 | }
90 | }
91 | }
92 |
93 | return stream;
94 | }
95 |
96 | /**
97 | * creates a new {@link IResourceStream} for given resource path with should be loaded by given
98 | * class loader.
99 | *
100 | * @param webjarsPath The resource to load
101 | * @return new {@link IResourceStream} instance that represents the content of given resource path or
102 | * null if resource wasn't found
103 | */
104 | protected IResourceStream newResourceStream(final String webjarsPath) {
105 | return resourceStreamProvider.newResourceStream(webjarsPath);
106 | }
107 |
108 | @Override
109 | public boolean equals(Object o) {
110 | if (this == o) {
111 | return true;
112 | }
113 | if (o == null || getClass() != o.getClass()) {
114 | return false;
115 | }
116 |
117 | WebjarsResourceFinder that = (WebjarsResourceFinder) o;
118 |
119 | if (locator != null ? !locator.equals(that.locator) : that.locator != null) {
120 | return false;
121 | }
122 | if (settings != null ? !settings.equals(that.settings) : that.settings != null) {
123 | return false;
124 | }
125 |
126 | return true;
127 | }
128 |
129 | @Override
130 | public int hashCode() {
131 | return hashCode;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/library/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | module de.agilecoders.wicket.webjars {
2 | exports de.agilecoders.wicket.webjars;
3 | exports de.agilecoders.wicket.webjars.collectors;
4 | exports de.agilecoders.wicket.webjars.request;
5 | exports de.agilecoders.wicket.webjars.request.resource;
6 | exports de.agilecoders.wicket.webjars.settings;
7 | exports de.agilecoders.wicket.webjars.util;
8 | exports de.agilecoders.wicket.webjars.util.file;
9 |
10 | requires org.apache.wicket.core;
11 | requires org.apache.wicket.request;
12 | requires org.apache.wicket.util;
13 |
14 | requires static emory.util.classloader;
15 |
16 | requires org.slf4j;
17 | }
18 |
--------------------------------------------------------------------------------
/library/src/test/java/de/agilecoders/wicket/webjars/WicketWebjarsTest.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars;
2 |
3 | import static org.hamcrest.CoreMatchers.is;
4 | import static org.hamcrest.MatcherAssert.assertThat;
5 |
6 | import org.apache.wicket.protocol.http.WebApplication;
7 | import org.apache.wicket.util.tester.WicketTester;
8 | import org.junit.jupiter.api.AfterEach;
9 | import org.junit.jupiter.api.Assertions;
10 | import org.junit.jupiter.api.BeforeEach;
11 | import org.junit.jupiter.api.Test;
12 |
13 | class WicketWebjarsTest extends Assertions {
14 |
15 | private WicketTester tester;
16 |
17 | @BeforeEach
18 | void setUp() throws Exception {
19 | tester = new WicketTester();
20 | }
21 |
22 | @AfterEach
23 | void tearDown() throws Exception {
24 | tester.destroy();
25 | }
26 |
27 | @Test
28 | public void isInstalled() throws Exception {
29 | WebApplication application = tester.getApplication();
30 |
31 |
32 | assertThat(WicketWebjars.isInstalled(application), is(false));
33 |
34 | WicketWebjars.install(application);
35 | assertThat(WicketWebjars.isInstalled(application), is(true));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/library/src/test/java/de/agilecoders/wicket/webjars/collectors/AssetsMapTest.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.collectors;
2 |
3 | import static org.hamcrest.CoreMatchers.equalTo;
4 | import static org.hamcrest.CoreMatchers.is;
5 | import static org.hamcrest.MatcherAssert.assertThat;
6 |
7 | import java.util.HashSet;
8 | import java.util.Map;
9 | import java.util.Set;
10 | import java.util.SortedMap;
11 | import java.util.TreeMap;
12 |
13 | import org.junit.jupiter.api.Assertions;
14 | import org.junit.jupiter.api.Test;
15 |
16 | import de.agilecoders.wicket.webjars.settings.WebjarsSettings;
17 |
18 | public class AssetsMapTest extends Assertions {
19 |
20 | /**
21 | * https://github.com/l0rdn1kk0n/wicket-webjars/issues/22
22 | *
23 | * Parse the version of the correct asset when there is an asset
24 | * with a similar name but with a prefix
25 | */
26 | @Test
27 | public void correctVersion()
28 | {
29 | AssetsMap assetsMap = new AssetsMap(new WebjarsSettings()) {
30 | @Override
31 | public Set listAssets(String folderPath) {
32 | Set assets = new HashSet();
33 | assets.add("/webjars/realname/3.0.0/prefix.realname.js");
34 | assets.add("/webjars/realname/2.0.0/realname.js");
35 | return assets;
36 | }
37 | };
38 | String versionFor = assetsMap.findRecentVersionFor("realname/current/realname.js");
39 | assertThat(versionFor, is(equalTo("2.0.0")));
40 | }
41 |
42 | /**
43 | * https://github.com/martin-g/wicket-webjars/issues/167
44 | *
45 | * Matching was done on partial path-component, so bootstrap4 resources matched bootstrap resource lookup.
46 | */
47 | @Test
48 | public void partialPathMatching() {
49 | AssetsMap assetsMap = new AssetsMap(new WebjarsSettings()) {
50 | public SortedMap getFullPathIndex() {
51 | // only values are significant for the use case
52 | // same versions must be used to triggers the issue as versions are put in a Set and first version
53 | // is retrieved.
54 | return new TreeMap<>(Map.of(
55 | "0", "META-INF/resources/webjars/bootstrap4/4.6.0/matching.js",
56 | "1", "META-INF/resources/webjars/bootstrap/5.3.2/matching.js"
57 | ));
58 | }
59 | };
60 | String versionFor = assetsMap.findRecentVersionFor("/bootstrap/current/matching.js");
61 | // bootstrap4 must not match
62 | assertThat(versionFor, is(equalTo("5.3.2")));
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/library/src/test/java/de/agilecoders/wicket/webjars/util/file/WebjarsResourceFinderTest.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket.webjars.util.file;
2 |
3 | import org.apache.wicket.mock.MockApplication;
4 | import org.apache.wicket.protocol.http.WebApplication;
5 | import org.apache.wicket.util.io.IOUtils;
6 | import org.apache.wicket.util.resource.IResourceStream;
7 | import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
8 | import org.apache.wicket.util.tester.WicketTestCase;
9 |
10 | import java.io.IOException;
11 |
12 | import de.agilecoders.wicket.webjars.WicketWebjars;
13 | import de.agilecoders.wicket.webjars.request.resource.IWebjarsResourceReference;
14 | import org.junit.jupiter.api.Test;
15 |
16 | import static de.agilecoders.wicket.webjars.util.WebjarsVersion.useRecent;
17 | import static org.hamcrest.MatcherAssert.assertThat;
18 | import static org.hamcrest.Matchers.is;
19 | import static org.hamcrest.Matchers.not;
20 | import static org.hamcrest.Matchers.nullValue;
21 | import static org.hamcrest.Matchers.startsWith;
22 | import static org.junit.jupiter.api.Assertions.assertNull;
23 |
24 | /**
25 | * Tests for WebjarsResourceFinder
26 | */
27 | public class WebjarsResourceFinderTest extends WicketTestCase {
28 |
29 | /**
30 | * https://github.com/l0rdn1kk0n/wicket-bootstrap/issues/280
31 | *
32 | * Return {@code null} for missing resources
33 | */
34 | @Test
35 | public void findNonExistingFile() {
36 | WebjarsResourceFinder finder = new WebjarsResourceFinder(WicketWebjars.settings());
37 |
38 | assertNull(finder.find(String.class, "non existing"));
39 | }
40 |
41 | /**
42 | * https://github.com/l0rdn1kk0n/wicket-webjars/issues/20
43 | *
44 | * Return {@code null} for missing resources
45 | */
46 | @Test
47 | public void findWithNullScope() {
48 | WebjarsResourceFinder finder = new WebjarsResourceFinder(WicketWebjars.settings());
49 |
50 | assertNull(finder.find(null, "non existing"));
51 | }
52 |
53 | @Test
54 | public void findOnGAE() throws ResourceStreamNotFoundException, IOException {
55 | System.setProperty("com.google.appengine.runtime.environment", "Production");
56 |
57 | WebjarsResourceFinder finder = new WebjarsResourceFinder(WicketWebjars.settings());
58 | IResourceStream stream = finder.find(IWebjarsResourceReference.class, "/webjars/jquery/3.7.1/jquery.min.js");
59 |
60 | System.setProperty("com.google.appengine.runtime.environment", "");
61 |
62 | assertThat(stream, is(not(nullValue())));
63 | assertThat(IOUtils.toString(stream.getInputStream()), startsWith("/*! jQuery v3.7.1"));
64 | }
65 |
66 | @Test
67 | public void findFile() throws ResourceStreamNotFoundException, IOException {
68 | WebjarsResourceFinder finder = new WebjarsResourceFinder(WicketWebjars.settings());
69 | IResourceStream stream = finder.find(IWebjarsResourceReference.class, "/webjars/jquery/3.7.1/jquery.min.js");
70 |
71 | assertThat(stream, is(not(nullValue())));
72 | assertThat(IOUtils.toString(stream.getInputStream()), startsWith("/*! jQuery v3.7.1"));
73 | }
74 |
75 | @Test
76 | public void findFileWithoutVersion() throws ResourceStreamNotFoundException, IOException {
77 | WebjarsResourceFinder finder = new WebjarsResourceFinder(WicketWebjars.settings());
78 | IResourceStream stream = finder.find(IWebjarsResourceReference.class,
79 | useRecent("/webjars/jquery/current/jquery.min.js"));
80 |
81 | assertThat(stream, is(not(nullValue())));
82 | assertThat(IOUtils.toString(stream.getInputStream()), startsWith("/*! jQuery v3.7.1"));
83 | }
84 |
85 | @Override
86 | protected WebApplication newApplication() {
87 | return new MockApplication() {
88 | @Override
89 | protected void init() {
90 | super.init();
91 |
92 | WicketWebjars.install(this);
93 | }
94 | };
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | org.sonatype.oss
7 | oss-parent
8 | 9
9 |
10 |
11 | de.agilecoders.wicket.webjars
12 | wicket-webjars-parent
13 | pom
14 | 4.0.9-SNAPSHOT
15 | wicket-webjars-parent
16 |
17 | https://github.com/l0rdn1kk0n/wicket-webjars
18 |
19 |
20 | git@github.com:l0rdn1kk0n/wicket-webjars.git
21 | scm:git:git@github.com:l0rdn1kk0n/wicket-webjars.git
22 | scm:git:git@github.com:l0rdn1kk0n/wicket-webjars.git
23 |
24 |
25 |
26 | github
27 | https://github.com/l0rdn1kk0n/wicket-webjars/issues
28 |
29 |
30 |
31 | agilecoders.de
32 | http://agilecoders.de
33 |
34 |
35 |
36 | library
37 | samples
38 |
39 |
40 |
41 | github
42 | false
43 |
44 | UTF-8
45 | ${project.build.sourceEncoding}
46 | 17
47 | 10.5.0
48 | 5.0.0
49 | 2.0.17
50 | 5.12.2
51 | 3.0
52 | 2.1
53 | 3.7.1
54 |
55 | 2.18.0
56 | 3.2.7
57 | 3.14.0
58 | 3.11.2
59 | 3.3.1
60 | 3.4.2
61 | 3.1.4
62 | 3.4.0
63 | 3.5.3
64 |
65 |
66 |
67 |
68 |
69 |
70 | de.agilecoders.wicket.webjars
71 | wicket-webjars
72 | ${project.version}
73 |
74 |
75 |
76 |
77 | org.apache.wicket
78 | wicket-core
79 | ${wicket.version}
80 |
81 |
82 |
83 |
84 | org.slf4j
85 | slf4j-api
86 | ${slf4j.version}
87 |
88 |
89 |
90 | jakarta.servlet
91 | jakarta.servlet-api
92 | ${jakarta.servlet-api.version}
93 |
94 |
95 |
96 |
97 | org.apache.wicket
98 | wicket-tester
99 | ${wicket.version}
100 | test
101 |
102 |
103 | org.junit
104 | junit-bom
105 | ${junit-jupiter.version}
106 | pom
107 | import
108 |
109 |
110 | org.junit.jupiter
111 | junit-jupiter-api
112 | ${junit-jupiter.version}
113 | test
114 |
115 |
116 | org.junit.vintage
117 | junit-vintage-engine
118 | ${junit-jupiter.version}
119 | test
120 |
121 |
122 | org.hamcrest
123 | hamcrest
124 | ${hamcrest.version}
125 | test
126 |
127 |
128 |
129 |
130 | edu.emory.mathcs.util
131 | emory-util-classloader
132 | ${emory-util-classloader.version}
133 |
134 |
135 |
136 | org.webjars
137 | jquery
138 | ${jquery.version}
139 | test
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 | false
149 | src/main/resources
150 |
151 |
152 | false
153 | src/main/java
154 |
155 | **
156 |
157 |
158 | **/*.java
159 |
160 |
161 |
162 |
163 |
164 | false
165 | src/test/java
166 |
167 | **
168 |
169 |
170 | **/*.java
171 |
172 |
173 |
174 |
175 |
176 |
177 | org.sonatype.central
178 | central-publishing-maven-plugin
179 | 0.7.0
180 | true
181 |
182 | central
183 |
184 |
185 |
186 | org.apache.maven.plugins
187 | maven-gpg-plugin
188 | ${maven-gpg-plugin.version}
189 |
190 |
191 | sign-artifacts
192 | verify
193 |
194 | sign
195 |
196 |
197 |
198 |
199 |
200 | org.apache.maven.plugins
201 | maven-compiler-plugin
202 | ${maven-compiler-plugin.version}
203 |
204 | ${mvn.build.java.version}
205 | ${project.build.sourceEncoding}
206 | true
207 | true
208 |
209 |
210 |
211 |
212 | org.apache.maven.plugins
213 | maven-javadoc-plugin
214 | ${maven-javadoc-plugin.version}
215 |
216 | false
217 | 128m
218 | 256m
219 | true
220 | true
221 |
222 | https://docs.oracle.com/en/java/javase/17/docs/api/
223 | https://nightlies.apache.org/wicket/apidocs/10.x
224 | https://logback.qos.ch/apidocs
225 |
226 | false
227 | none
228 | ${javadoc.disabled}
229 |
230 |
231 |
232 | attach-javadoc
233 |
234 | jar
235 |
236 |
237 |
238 |
239 |
240 | org.codehaus.mojo
241 | versions-maven-plugin
242 | ${versions-maven-plugin.version}
243 | true
244 |
245 |
246 | org.apache.maven.plugins
247 | maven-source-plugin
248 | ${maven-source-plugin.version}
249 |
250 |
251 | org.apache.maven.plugins
252 | maven-jar-plugin
253 | ${maven-jar-plugin.version}
254 |
255 |
256 |
257 |
258 |
259 | org.apache.maven.plugins
260 | maven-war-plugin
261 | ${maven-war-plugin.version}
262 |
263 |
264 | org.apache.maven.plugins
265 | maven-surefire-plugin
266 | ${maven-surefire-plugin.version}
267 |
268 | false
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 | The Apache Software License, Version 2.0
278 | http://www.apache.org/licenses/LICENSE-2.0.txt
279 | repo
280 |
281 |
282 |
283 |
284 |
285 | miha
286 | Michael Haitz
287 | michael.haitz@agilecoders.de
288 | agilecoders.de
289 |
290 | Owner
291 | Committer
292 |
293 |
294 |
295 | martin-g
296 | Martin Grigorov
297 | mgrigorov@apache.org
298 | Apache Software Organization
299 |
300 | Committer
301 |
302 |
303 |
304 |
305 |
306 |
--------------------------------------------------------------------------------
/samples/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | de.agilecoders.wicket.webjars
7 | wicket-webjars-parent
8 | 4.0.9-SNAPSHOT
9 | ../
10 |
11 |
12 | samples
13 | war
14 |
15 |
16 | 1.14.1
17 | 5.3.5
18 | 2.24.3
19 |
20 |
21 |
22 |
23 | de.agilecoders.wicket.webjars
24 | wicket-webjars
25 |
26 |
27 |
28 | org.apache.wicket
29 | wicket-core
30 |
31 |
32 |
33 | org.webjars
34 | jquery
35 |
36 |
37 |
38 | org.webjars
39 | jquery-ui
40 | ${jquery-ui.version}
41 |
42 |
43 |
44 | org.webjars
45 | bootstrap
46 | ${bootstrap.version}
47 |
48 |
49 |
50 | org.slf4j
51 | slf4j-log4j12
52 | ${slf4j.version}
53 |
54 |
55 |
56 | org.apache.logging.log4j
57 | log4j-core
58 | ${log4j.version}
59 |
60 |
61 |
62 |
63 |
64 |
65 | src/main/resources
66 |
67 |
68 | src/main/java
69 |
70 | **
71 |
72 |
73 | **/*.java
74 |
75 |
76 |
77 |
78 |
79 | src/test/resources
80 |
81 |
82 | src/test/java
83 |
84 | **
85 |
86 |
87 | **/*.java
88 |
89 |
90 |
91 |
92 |
93 | org.apache.maven.plugins
94 | maven-deploy-plugin
95 | ${maven-deploy-plugin.version}
96 |
97 | true
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/samples/src/main/java/de/agilecoders/wicket/HomePage.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Apache Wicket Quickstart
6 |
7 |
8 |
9 |
10 |
11 |
12 |

13 |
14 |
Apache Wicket Webjars
15 |
16 |
17 |
254 |
255 |
256 |
257 |
258 |
--------------------------------------------------------------------------------
/samples/src/main/java/de/agilecoders/wicket/HomePage.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket;
2 |
3 | import de.agilecoders.wicket.webjars.WicketWebjars;
4 | import de.agilecoders.wicket.webjars.request.resource.WebjarsCssResourceReference;
5 | import de.agilecoders.wicket.webjars.request.resource.WebjarsJavaScriptResourceReference;
6 | import org.apache.wicket.markup.head.CssHeaderItem;
7 | import org.apache.wicket.markup.head.IHeaderResponse;
8 | import org.apache.wicket.markup.head.JavaScriptHeaderItem;
9 | import org.apache.wicket.markup.html.WebPage;
10 | import org.apache.wicket.markup.html.link.Link;
11 | import org.apache.wicket.protocol.http.WebApplication;
12 | import org.apache.wicket.request.mapper.parameter.PageParameters;
13 |
14 | public class HomePage extends WebPage {
15 | private static final long serialVersionUID = 1L;
16 |
17 | public HomePage(final PageParameters parameters) {
18 | super(parameters);
19 |
20 | add(new Link("reindex") {
21 | @Override
22 | public void onClick() {
23 | WicketWebjars.reindex(WebApplication.get());
24 | }
25 | });
26 | }
27 |
28 | @Override
29 | public void renderHead(IHeaderResponse response) {
30 | super.renderHead(response);
31 |
32 | response.render(JavaScriptHeaderItem.forReference(new WebjarsJavaScriptResourceReference("jquery/current/jquery.min.js")));
33 | response.render(CssHeaderItem.forReference(new WebjarsCssResourceReference("bootstrap/current/css/bootstrap.css")));
34 | response.render(CssHeaderItem.forReference(new WebjarsCssResourceReference("bootstrap/current/css/bootstrap-theme.css")));
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/samples/src/main/java/de/agilecoders/wicket/WicketApplication.java:
--------------------------------------------------------------------------------
1 | package de.agilecoders.wicket;
2 |
3 | import de.agilecoders.wicket.webjars.WicketWebjars;
4 | import de.agilecoders.wicket.webjars.settings.WebjarsSettings;
5 | import org.apache.wicket.markup.html.WebPage;
6 | import org.apache.wicket.protocol.http.WebApplication;
7 |
8 | /**
9 | * Application object for your web application. If you want to run this application without deploying, run the Start class.
10 | */
11 | public class WicketApplication extends WebApplication {
12 |
13 | @Override
14 | public Class extends WebPage> getHomePage() {
15 | return HomePage.class;
16 | }
17 |
18 | @Override
19 | public void init() {
20 | super.init();
21 |
22 | WebjarsSettings settings = new WebjarsSettings();
23 | settings.useCdnResources(false);
24 |
25 | WicketWebjars.install(this, settings);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/samples/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.appender.Stdout=org.apache.log4j.ConsoleAppender
2 | log4j.appender.Stdout.layout=org.apache.log4j.PatternLayout
3 | log4j.appender.Stdout.layout.conversionPattern=%-5p - %-26.26c{1} - %m\n
4 |
5 | log4j.rootLogger=INFO,Stdout
6 |
7 | log4j.logger.org.apache.wicket=INFO
8 | log4j.logger.org.apache.wicket.protocol.http.HttpSessionStore=INFO
9 | log4j.logger.org.apache.wicket.version=INFO
10 | log4j.logger.org.apache.wicket.RequestCycle=INFO
11 | log4j.logger.de.agilecoders=DEBUG
12 | log4j.logger.wicket-webjars=DEBUG
13 |
14 |
15 |
--------------------------------------------------------------------------------
/samples/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | wicket-webjars-samples
7 |
8 |
18 |
19 |
20 | wicket.wicket-webjars-samples
21 | org.apache.wicket.protocol.http.WicketFilter
22 |
23 | applicationClassName
24 | de.agilecoders.wicket.WicketApplication
25 |
26 |
27 |
28 |
29 | wicket.wicket-webjars-samples
30 | /*
31 |
32 |
33 |
--------------------------------------------------------------------------------
/samples/src/main/webapp/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/martin-g/wicket-webjars/273a07fc42796fd5a8e78a365296fd18c51a9086/samples/src/main/webapp/logo.png
--------------------------------------------------------------------------------
/samples/src/main/webapp/style.css:
--------------------------------------------------------------------------------
1 | body, p, li, a {
2 | font-family: georgia, times, serif;
3 | font-size: 13pt;
4 | }
5 |
6 | h1, h2, h3 {
7 | font-family: 'Yanone Kaffeesatz', arial, serif;
8 | }
9 |
10 | body {
11 | margin: 0;
12 | padding: 0;
13 | }
14 |
15 | #hd {
16 | width: 100%;
17 | height: 87px;
18 | background-color: #092E67;
19 | margin-top: 0;
20 | padding-top: 10px;
21 | border-bottom: 1px solid #888;
22 | z-index: 0;
23 | }
24 |
25 | #ft {
26 | position: absolute;
27 | bottom: 0;
28 | width: 100%;
29 | height: 99px;
30 | background-color: #6493D2;
31 | border-top: 1px solid #888;
32 | z-index: 0;
33 | }
34 |
35 | #logo, #bd {
36 | width: 650px;
37 | margin: 0 auto;
38 | padding: 25px 50px 0 50px;
39 | }
40 |
41 | #logo h1 {
42 | color: white;
43 | font-size: 36pt;
44 | display: inline;
45 | }
46 |
47 | #logo img {
48 | display: inline;
49 | vertical-align: bottom;
50 | margin-left: 50px;
51 | margin-right: 5px;
52 | }
53 |
54 | body {
55 | margin-top: 0;
56 | padding-top: 0;
57 | }
58 |
59 | #logo, #logo h1 {
60 | margin-top: 0;
61 | padding-top: 0;
62 | }
63 |
64 | #bd {
65 | position: absolute;
66 | top: 75px;
67 | bottom: 75px;
68 | left: 50%;
69 | margin-left: -325px;
70 | z-index: 1;
71 | overflow: auto;
72 | background-color: #fff;
73 | -webkit-border-radius: 10px;
74 | -moz-border-radius: 10px;
75 | border-radius: 10px;
76 | -moz-box-shadow: 0px 0px 10px #888;
77 | -webkit-box-shadow: 0px 0px 10px #888;
78 | box-shadow: 0px 0px 10px #888;
79 | }
80 |
81 | a, a:visited, a:hover, a:active {
82 | color: #6493D2;
83 | }
84 |
85 | h2 {
86 | padding: 0;
87 | margin: 0;
88 | font-size: 36pt;
89 | color: #FF5500;
90 | }
91 |
92 | h3 {
93 | padding: 0;
94 | margin: 0;
95 | font-size: 24pt;
96 | color: #092E67;
97 | }
--------------------------------------------------------------------------------