├── .github ├── dependabot.yml └── workflows │ └── run_tests.yml ├── .gitignore ├── .travis.yml ├── README.md ├── library ├── pom.xml └── src │ ├── main │ └── java │ │ ├── de │ │ └── agilecoders │ │ │ └── wicket │ │ │ └── webjars │ │ │ ├── WicketWebjars.java │ │ │ ├── collectors │ │ │ ├── AssetPathCollector.java │ │ │ ├── AssetsMap.java │ │ │ ├── ClasspathAssetPathCollector.java │ │ │ ├── FileAssetPathCollector.java │ │ │ ├── IAssetProvider.java │ │ │ ├── IRecentVersionProvider.java │ │ │ ├── JarAssetPathCollector.java │ │ │ ├── ProtocolAwareAssetPathCollector.java │ │ │ ├── VfsAssetPathCollector.java │ │ │ └── WebSphereClasspathAssetPathCollector.java │ │ │ ├── request │ │ │ ├── WebjarsCDNRequestMapper.java │ │ │ └── resource │ │ │ │ ├── IWebjarsResourceReference.java │ │ │ │ ├── WebjarsCssResourceReference.java │ │ │ │ ├── WebjarsJavaScriptResourceReference.java │ │ │ │ └── WebjarsPackageResourceReference.java │ │ │ ├── settings │ │ │ ├── IWebjarsSettings.java │ │ │ ├── ResourceStreamProvider.java │ │ │ ├── WebSphereWebjarsSettings.java │ │ │ └── WebjarsSettings.java │ │ │ └── util │ │ │ ├── ClassLoaderResourceStreamProvider.java │ │ │ ├── ClasspathUrlStreamHandler.java │ │ │ ├── Helper.java │ │ │ ├── IFullPathProvider.java │ │ │ ├── IResourceStreamProvider.java │ │ │ ├── RecentVersionCallable.java │ │ │ ├── UrlResourceStreamProvider.java │ │ │ ├── WebJarAssetLocator.java │ │ │ ├── WebjarsVersion.java │ │ │ └── file │ │ │ └── WebjarsResourceFinder.java │ │ └── module-info.java │ └── test │ └── java │ └── de │ └── agilecoders │ └── wicket │ └── webjars │ ├── WicketWebjarsTest.java │ ├── collectors │ └── AssetsMapTest.java │ └── util │ └── file │ └── WebjarsResourceFinderTest.java ├── pom.xml └── samples ├── pom.xml └── src └── main ├── java └── de │ └── agilecoders │ └── wicket │ ├── HomePage.html │ ├── HomePage.java │ └── WicketApplication.java ├── resources └── log4j.properties └── webapp ├── WEB-INF └── web.xml ├── logo.png └── style.css /.github/dependabot.yml: -------------------------------------------------------------------------------- 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 | version: 2 20 | updates: 21 | 22 | - package-ecosystem: "maven" 23 | directory: "/" 24 | schedule: 25 | interval: "weekly" 26 | day: "sunday" 27 | open-pull-requests-limit: 50 28 | 29 | - package-ecosystem: "github-actions" 30 | directory: "/" 31 | schedule: 32 | interval: "weekly" 33 | day: "sunday" 34 | -------------------------------------------------------------------------------- /.github/workflows/run_tests.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest, windows-latest] 10 | 11 | runs-on: ${{ matrix.os }} 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - name: Set up JDK 16 | uses: actions/setup-java@v4 17 | with: 18 | java-version: 17 19 | distribution: temurin 20 | 21 | - name: Cache Local Maven Repository 22 | uses: actions/cache@v4 23 | with: 24 | path: ~/.m2/repository 25 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 26 | restore-keys: | 27 | ${{ runner.os }}-maven- 28 | 29 | - name: Build with Maven 30 | run: mvn -B package --file pom.xml 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # project files 11 | *.iml 12 | .idea 13 | */.idea 14 | 15 | # Packages # 16 | ############ 17 | # it's better to unpack these files and commit the raw source 18 | # git has its own built in compression methods 19 | *.7z 20 | *.dmg 21 | *.gz 22 | *.iso 23 | *.jar 24 | *.rar 25 | *.tar 26 | *.zip 27 | 28 | # Logs and databases # 29 | ###################### 30 | *.log 31 | *.sql 32 | *.sqlite 33 | 34 | # OS generated files # 35 | ###################### 36 | .DS_Store* 37 | ehthumbs.db 38 | Icon? 39 | Thumbs.db 40 | 41 | target 42 | 43 | # eclipse generated files # 44 | ########################### 45 | .project 46 | .classpath 47 | .settings -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | wicket-webjars 2 | ============== 3 | 4 | Integration of webjars for Apache Wicket. 5 | 6 | Current build status: [![Build Status](https://buildhive.cloudbees.com/job/l0rdn1kk0n/job/wicket-webjars/badge/icon)](https://buildhive.cloudbees.com/job/l0rdn1kk0n/job/wicket-webjars/) 7 | 8 | **wicket-webjars** dependes on [webjars](https://github.com/webjars/webjars). 9 | 10 | Current release version: 11 | 12 | * For Wicket 10.x use 4.x 13 | * For Wicket 9.x use 3.x 14 | * For Wicket 8.x use 2.x 15 | * For Wicket 7.x use 0.5.x 16 | * For Wicket 6.x use 0.4.x 17 | 18 | 19 | Documentation: 20 | 21 | - [Webjars Documentation](http://www.webjars.org/documentation) 22 | - [Available Webjars](http://www.webjars.org) 23 | 24 | Add maven dependency: 25 | 26 | ```xml 27 | 28 | de.agilecoders.wicket.webjars 29 | wicket-webjars 30 | 4.0.0 31 | 32 | ``` 33 | 34 | Installation: 35 | 36 | ```java 37 | /** 38 | * @see org.apache.wicket.Application#init() 39 | */ 40 | @Override 41 | public void init() { 42 | super.init(); 43 | 44 | // install 3 default collector instances 45 | // (FileAssetPathCollector(WEBJARS_PATH_PREFIX), JarAssetPathCollector, VfsAssetPathCollector) 46 | // and a webjars resource finder. 47 | WebjarsSettings settings = new WebjarsSettings(); 48 | 49 | WicketWebjars.install(this, settings); 50 | } 51 | ``` 52 | 53 | Usage 54 | ===== 55 | 56 | Add a webjars resource reference (css,js) to your IHeaderResponse: 57 | 58 | ```java 59 | public WebjarsComponent extends Panel { 60 | 61 | public WebjarsComponent(String id) { 62 | super(id); 63 | } 64 | 65 | @Override 66 | public void renderHead(IHeaderResponse response) { 67 | super.renderHead(response); 68 | 69 | response.render(JavaScriptHeaderItem.forReference(new WebjarsJavaScriptResourceReference("jquery/1.8.3/jquery.js"))); 70 | } 71 | } 72 | ``` 73 | 74 | Add dependencies to your pom.xml: 75 | 76 | ```xml 77 | 78 | 79 | de.agilecoders.wicket.webjars 80 | wicket-webjars 81 | 82 | 83 | 84 | org.webjars 85 | jquery 86 | 1.8.3 87 | 88 | 89 | ``` 90 | 91 | It is also possible to use a resource by adding it to your html markup directly: 92 | 93 | ```html 94 | 95 | ``` 96 | 97 | **Note**: The above works only for Servlet 3 web containers that automatically map META-INF/resources/* as browsable resources! Embedded Jetty or containers without this feature need extra configuration to enable this feature! Explicit version is also required (``jquery-ui/current/...`` urls are not handled). 98 | 99 | To use always recent version from your pom you have to replace the version in path with the string "current". When resource 100 | name gets resolved this string will be replaced by recent available version in classpath. (this feature is available since 0.2.0) 101 | 102 | ```java 103 | public WebjarsComponent extends Panel { 104 | 105 | public WebjarsComponent(String id) { 106 | super(id); 107 | } 108 | 109 | @Override 110 | public void renderHead(IHeaderResponse response) { 111 | super.renderHead(response); 112 | 113 | response.render(JavaScriptHeaderItem.forReference(new WebjarsJavaScriptResourceReference("jquery/current/jquery.js"))); 114 | } 115 | } 116 | ``` 117 | 118 | **Note**: you must specify in your path either an explicit version (e.g. ``1.8.3``) or the ```` configured in ``IWebjarsSettings`` (``current`` for a default config). 119 | 120 | Authors 121 | ------- 122 | 123 | [![Ohloh profile for Michael Haitz](https://www.openhub.net/accounts/l0rdn1kk0n/widgets/account_detailed.gif)](https://www.openhub.net/accounts/l0rdn1kk0n?ref=Detailed) 124 | 125 | [![Ohloh profile for Martin Grigorov](https://www.openhub.net/accounts/mgrigorov/widgets/account_detailed.gif)](https://www.openhub.net/accounts/mgrigorov?ref=Detailed) 126 | 127 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/l0rdn1kk0n/wicket-webjars/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 128 | 129 | -------------------------------------------------------------------------------- /library/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 | ../pom.xml 10 | 11 | 12 | wicket-webjars 13 | jar 14 | library 15 | 16 | 17 | 18 | 19 | org.apache.wicket 20 | wicket-core 21 | 22 | 23 | 24 | 25 | edu.emory.mathcs.util 26 | emory-util-classloader 27 | true 28 | 29 | 30 | 31 | 32 | org.slf4j 33 | slf4j-api 34 | 35 | 36 | 37 | 38 | org.apache.wicket 39 | wicket-tester 40 | 41 | 42 | org.junit.jupiter 43 | junit-jupiter-api 44 | 45 | 46 | org.junit.vintage 47 | junit-vintage-engine 48 | 49 | 50 | 51 | org.hamcrest 52 | hamcrest 53 | test 54 | 55 | 56 | 57 | org.webjars 58 | jquery 59 | test 60 | 61 | 62 | 63 | jakarta.servlet 64 | jakarta.servlet-api 65 | test 66 | 67 | 68 | 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-surefire-plugin 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /library/src/main/java/de/agilecoders/wicket/webjars/WicketWebjars.java: -------------------------------------------------------------------------------- 1 | package de.agilecoders.wicket.webjars; 2 | 3 | import de.agilecoders.wicket.webjars.request.WebjarsCDNRequestMapper; 4 | import de.agilecoders.wicket.webjars.settings.IWebjarsSettings; 5 | import de.agilecoders.wicket.webjars.settings.WebjarsSettings; 6 | import de.agilecoders.wicket.webjars.util.WebjarsVersion; 7 | import de.agilecoders.wicket.webjars.util.file.WebjarsResourceFinder; 8 | import org.apache.wicket.Application; 9 | import org.apache.wicket.MetaDataKey; 10 | import org.apache.wicket.core.request.mapper.ResourceReferenceMapper; 11 | import org.apache.wicket.protocol.http.WebApplication; 12 | import org.apache.wicket.request.IRequestMapper; 13 | import org.apache.wicket.request.mapper.parameter.PageParametersEncoder; 14 | import org.apache.wicket.request.resource.caching.IResourceCachingStrategy; 15 | import org.apache.wicket.util.file.IResourceFinder; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | import java.util.List; 20 | import java.util.function.Supplier; 21 | 22 | /** 23 | * Helper class for webjars resources 24 | * 25 | * @author miha 26 | */ 27 | public final class WicketWebjars { 28 | private static final Logger LOG = LoggerFactory.getLogger("wicket-webjars"); 29 | 30 | /** 31 | * The {@link org.apache.wicket.MetaDataKey} used to retrieve the {@link IWebjarsSettings} from the Wicket {@link Appendable}. 32 | */ 33 | private static final MetaDataKey WEBJARS_SETTINGS_METADATA_KEY = new MetaDataKey() { 34 | }; 35 | 36 | /** 37 | * Checks whether Webjars support is already installed 38 | * 39 | * @param application the wicket application 40 | * @return {@code true} if Webjars is already installed, otherwise {@code false} 41 | */ 42 | public static boolean isInstalled(Application application) { 43 | return application.getMetaData(WEBJARS_SETTINGS_METADATA_KEY) != null; 44 | } 45 | 46 | /** 47 | * installs the webjars resource finder and uses a set of default settings. 48 | * 49 | * @param app the wicket application 50 | */ 51 | public static void install(final WebApplication app) { 52 | install(app, null); 53 | } 54 | 55 | /** 56 | * installs the webjars resource finder 57 | * 58 | * @param app the wicket application 59 | * @param settings the settings to use 60 | */ 61 | public static void install(WebApplication app, IWebjarsSettings settings) { 62 | final IWebjarsSettings existingSettings = settings(app); 63 | 64 | if (existingSettings == null) { 65 | if (settings == null) { 66 | settings = new WebjarsSettings(); 67 | } 68 | 69 | app.setMetaData(WEBJARS_SETTINGS_METADATA_KEY, settings); 70 | 71 | if (settings.useCdnResources()) { 72 | mountCDNMapper(app, settings.cdnUrl()); 73 | } 74 | 75 | final List finders = app.getResourceSettings().getResourceFinders(); 76 | final WebjarsResourceFinder finder = new WebjarsResourceFinder(settings); 77 | 78 | if (!finders.contains(finder)) { 79 | finders.add(finder); 80 | } 81 | 82 | LOG.info("initialize wicket webjars with given settings: {}", settings); 83 | } 84 | } 85 | 86 | public static void reindex(final WebApplication application) { 87 | final List resourceFinders = application.getResourceSettings().getResourceFinders(); 88 | for (IResourceFinder resourceFinder : resourceFinders) { 89 | if (resourceFinder instanceof WebjarsResourceFinder) { 90 | WebjarsVersion.reset(); 91 | WebjarsResourceFinder webjarsResourceFinder = (WebjarsResourceFinder) resourceFinder; 92 | webjarsResourceFinder.reindex(); 93 | break; 94 | } 95 | } 96 | } 97 | 98 | /** 99 | * mounts a special resource reference mapper that transform webjars resource urls into a cdn url. 100 | * 101 | * @param app current web app 102 | * @param cdnUrl the cdn url to use 103 | */ 104 | private static void mountCDNMapper(final WebApplication app, String cdnUrl) { 105 | Supplier parentFolderPlaceholderProvider = () -> app.getResourceSettings().getParentFolderPlaceholder(); 106 | Supplier cachingStrategyProvider = () -> app.getResourceSettings().getCachingStrategy(); 107 | 108 | LOG.info("use cdn resources from {}", cdnUrl); 109 | 110 | IRequestMapper delegate = new ResourceReferenceMapper(new PageParametersEncoder(), parentFolderPlaceholderProvider, cachingStrategyProvider); 111 | app.mount(new WebjarsCDNRequestMapper(delegate, cdnUrl, cachingStrategyProvider)); 112 | } 113 | 114 | /** 115 | * returns the {@link IWebjarsSettings} which are assigned to given application 116 | * 117 | * @param app The current application 118 | * @return assigned {@link IWebjarsSettings} 119 | */ 120 | public static IWebjarsSettings settings(final Application app) { 121 | return app.getMetaData(WEBJARS_SETTINGS_METADATA_KEY); 122 | } 123 | 124 | /** 125 | * returns the {@link IWebjarsSettings} which are assigned to current application 126 | * 127 | * @return assigned {@link IWebjarsSettings} 128 | */ 129 | public static IWebjarsSettings settings() { 130 | if (Application.exists()) { 131 | final IWebjarsSettings settings = Application.get().getMetaData(WEBJARS_SETTINGS_METADATA_KEY); 132 | 133 | if (settings != null) { 134 | return settings; 135 | } else { 136 | throw new IllegalStateException("you have to call WicketWebjars.install() before you can use an " 137 | + "IWebjarsResourceReference or any other component."); 138 | } 139 | } 140 | 141 | final String warning = "There is no Wicket Application thread local! Going to use default Webjars settings."; 142 | LOG.warn(warning, new RuntimeException(warning)); 143 | return new WebjarsSettings(); 144 | } 145 | 146 | /** 147 | * private constructor. 148 | */ 149 | private WicketWebjars() { 150 | throw new UnsupportedOperationException(); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /library/src/main/java/de/agilecoders/wicket/webjars/collectors/AssetPathCollector.java: -------------------------------------------------------------------------------- 1 | package de.agilecoders.wicket.webjars.collectors; 2 | 3 | import java.net.URL; 4 | import java.util.Collection; 5 | import java.util.regex.Pattern; 6 | 7 | /** 8 | * An {@link de.agilecoders.wicket.webjars.collectors.AssetPathCollector} collects webjars assets from 9 | * an url/classpath/disc and so on depending on the protocol that is used. 10 | * 11 | * @author miha 12 | */ 13 | public interface AssetPathCollector { 14 | 15 | /** 16 | * whether this collector supports given url (especially protocol) 17 | * 18 | * @param url the url to webjars asset 19 | * @return true, if given protocol is accepted 20 | */ 21 | boolean accept(URL url); 22 | 23 | /** 24 | * collects all webjars assets on given url. 25 | * 26 | * @param url the path to webjars assets 27 | * @param filterExpr a filter that must be applied on all found assets. 28 | * @return a collection of webjars assets on given {@code url} that matches given {@code filterExpr} 29 | */ 30 | Collection collect(URL url, Pattern filterExpr); 31 | } 32 | -------------------------------------------------------------------------------- /library/src/main/java/de/agilecoders/wicket/webjars/collectors/AssetsMap.java: -------------------------------------------------------------------------------- 1 | package de.agilecoders.wicket.webjars.collectors; 2 | 3 | import de.agilecoders.wicket.webjars.WicketWebjars; 4 | import de.agilecoders.wicket.webjars.settings.IWebjarsSettings; 5 | import de.agilecoders.wicket.webjars.util.Helper; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import static de.agilecoders.wicket.webjars.util.Helper.reversePath; 9 | 10 | import java.io.IOException; 11 | import java.net.URL; 12 | import java.util.ArrayList; 13 | import java.util.Collection; 14 | import java.util.Enumeration; 15 | import java.util.HashSet; 16 | import java.util.List; 17 | import java.util.Set; 18 | import java.util.SortedMap; 19 | import java.util.TreeMap; 20 | import java.util.regex.Matcher; 21 | import java.util.regex.Pattern; 22 | 23 | /** 24 | * asset holder map. 25 | * 26 | * @author miha 27 | */ 28 | public class AssetsMap implements IAssetProvider, IRecentVersionProvider { 29 | private static final Logger LOG = LoggerFactory.getLogger(WicketWebjars.class); 30 | 31 | private final IWebjarsSettings settings; 32 | private final SortedMap fullPathIndex; 33 | private final AssetPathCollector[] collectors; 34 | private final String recentVersionPlaceHolder; 35 | 36 | /** 37 | * Construct. 38 | * 39 | * @param settings the settings to use. 40 | */ 41 | public AssetsMap(IWebjarsSettings settings) { 42 | this.settings = settings; 43 | this.collectors = settings.assetPathCollectors(); 44 | this.recentVersionPlaceHolder = settings.recentVersionPlaceHolder(); 45 | this.fullPathIndex = new TreeMap<>(); 46 | reindex(); 47 | } 48 | 49 | public AssetsMap reindex() { 50 | final Pattern resourcePattern = settings.resourcePattern(); 51 | final ClassLoader[] classLoaders = settings.classLoaders(); 52 | final SortedMap _fullPathIndex = createFullPathIndex(resourcePattern, classLoaders); 53 | fullPathIndex.clear(); 54 | fullPathIndex.putAll(_fullPathIndex); 55 | return this; 56 | } 57 | 58 | @Override 59 | public String findRecentVersionFor(String path) { 60 | final String partialPath = Helper.prependWebjarsPathIfMissing(path); 61 | final Matcher partialPathMatcher = settings.webjarsPathPattern().matcher(partialPath); 62 | 63 | if (partialPathMatcher.find() && recentVersionPlaceHolder.equalsIgnoreCase(partialPathMatcher.group(2))) { 64 | final Set assets = listAssets(partialPathMatcher.group(1)); 65 | final String fileName = "/" + partialPathMatcher.group(3); 66 | final Set versions = new HashSet<>(); 67 | 68 | for (String asset : assets) { 69 | if (asset.endsWith(fileName)) { 70 | final Matcher matcher = settings.webjarsPathPattern().matcher(asset); 71 | 72 | if (matcher.find()) { 73 | versions.add(matcher.group(2)); 74 | } 75 | } 76 | } 77 | 78 | if (versions.size() == 1) { 79 | return versions.iterator().next(); 80 | } else if (versions.size() > 1) { 81 | final String first = versions.iterator().next(); 82 | LOG.warn("More than one version of a webjars resource has been found in the classpath. " + 83 | "This is not supported! Webjars resource: {}; available versions: {}; going to use: {}", 84 | fileName, versions, first); 85 | 86 | return first; 87 | } else { 88 | LOG.debug("No version found for webjars resource: {}", partialPath); 89 | } 90 | } else { 91 | LOG.trace("given webjars resource isn't a dynamic versioned one: {}", partialPath); 92 | } 93 | 94 | return null; 95 | } 96 | 97 | @Override 98 | public SortedMap getFullPathIndex() { 99 | return fullPathIndex; 100 | } 101 | 102 | @Override 103 | public Set listAssets(final String folderPath) { 104 | final Collection allAssets = getFullPathIndex().values(); 105 | // ensure that webjarModulePath contains trailing a file separator (bootstrap -> boostrap/) 106 | final String webjarModulePath = folderPath.endsWith("/") ? folderPath : folderPath + "/"; 107 | 108 | final Set assets = new HashSet<>(); 109 | 110 | final String prefix; 111 | final String webjarsPath = settings.webjarsPath(); 112 | if (webjarsPath.endsWith("/")) { 113 | prefix = webjarsPath + Helper.removeLeadingSlash(webjarModulePath); 114 | } else { 115 | prefix = webjarsPath + Helper.prependLeadingSlash(webjarModulePath); 116 | } 117 | 118 | for (final String asset : allAssets) { 119 | if (asset.startsWith(prefix)) { 120 | // all assets in webjarModulePath subpath are candidates 121 | assets.add(asset); 122 | } 123 | } 124 | 125 | return assets; 126 | } 127 | 128 | /** 129 | * Return all {@link URL}s found in webjars directory, 130 | * either identifying JAR files or plain directories. 131 | */ 132 | private Set listWebjarsParentURLs(final ClassLoader[] classLoaders) { 133 | final Set urls = new HashSet<>(); 134 | final String webjarsPath = settings.webjarsPath(); 135 | 136 | for (final ClassLoader classLoader : classLoaders) { 137 | try { 138 | final Enumeration enumeration = classLoader.getResources(webjarsPath); 139 | while (enumeration.hasMoreElements()) { 140 | urls.add(enumeration.nextElement()); 141 | } 142 | } catch (IOException e) { 143 | throw new RuntimeException(e); 144 | } 145 | } 146 | return urls; 147 | } 148 | 149 | /** 150 | * Return all of the resource paths filtered given an expression and a list of class loaders. 151 | */ 152 | private Set getAssetPaths(final Pattern filterExpr, final ClassLoader... classLoaders) { 153 | final Set assetPaths = new HashSet<>(); 154 | 155 | final Set urls = listWebjarsParentURLs(classLoaders); 156 | 157 | for (final URL url : urls) { 158 | for (AssetPathCollector collector : collectors) { 159 | if (collector.accept(url)) { 160 | Collection collection = collector.collect(url, filterExpr); 161 | assetPaths.addAll(collection); 162 | } 163 | } 164 | } 165 | 166 | return assetPaths; 167 | } 168 | 169 | /** 170 | * Return a map that can be used to perform index lookups of partial file paths. This index constitutes a key that is the reverse form of the path 171 | * it relates to. Thus if a partial lookup needs to be performed from the rightmost path components then the key to access can be expressed easily 172 | * e.g. the path "a/b" would be the map tuple "b/a" -> "a/b". If we need to look for an asset named "a" without knowing the full path then we can 173 | * perform a partial lookup on the sorted map. 174 | * 175 | * @param filterExpr the regular expression to be used to filter resources that will be included in the index. 176 | * @param classLoaders the class loaders to be considered for loading the resources from. 177 | * @return the index. 178 | */ 179 | private SortedMap createFullPathIndex(final Pattern filterExpr, final ClassLoader... classLoaders) { 180 | final Set assetPaths = getAssetPaths(filterExpr, classLoaders); 181 | 182 | final SortedMap assetPathIndex = new TreeMap<>(); 183 | for (final String assetPath : assetPaths) { 184 | assetPathIndex.put(reversePath(assetPath), assetPath); 185 | } 186 | 187 | return assetPathIndex; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /library/src/main/java/de/agilecoders/wicket/webjars/collectors/ClasspathAssetPathCollector.java: -------------------------------------------------------------------------------- 1 | package de.agilecoders.wicket.webjars.collectors; 2 | 3 | import java.io.IOException; 4 | import java.net.JarURLConnection; 5 | import java.net.URL; 6 | import java.net.URLConnection; 7 | import java.util.Collection; 8 | import java.util.Enumeration; 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | import java.util.jar.JarEntry; 12 | import java.util.jar.JarFile; 13 | import java.util.regex.Pattern; 14 | 15 | import de.agilecoders.wicket.webjars.settings.IWebjarsSettings; 16 | 17 | /** 18 | * A collector that searches for assets in the classpath, only in 19 | * {@link IWebjarsSettings#webjarsPath()}, usually in META-INF/resources/webjars/**. 20 | */ 21 | public class ClasspathAssetPathCollector implements AssetPathCollector { 22 | 23 | @Override 24 | public boolean accept(final URL url) { 25 | return true; 26 | } 27 | 28 | @Override 29 | public Collection collect(final URL url, final Pattern filterExpr) { 30 | final Set assetPaths = new HashSet<>(); 31 | try { 32 | Set paths = collectFromWebJarPath(url, filterExpr); 33 | assetPaths.addAll(paths); 34 | } catch (IOException e) { 35 | throw new IllegalStateException(e); 36 | } 37 | return assetPaths; 38 | } 39 | 40 | private Set collectFromWebJarPath(URL webJarPathResource, final Pattern filterExpr) throws IOException { 41 | final Set assetPaths = new HashSet<>(); 42 | 43 | URLConnection urlConnection = webJarPathResource.openConnection(); 44 | if (urlConnection instanceof JarURLConnection) { 45 | JarURLConnection urlcon = (JarURLConnection) urlConnection; 46 | JarFile jar = null; 47 | try { 48 | jar = urlcon.getJarFile(); 49 | Enumeration entries = jar.entries(); 50 | while (entries.hasMoreElements()) { 51 | String innerJarEntryName = entries.nextElement().getName(); 52 | if (!isDirectory(innerJarEntryName) && filterExpr.matcher(innerJarEntryName).matches()) { 53 | assetPaths.add(innerJarEntryName); 54 | } 55 | } 56 | } finally { 57 | if (jar != null) { 58 | jar.close(); 59 | } 60 | } 61 | } 62 | return assetPaths; 63 | } 64 | 65 | private boolean isDirectory(String innerJarEntryName) { 66 | return innerJarEntryName.endsWith("/"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /library/src/main/java/de/agilecoders/wicket/webjars/collectors/FileAssetPathCollector.java: -------------------------------------------------------------------------------- 1 | package de.agilecoders.wicket.webjars.collectors; 2 | 3 | import java.io.File; 4 | import java.net.URISyntaxException; 5 | import java.net.URL; 6 | import java.util.Collection; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.regex.Pattern; 10 | 11 | import de.agilecoders.wicket.webjars.util.WebJarAssetLocator; 12 | 13 | /** 14 | * A {@link de.agilecoders.wicket.webjars.collectors.FileAssetPathCollector} searches webjars on disk 15 | * in a special directory. 16 | * 17 | * @author miha 18 | */ 19 | public class FileAssetPathCollector extends ProtocolAwareAssetPathCollector { 20 | 21 | private final String pathPrefix; 22 | 23 | /** 24 | * Construct. 25 | * 26 | * @param pathPrefix the path where to look for resources 27 | */ 28 | public FileAssetPathCollector(final String pathPrefix) { 29 | super("file"); 30 | 31 | this.pathPrefix = pathPrefix; 32 | } 33 | 34 | @Override 35 | public Collection collect(URL url, Pattern filterExpr) { 36 | final File file; 37 | try { 38 | file = new File(url.toURI()); 39 | } catch (URISyntaxException e) { 40 | throw new WebJarAssetLocator.ResourceException(url.toString(), e.getMessage()); 41 | } 42 | 43 | return listFiles(file, filterExpr); 44 | } 45 | 46 | /** 47 | * Recursively search all directories for relative file paths matching `filterExpr`. 48 | * 49 | * @param file the directory to search in 50 | * @param filterExpr the filter to apply 51 | * @return all files that matches given filter 52 | */ 53 | private Set listFiles(final File file, final Pattern filterExpr) { 54 | final Set aggregatedChildren = new HashSet<>(); 55 | aggregateChildren(file, aggregatedChildren, filterExpr); 56 | return aggregatedChildren; 57 | } 58 | 59 | private void aggregateChildren(final File file, final Set aggregatedChildren, final Pattern filterExpr) { 60 | if (file != null && file.isDirectory()) { 61 | File[] files = file.listFiles(); 62 | 63 | if (files != null) { 64 | for (final File child : files) { 65 | aggregateChildren(child, aggregatedChildren, filterExpr); 66 | } 67 | } 68 | } else if (file != null) { 69 | aggregateFile(file, aggregatedChildren, filterExpr); 70 | } 71 | } 72 | 73 | private void aggregateFile(final File file, final Set aggregatedChildren, final Pattern filterExpr) { 74 | final String path = file.getPath().replace('\\', '/'); 75 | final String relativePath = path.substring(path.indexOf(pathPrefix)); 76 | 77 | if (filterExpr.matcher(relativePath).matches()) { 78 | aggregatedChildren.add(relativePath); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /library/src/main/java/de/agilecoders/wicket/webjars/collectors/IAssetProvider.java: -------------------------------------------------------------------------------- 1 | package de.agilecoders.wicket.webjars.collectors; 2 | 3 | import java.util.Set; 4 | import java.util.SortedMap; 5 | 6 | /** 7 | * base provider interface 8 | * 9 | * @author miha 10 | */ 11 | public interface IAssetProvider { 12 | 13 | /** 14 | * List assets within a folder. 15 | * 16 | * @param folderPath the root path to the folder. Must begin with '/'. 17 | * @return a set of folder paths that match. 18 | */ 19 | Set listAssets(final String folderPath); 20 | 21 | /** 22 | * @return the full path index map. 23 | */ 24 | SortedMap getFullPathIndex(); 25 | } 26 | -------------------------------------------------------------------------------- /library/src/main/java/de/agilecoders/wicket/webjars/collectors/IRecentVersionProvider.java: -------------------------------------------------------------------------------- 1 | package de.agilecoders.wicket.webjars.collectors; 2 | 3 | /** 4 | * provides access to recent version of a webjars 5 | * 6 | * @author miha 7 | */ 8 | public interface IRecentVersionProvider { 9 | 10 | /** 11 | * @param path the path to detect version for 12 | * @return recent version 13 | */ 14 | String findRecentVersionFor(String path); 15 | } 16 | -------------------------------------------------------------------------------- /library/src/main/java/de/agilecoders/wicket/webjars/collectors/JarAssetPathCollector.java: -------------------------------------------------------------------------------- 1 | package de.agilecoders.wicket.webjars.collectors; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.net.URI; 7 | import java.net.URL; 8 | import java.util.Collection; 9 | import java.util.Enumeration; 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | import java.util.jar.JarEntry; 13 | import java.util.jar.JarFile; 14 | import java.util.jar.JarInputStream; 15 | import java.util.regex.Pattern; 16 | 17 | import de.agilecoders.wicket.webjars.util.WebJarAssetLocator; 18 | 19 | /** 20 | * @deprecated Use ClasspathAssetPathCollector instead 21 | */ 22 | @Deprecated 23 | public class JarAssetPathCollector extends ProtocolAwareAssetPathCollector { 24 | 25 | /** 26 | * Construct accepting the jar protocol. 27 | */ 28 | public JarAssetPathCollector() { 29 | super("jar"); 30 | } 31 | 32 | /** 33 | * Construct. 34 | * 35 | * @param protocols the protocols to accept 36 | */ 37 | protected JarAssetPathCollector(final String... protocols) { 38 | super(protocols); 39 | } 40 | 41 | @Override 42 | public Collection collect(URL url, Pattern filterExpr) { 43 | final JarFile jarFile = newJarFile(url); 44 | final Set assetPaths = new HashSet<>(); 45 | 46 | final String jarFileName = jarFile.getName(); 47 | boolean isArchive = jarFileName.endsWith(".war") || jarFileName.endsWith(".jar"); 48 | 49 | final Enumeration entries = jarFile.entries(); 50 | while (entries.hasMoreElements()) { 51 | final JarEntry entry = entries.nextElement(); 52 | final String assetPathCandidate = entry.getName(); 53 | 54 | if (isArchive && assetPathCandidate.endsWith(".jar")) { 55 | collectInnerJar(jarFile, entry, assetPaths, filterExpr); 56 | } else if (!entry.isDirectory() && filterExpr.matcher("/" + assetPathCandidate).matches()) { 57 | assetPaths.add(assetPathCandidate); 58 | } 59 | } 60 | 61 | return assetPaths; 62 | } 63 | 64 | protected void collectInnerJar(JarFile jarFile, JarEntry entry, Set assetPaths, Pattern filterExpr) { 65 | try { 66 | InputStream inputStream = jarFile.getInputStream(entry); 67 | JarInputStream jarInputStream = new JarInputStream(inputStream); 68 | JarEntry innerJarEntry; 69 | while ((innerJarEntry = jarInputStream.getNextJarEntry()) != null) { 70 | String innerJarEntryName = innerJarEntry.getName(); 71 | if(!filterExpr.matcher(innerJarEntryName).matches()){ 72 | break; 73 | } 74 | 75 | if (!innerJarEntry.isDirectory() && filterExpr.matcher(innerJarEntryName).matches()) { 76 | assetPaths.add(innerJarEntryName); 77 | } 78 | } 79 | } catch (IOException e) { 80 | throw new WebJarAssetLocator.ResourceException(jarFile.getName(), e.getMessage()); 81 | } 82 | } 83 | 84 | protected JarFile newJarFile(final URL url) { 85 | try { 86 | final String path = url.getPath(); 87 | final File file = new File(URI.create(path.substring(0, path.indexOf("!")))); 88 | 89 | return new JarFile(file); 90 | } catch (IOException e) { 91 | throw new WebJarAssetLocator.ResourceException(url.toString(), e.getMessage()); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /library/src/main/java/de/agilecoders/wicket/webjars/collectors/ProtocolAwareAssetPathCollector.java: -------------------------------------------------------------------------------- 1 | package de.agilecoders.wicket.webjars.collectors; 2 | 3 | import java.net.URL; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | public abstract class ProtocolAwareAssetPathCollector implements AssetPathCollector { 8 | 9 | private final List protocols; 10 | 11 | /** 12 | * Construct. 13 | * 14 | * @param protocols the protocols to accept 15 | */ 16 | protected ProtocolAwareAssetPathCollector(final String... protocols) { 17 | this.protocols = Arrays.asList(protocols); 18 | } 19 | 20 | @Override 21 | public boolean accept(URL url) { 22 | if (url == null) { 23 | return false; 24 | } 25 | for (String protocol : protocols) { 26 | if (protocol.equalsIgnoreCase(url.getProtocol())) { 27 | return true; 28 | } 29 | } 30 | return false; 31 | } 32 | } -------------------------------------------------------------------------------- /library/src/main/java/de/agilecoders/wicket/webjars/collectors/VfsAssetPathCollector.java: -------------------------------------------------------------------------------- 1 | package de.agilecoders.wicket.webjars.collectors; 2 | 3 | import java.net.URL; 4 | import java.net.URLConnection; 5 | import java.util.Collection; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | import java.util.jar.JarEntry; 9 | import java.util.jar.JarInputStream; 10 | import java.util.regex.Pattern; 11 | 12 | /** 13 | * An {@link AssetPathCollector} that collects all file entries in JBoss virtual file system 14 | */ 15 | public class VfsAssetPathCollector extends ProtocolAwareAssetPathCollector { 16 | 17 | /** 18 | * Construct accepting the jar protocol. 19 | */ 20 | public VfsAssetPathCollector() { 21 | super("vfs"); 22 | } 23 | 24 | @Override 25 | public Collection collect(URL url, Pattern filterExpr) { 26 | final Set assetPaths = new HashSet(); 27 | try { 28 | URLConnection connection = url.openConnection(); 29 | JarInputStream inputStream = (JarInputStream) connection.getInputStream(); 30 | 31 | JarEntry entry; 32 | while ((entry = inputStream.getNextJarEntry()) != null) { 33 | String entryName = entry.getName(); 34 | if (!entry.isDirectory()) { 35 | assetPaths.add("META-INF/resources/webjars/" + entryName); 36 | } 37 | 38 | } 39 | } catch (Exception x) { 40 | throw new RuntimeException("Cannot collect the file entries in url: " + url, x); 41 | } 42 | 43 | return assetPaths; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /library/src/main/java/de/agilecoders/wicket/webjars/collectors/WebSphereClasspathAssetPathCollector.java: -------------------------------------------------------------------------------- 1 | package de.agilecoders.wicket.webjars.collectors; 2 | 3 | import java.io.IOException; 4 | import java.net.URL; 5 | import java.util.Collection; 6 | import java.util.Enumeration; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.jar.JarEntry; 10 | import java.util.jar.JarFile; 11 | import java.util.regex.Pattern; 12 | 13 | import de.agilecoders.wicket.webjars.settings.IWebjarsSettings; 14 | import edu.emory.mathcs.util.classloader.jar.JarProxy; 15 | 16 | /** 17 | * A collector that searches for assets in the classpath, only in 18 | * {@link IWebjarsSettings#webjarsPath()}, usually in META-INF/resources/webjars/**. 19 | * 20 | * Make sure to add dependency on edu.emory.mathcs.util:emory-util-classloader to the classpath! 21 | * 22 | * @see de.agilecoders.wicket.webjars.settings.WebSphereWebjarsSettings 23 | */ 24 | public class WebSphereClasspathAssetPathCollector implements AssetPathCollector { 25 | 26 | @Override 27 | public boolean accept(final URL url) { 28 | return true; 29 | } 30 | 31 | @Override 32 | public Collection collect(final URL url, final Pattern filterExpr) { 33 | final Set assetPaths = new HashSet(); 34 | try { 35 | Set paths = collectFromWebJarPath(url, filterExpr); 36 | assetPaths.addAll(paths); 37 | } catch (IOException e) { 38 | throw new IllegalStateException(e); 39 | } 40 | return assetPaths; 41 | } 42 | 43 | private Set collectFromWebJarPath(URL webJarPathResource, final Pattern filterExpr) throws IOException { 44 | final Set assetPaths = new HashSet(); 45 | 46 | edu.emory.mathcs.util.classloader.jar.JarURLConnection urlConnection = 47 | new edu.emory.mathcs.util.classloader.jar.JarURLConnection(webJarPathResource, new JarProxy()); 48 | 49 | JarFile jar = null; 50 | try { 51 | jar = urlConnection.getJarFile(); 52 | Enumeration entries = jar.entries(); 53 | while (entries.hasMoreElements()) { 54 | String innerJarEntryName = entries.nextElement().getName(); 55 | if (!isDirectory(innerJarEntryName) && filterExpr.matcher(innerJarEntryName).matches()) { 56 | assetPaths.add(innerJarEntryName); 57 | } 58 | } 59 | } finally { 60 | if (jar != null) { 61 | jar.close(); 62 | } 63 | } 64 | return assetPaths; 65 | } 66 | 67 | private boolean isDirectory(String innerJarEntryName) { 68 | return innerJarEntryName.endsWith("/"); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /library/src/main/java/de/agilecoders/wicket/webjars/request/WebjarsCDNRequestMapper.java: -------------------------------------------------------------------------------- 1 | package de.agilecoders.wicket.webjars.request; 2 | 3 | import org.apache.wicket.request.IRequestHandler; 4 | import org.apache.wicket.request.IRequestMapper; 5 | import org.apache.wicket.request.Request; 6 | import org.apache.wicket.request.Url; 7 | import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler; 8 | import org.apache.wicket.request.mapper.parameter.PageParameters; 9 | import org.apache.wicket.request.resource.ResourceReference; 10 | import org.apache.wicket.request.resource.caching.IResourceCachingStrategy; 11 | import org.apache.wicket.request.resource.caching.ResourceUrl; 12 | import org.apache.wicket.util.string.Strings; 13 | 14 | import java.util.List; 15 | import java.util.function.Supplier; 16 | 17 | import de.agilecoders.wicket.webjars.request.resource.IWebjarsResourceReference; 18 | import de.agilecoders.wicket.webjars.util.Helper; 19 | 20 | /** 21 | * Maps {@link ResourceReference}s of type {@link IWebjarsResourceReference} to 22 | * the WebJar CDN URLs. Based on 23 | * de.agilecoders.wicket.extensions.request.StaticResourceRewriteMapper. 24 | */ 25 | public class WebjarsCDNRequestMapper implements IRequestMapper { 26 | 27 | private final IRequestMapper chain; 28 | private final String webJarCdnUrl; 29 | private final Supplier cachingStrategyProvider; 30 | 31 | public WebjarsCDNRequestMapper(final IRequestMapper chain, 32 | final String cdnUrl, 33 | final Supplier cachingStrategyProvider) { 34 | this.chain = chain; 35 | this.webJarCdnUrl = cdnUrl; 36 | this.cachingStrategyProvider = cachingStrategyProvider; 37 | } 38 | 39 | @Override 40 | public Url mapHandler(final IRequestHandler requestHandler) { 41 | if (isWebjarsResourceReference(requestHandler)) { 42 | final Url url = chain.mapHandler(requestHandler); 43 | final String urlString = urlToStringWithNoVersion(url); 44 | final int index = urlString.indexOf(Helper.PATH_PREFIX); 45 | 46 | if (index >= 0) { 47 | return Url.parse(Strings.join("/", webJarCdnUrl, 48 | urlString.substring(index + Helper.PATH_PREFIX.length()))); 49 | } else { 50 | return url; 51 | } 52 | } 53 | 54 | return null; 55 | } 56 | 57 | private static boolean isWebjarsResourceReference(final IRequestHandler requestHandler) { 58 | if (requestHandler instanceof ResourceReferenceRequestHandler) { 59 | final ResourceReferenceRequestHandler resourceReferenceRequestHandler = (ResourceReferenceRequestHandler) requestHandler; 60 | final ResourceReference resourceReference = resourceReferenceRequestHandler.getResourceReference(); 61 | 62 | if (resourceReference instanceof IWebjarsResourceReference) { 63 | return true; 64 | } 65 | } 66 | 67 | return false; 68 | } 69 | 70 | @Override 71 | public IRequestHandler mapRequest(final Request request) { 72 | return null; 73 | } 74 | 75 | @Override 76 | public int getCompatibilityScore(final Request request) { 77 | return 0; 78 | } 79 | 80 | /** 81 | * @param url to remove version from 82 | * @return the string representation of the {@link Url} with any version info removed 83 | */ 84 | private String urlToStringWithNoVersion(final Url url) { 85 | final Url copy = new Url(url); 86 | final List 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 | 16 |
17 |
18 | Reindex! 19 | 20 |

Default wicket resource reference:

21 | 22 |
This text will be replaced by jQuery after 5 seconds...
23 | 41 |

42 | 43 |

Static linked resource:

44 |
45 | 46 | 47 |
48 |
49 | 50 |

Bootstrap

51 |
    52 |
  • .glyphicon .glyphicon-adjust
  • 53 |
  • .glyphicon .glyphicon-align-center
  • 54 |
  • .glyphicon .glyphicon-align-justify
  • 55 |
  • .glyphicon .glyphicon-align-left
  • 56 |
  • .glyphicon .glyphicon-align-right
  • 57 |
  • .glyphicon .glyphicon-arrow-down
  • 58 |
  • .glyphicon .glyphicon-arrow-left
  • 59 |
  • .glyphicon .glyphicon-arrow-right
  • 60 |
  • .glyphicon .glyphicon-arrow-up
  • 61 |
  • .glyphicon .glyphicon-asterisk
  • 62 |
  • .glyphicon .glyphicon-backward
  • 63 |
  • .glyphicon .glyphicon-ban-circle
  • 64 |
  • .glyphicon .glyphicon-barcode
  • 65 |
  • .glyphicon .glyphicon-bell
  • 66 |
  • .glyphicon .glyphicon-bold
  • 67 |
  • .glyphicon .glyphicon-book
  • 68 |
  • .glyphicon .glyphicon-bookmark
  • 69 |
  • .glyphicon .glyphicon-briefcase
  • 70 |
  • .glyphicon .glyphicon-bullhorn
  • 71 |
  • .glyphicon .glyphicon-calendar
  • 72 |
  • .glyphicon .glyphicon-camera
  • 73 |
  • .glyphicon .glyphicon-certificate
  • 74 |
  • .glyphicon .glyphicon-check
  • 75 |
  • .glyphicon .glyphicon-chevron-down
  • 76 |
  • .glyphicon .glyphicon-chevron-left
  • 77 |
  • .glyphicon .glyphicon-chevron-right
  • 78 |
  • .glyphicon .glyphicon-chevron-up
  • 79 |
  • .glyphicon .glyphicon-circle-arrow-down
  • 80 |
  • .glyphicon .glyphicon-circle-arrow-left
  • 81 |
  • .glyphicon .glyphicon-circle-arrow-right
  • 82 |
  • .glyphicon .glyphicon-circle-arrow-up
  • 83 |
  • .glyphicon .glyphicon-cloud
  • 84 |
  • .glyphicon .glyphicon-cloud-download
  • 85 |
  • .glyphicon .glyphicon-cloud-upload
  • 86 |
  • .glyphicon .glyphicon-cog
  • 87 |
  • .glyphicon .glyphicon-collapse-down
  • 88 |
  • .glyphicon .glyphicon-collapse-up
  • 89 |
  • .glyphicon .glyphicon-comment
  • 90 |
  • .glyphicon .glyphicon-compressed
  • 91 |
  • .glyphicon .glyphicon-copyright-mark
  • 92 |
  • .glyphicon .glyphicon-credit-card
  • 93 |
  • .glyphicon .glyphicon-cutlery
  • 94 |
  • .glyphicon .glyphicon-dashboard
  • 95 |
  • .glyphicon .glyphicon-download
  • 96 |
  • .glyphicon .glyphicon-download-alt
  • 97 |
  • .glyphicon .glyphicon-earphone
  • 98 |
  • .glyphicon .glyphicon-edit
  • 99 |
  • .glyphicon .glyphicon-eject
  • 100 |
  • .glyphicon .glyphicon-envelope
  • 101 |
  • .glyphicon .glyphicon-euro
  • 102 |
  • .glyphicon .glyphicon-exclamation-sign
  • 103 |
  • .glyphicon .glyphicon-expand
  • 104 |
  • .glyphicon .glyphicon-export
  • 105 |
  • .glyphicon .glyphicon-eye-close
  • 106 |
  • .glyphicon .glyphicon-eye-open
  • 107 |
  • .glyphicon .glyphicon-facetime-video
  • 108 |
  • .glyphicon .glyphicon-fast-backward
  • 109 |
  • .glyphicon .glyphicon-fast-forward
  • 110 |
  • .glyphicon .glyphicon-file
  • 111 |
  • .glyphicon .glyphicon-film
  • 112 |
  • .glyphicon .glyphicon-filter
  • 113 |
  • .glyphicon .glyphicon-fire
  • 114 |
  • .glyphicon .glyphicon-flag
  • 115 |
  • .glyphicon .glyphicon-flash
  • 116 |
  • .glyphicon .glyphicon-floppy-disk
  • 117 |
  • .glyphicon .glyphicon-floppy-open
  • 118 |
  • .glyphicon .glyphicon-floppy-remove
  • 119 |
  • .glyphicon .glyphicon-floppy-save
  • 120 |
  • .glyphicon .glyphicon-floppy-saved
  • 121 |
  • .glyphicon .glyphicon-folder-close
  • 122 |
  • .glyphicon .glyphicon-folder-open
  • 123 |
  • .glyphicon .glyphicon-font
  • 124 |
  • .glyphicon .glyphicon-forward
  • 125 |
  • .glyphicon .glyphicon-fullscreen
  • 126 |
  • .glyphicon .glyphicon-gbp
  • 127 |
  • .glyphicon .glyphicon-gift
  • 128 |
  • .glyphicon .glyphicon-glass
  • 129 |
  • .glyphicon .glyphicon-globe
  • 130 |
  • .glyphicon .glyphicon-hand-down
  • 131 |
  • .glyphicon .glyphicon-hand-left
  • 132 |
  • .glyphicon .glyphicon-hand-right
  • 133 |
  • .glyphicon .glyphicon-hand-up
  • 134 |
  • .glyphicon .glyphicon-hd-video
  • 135 |
  • .glyphicon .glyphicon-hdd
  • 136 |
  • .glyphicon .glyphicon-header
  • 137 |
  • .glyphicon .glyphicon-headphones
  • 138 |
  • .glyphicon .glyphicon-heart
  • 139 |
  • .glyphicon .glyphicon-heart-empty
  • 140 |
  • .glyphicon .glyphicon-home
  • 141 |
  • .glyphicon .glyphicon-import
  • 142 |
  • .glyphicon .glyphicon-inbox
  • 143 |
  • .glyphicon .glyphicon-indent-left
  • 144 |
  • .glyphicon .glyphicon-indent-right
  • 145 |
  • .glyphicon .glyphicon-info-sign
  • 146 |
  • .glyphicon .glyphicon-italic
  • 147 |
  • .glyphicon .glyphicon-leaf
  • 148 |
  • .glyphicon .glyphicon-link
  • 149 |
  • .glyphicon .glyphicon-list
  • 150 |
  • .glyphicon .glyphicon-list-alt
  • 151 |
  • .glyphicon .glyphicon-lock
  • 152 |
  • .glyphicon .glyphicon-log-in
  • 153 |
  • .glyphicon .glyphicon-log-out
  • 154 |
  • .glyphicon .glyphicon-magnet
  • 155 |
  • .glyphicon .glyphicon-map-marker
  • 156 |
  • .glyphicon .glyphicon-minus
  • 157 |
  • .glyphicon .glyphicon-minus-sign
  • 158 |
  • .glyphicon .glyphicon-move
  • 159 |
  • .glyphicon .glyphicon-music
  • 160 |
  • .glyphicon .glyphicon-new-window
  • 161 |
  • .glyphicon .glyphicon-off
  • 162 |
  • .glyphicon .glyphicon-ok
  • 163 |
  • .glyphicon .glyphicon-ok-circle
  • 164 |
  • .glyphicon .glyphicon-ok-sign
  • 165 |
  • .glyphicon .glyphicon-open
  • 166 |
  • .glyphicon .glyphicon-paperclip
  • 167 |
  • .glyphicon .glyphicon-pause
  • 168 |
  • .glyphicon .glyphicon-pencil
  • 169 |
  • .glyphicon .glyphicon-phone
  • 170 |
  • .glyphicon .glyphicon-phone-alt
  • 171 |
  • .glyphicon .glyphicon-picture
  • 172 |
  • .glyphicon .glyphicon-plane
  • 173 |
  • .glyphicon .glyphicon-play
  • 174 |
  • .glyphicon .glyphicon-play-circle
  • 175 |
  • .glyphicon .glyphicon-plus
  • 176 |
  • .glyphicon .glyphicon-plus-sign
  • 177 |
  • .glyphicon .glyphicon-print
  • 178 |
  • .glyphicon .glyphicon-pushpin
  • 179 |
  • .glyphicon .glyphicon-qrcode
  • 180 |
  • .glyphicon .glyphicon-question-sign
  • 181 |
  • .glyphicon .glyphicon-random
  • 182 |
  • .glyphicon .glyphicon-record
  • 183 |
  • .glyphicon .glyphicon-refresh
  • 184 |
  • .glyphicon .glyphicon-registration-mark
  • 185 |
  • .glyphicon .glyphicon-remove
  • 186 |
  • .glyphicon .glyphicon-remove-circle
  • 187 |
  • .glyphicon .glyphicon-remove-sign
  • 188 |
  • .glyphicon .glyphicon-repeat
  • 189 |
  • .glyphicon .glyphicon-resize-full
  • 190 |
  • .glyphicon .glyphicon-resize-horizontal
  • 191 |
  • .glyphicon .glyphicon-resize-small
  • 192 |
  • .glyphicon .glyphicon-resize-vertical
  • 193 |
  • .glyphicon .glyphicon-retweet
  • 194 |
  • .glyphicon .glyphicon-road
  • 195 |
  • .glyphicon .glyphicon-save
  • 196 |
  • .glyphicon .glyphicon-saved
  • 197 |
  • .glyphicon .glyphicon-screenshot
  • 198 |
  • .glyphicon .glyphicon-sd-video
  • 199 |
  • .glyphicon .glyphicon-search
  • 200 |
  • .glyphicon .glyphicon-send
  • 201 |
  • .glyphicon .glyphicon-share
  • 202 |
  • .glyphicon .glyphicon-share-alt
  • 203 |
  • .glyphicon .glyphicon-shopping-cart
  • 204 |
  • .glyphicon .glyphicon-signal
  • 205 |
  • .glyphicon .glyphicon-sort
  • 206 |
  • .glyphicon .glyphicon-sort-by-alphabet
  • 207 |
  • .glyphicon .glyphicon-sort-by-alphabet-alt
  • 208 |
  • .glyphicon .glyphicon-sort-by-attributes
  • 209 |
  • .glyphicon .glyphicon-sort-by-attributes-alt
  • 210 |
  • .glyphicon .glyphicon-sort-by-order
  • 211 |
  • .glyphicon .glyphicon-sort-by-order-alt
  • 212 |
  • .glyphicon .glyphicon-sound-5-1
  • 213 |
  • .glyphicon .glyphicon-sound-6-1
  • 214 |
  • .glyphicon .glyphicon-sound-7-1
  • 215 |
  • .glyphicon .glyphicon-sound-dolby
  • 216 |
  • .glyphicon .glyphicon-sound-stereo
  • 217 |
  • .glyphicon .glyphicon-star
  • 218 |
  • .glyphicon .glyphicon-star-empty
  • 219 |
  • .glyphicon .glyphicon-stats
  • 220 |
  • .glyphicon .glyphicon-step-backward
  • 221 |
  • .glyphicon .glyphicon-step-forward
  • 222 |
  • .glyphicon .glyphicon-stop
  • 223 |
  • .glyphicon .glyphicon-subtitles
  • 224 |
  • .glyphicon .glyphicon-tag
  • 225 |
  • .glyphicon .glyphicon-tags
  • 226 |
  • .glyphicon .glyphicon-tasks
  • 227 |
  • .glyphicon .glyphicon-text-height
  • 228 |
  • .glyphicon .glyphicon-text-width
  • 229 |
  • .glyphicon .glyphicon-th
  • 230 |
  • .glyphicon .glyphicon-th-large
  • 231 |
  • .glyphicon .glyphicon-th-list
  • 232 |
  • .glyphicon .glyphicon-thumbs-down
  • 233 |
  • .glyphicon .glyphicon-thumbs-up
  • 234 |
  • .glyphicon .glyphicon-time
  • 235 |
  • .glyphicon .glyphicon-tint
  • 236 |
  • .glyphicon .glyphicon-tower
  • 237 |
  • .glyphicon .glyphicon-transfer
  • 238 |
  • .glyphicon .glyphicon-trash
  • 239 |
  • .glyphicon .glyphicon-tree-conifer
  • 240 |
  • .glyphicon .glyphicon-tree-deciduous
  • 241 |
  • .glyphicon .glyphicon-unchecked
  • 242 |
  • .glyphicon .glyphicon-upload
  • 243 |
  • .glyphicon .glyphicon-usd
  • 244 |
  • .glyphicon .glyphicon-user
  • 245 |
  • .glyphicon .glyphicon-volume-down
  • 246 |
  • .glyphicon .glyphicon-volume-off
  • 247 |
  • .glyphicon .glyphicon-volume-up
  • 248 |
  • .glyphicon .glyphicon-warning-sign
  • 249 |
  • .glyphicon .glyphicon-wrench
  • 250 |
  • .glyphicon .glyphicon-zoom-in
  • 251 |
  • .glyphicon .glyphicon-zoom-out
  • 252 |
253 |
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 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 | } --------------------------------------------------------------------------------