Repository: MarcGiffing/wicket-spring-boot Branch: master Commit: 74a6f03fb4f8 Files: 243 Total size: 389.0 KB Directory structure: gitextract_rhrs615o/ ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ └── maven.yml ├── .gitignore ├── README.adoc ├── pom.xml ├── wicket-spring-boot-context/ │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── giffing/ │ └── wicket/ │ └── spring/ │ └── boot/ │ └── context/ │ ├── condition/ │ │ ├── ConditionalOnWicket.java │ │ └── WicketSettingsCondition.java │ ├── exceptions/ │ │ ├── WicketSpringBootException.java │ │ └── extensions/ │ │ └── ExtensionMisconfigurationException.java │ ├── extensions/ │ │ ├── ApplicationInitExtension.java │ │ ├── WicketApplicationInitConfiguration.java │ │ ├── boot/ │ │ │ └── actuator/ │ │ │ ├── WicketAutoConfig.java │ │ │ └── WicketEndpointRepository.java │ │ └── types/ │ │ ├── DurationUnit.java │ │ ├── SessionUnit.java │ │ └── TypeParser.java │ ├── scan/ │ │ ├── WicketAccessDeniedPage.java │ │ ├── WicketExpiredPage.java │ │ ├── WicketHomePage.java │ │ ├── WicketInternalErrorPage.java │ │ └── WicketSignInPage.java │ └── security/ │ └── AuthenticatedWebSessionConfig.java ├── wicket-spring-boot-starter/ │ ├── .gitignore │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── giffing/ │ │ └── wicket/ │ │ └── spring/ │ │ └── boot/ │ │ └── starter/ │ │ ├── WicketAutoConfiguration.java │ │ ├── WicketBootWebApplicationAutoConfiguration.java │ │ ├── app/ │ │ │ ├── WicketBootSecuredWebApplication.java │ │ │ ├── WicketBootStandardWebApplication.java │ │ │ ├── WicketBootWebApplication.java │ │ │ ├── classscanner/ │ │ │ │ ├── ClassCandidateScanner.java │ │ │ │ ├── ClassCandidateScannerConfiguration.java │ │ │ │ └── candidates/ │ │ │ │ ├── WicketClassCandidate.java │ │ │ │ └── WicketClassCandidatesHolder.java │ │ │ └── verifier/ │ │ │ ├── WicketDependencyMismatchDetectedException.java │ │ │ ├── WicketDependencyVersionChecker.java │ │ │ ├── WicketDependencyVersionCheckerFailureAnalyzer.java │ │ │ └── WicketDependencyVersionCheckerProperties.java │ │ ├── configuration/ │ │ │ ├── CustomAnnotationBeanNameGenerator.java │ │ │ └── extensions/ │ │ │ ├── WicketExtensionLocation.java │ │ │ ├── core/ │ │ │ │ ├── csrf/ │ │ │ │ │ ├── CsrfAttacksPreventionConfig.java │ │ │ │ │ └── CsrfAttacksPreventionProperties.java │ │ │ │ ├── datastore/ │ │ │ │ │ ├── DataStoreHttpSessionConfig.java │ │ │ │ │ └── DataStoreHttpSessionProperties.java │ │ │ │ ├── requestmapper/ │ │ │ │ │ ├── CryptMapperConfig.java │ │ │ │ │ └── CryptMapperProperties.java │ │ │ │ ├── resources/ │ │ │ │ │ └── SpringBootMainClassResourceRegistration.java │ │ │ │ ├── resourcesettings/ │ │ │ │ │ └── packageresourceguard/ │ │ │ │ │ ├── PackageResourceGuardConfig.java │ │ │ │ │ └── PackageResourceGuardProperties.java │ │ │ │ ├── serializer/ │ │ │ │ │ └── deflated/ │ │ │ │ │ ├── DeflatedJavaSerializerConfig.java │ │ │ │ │ └── DeflatedJavaSerializerProperties.java │ │ │ │ └── settings/ │ │ │ │ ├── application/ │ │ │ │ │ └── ApplicationSettingsConfig.java │ │ │ │ ├── debug/ │ │ │ │ │ ├── DebugSettingsConfig.java │ │ │ │ │ └── DebugSettingsProperties.java │ │ │ │ ├── exceptions/ │ │ │ │ │ ├── ExceptionSettingsConfig.java │ │ │ │ │ └── ExceptionSettingsProperties.java │ │ │ │ ├── general/ │ │ │ │ │ └── GeneralSettingsProperties.java │ │ │ │ ├── markup/ │ │ │ │ │ ├── MarkupSettingsConfig.java │ │ │ │ │ └── MarkupSettingsProperties.java │ │ │ │ ├── pagestore/ │ │ │ │ │ ├── StoreSettingsConfig.java │ │ │ │ │ └── StoreSettingsProperties.java │ │ │ │ ├── requestlogger/ │ │ │ │ │ ├── RequestLoggerSettingsConfig.java │ │ │ │ │ └── RequestLoggerSettingsProperties.java │ │ │ │ ├── requrestcycle/ │ │ │ │ │ ├── RequestCycleSettingsConfig.java │ │ │ │ │ └── RequestCycleSettingsProperties.java │ │ │ │ └── resource/ │ │ │ │ ├── ResourceSettingsConfig.java │ │ │ │ └── ResourceSettingsProperties.java │ │ │ ├── external/ │ │ │ │ ├── beanvalidation/ │ │ │ │ │ ├── BeanValidationConfig.java │ │ │ │ │ └── BeanValidationProperties.java │ │ │ │ ├── development/ │ │ │ │ │ ├── devutils/ │ │ │ │ │ │ ├── diskstorebrowser/ │ │ │ │ │ │ │ ├── DiskStoreBrowserConfig.java │ │ │ │ │ │ │ └── DiskStoreBrowserProperties.java │ │ │ │ │ │ ├── inspector/ │ │ │ │ │ │ │ ├── InspectorConfig.java │ │ │ │ │ │ │ └── InspectorProperties.java │ │ │ │ │ │ └── statelesschecker/ │ │ │ │ │ │ ├── StatelessCheckerConfig.java │ │ │ │ │ │ └── StatelessCheckerProperties.java │ │ │ │ │ ├── springboot/ │ │ │ │ │ │ └── devtools/ │ │ │ │ │ │ ├── SpringDevToolsProperties.java │ │ │ │ │ │ ├── SpringDevToolsSerializer.java │ │ │ │ │ │ ├── SpringDevtoolsSerializerConfig.java │ │ │ │ │ │ └── WicketDevToolsPropertyDefaultsPostProcessor.java │ │ │ │ │ └── wicketsource/ │ │ │ │ │ ├── WicketSourceConfig.java │ │ │ │ │ └── WicketSourceProperties.java │ │ │ │ ├── spring/ │ │ │ │ │ ├── boot/ │ │ │ │ │ │ └── actuator/ │ │ │ │ │ │ ├── WicketEndpoint.java │ │ │ │ │ │ ├── WicketEndpointConfiguration.java │ │ │ │ │ │ └── WicketEndpointRepositoryDefault.java │ │ │ │ │ └── security/ │ │ │ │ │ ├── SecureWebSession.java │ │ │ │ │ ├── SpringSecurityConfig.java │ │ │ │ │ └── SpringSecurityProperties.java │ │ │ │ └── webjars/ │ │ │ │ ├── WebjarsConfig.java │ │ │ │ └── WebjarsProperties.java │ │ │ └── stuff/ │ │ │ ├── annotationscan/ │ │ │ │ ├── AnnotatedMountScannerConfig.java │ │ │ │ └── AnnotatedMountScannerProperties.java │ │ │ ├── datastore/ │ │ │ │ ├── cassandra/ │ │ │ │ │ ├── DataStoreCassandraConfig.java │ │ │ │ │ └── DataStoreCassandraProperties.java │ │ │ │ ├── hazelcast/ │ │ │ │ │ ├── DataStoreHazelcastConfig.java │ │ │ │ │ └── DataStoreHazelcastProperties.java │ │ │ │ ├── memcached/ │ │ │ │ │ ├── DataStoreMemcachedConfig.java │ │ │ │ │ └── DataStoreMemcachedProperties.java │ │ │ │ └── redis/ │ │ │ │ ├── DataStoreRedisConfig.java │ │ │ │ └── DataStoreRedisProperties.java │ │ │ ├── htmlcompressor/ │ │ │ │ ├── HTMLCompressingConfig.java │ │ │ │ └── HTMLCompressingProperties.java │ │ │ ├── monitoring/ │ │ │ │ └── jamon/ │ │ │ │ ├── BootJamonAdminPage.java │ │ │ │ ├── JamonConfig.java │ │ │ │ ├── JamonCssResourceReference.java │ │ │ │ ├── JamonProperties.java │ │ │ │ └── css/ │ │ │ │ └── jamon.css │ │ │ ├── restannotations/ │ │ │ │ ├── RestAnnotationsConfig.java │ │ │ │ └── RestAnnotationsProperties.java │ │ │ ├── serializer/ │ │ │ │ ├── fast2/ │ │ │ │ │ ├── WicketSerializerFast2Config.java │ │ │ │ │ └── WicketSerializerFast2Properties.java │ │ │ │ └── kryo2/ │ │ │ │ ├── WicketSerializerKryo2Config.java │ │ │ │ └── WicketSerializerKryo2Properties.java │ │ │ ├── shiro/ │ │ │ │ ├── ShiroSecurityConfig.java │ │ │ │ └── ShiroSecurityProperties.java │ │ │ └── springreference/ │ │ │ ├── SpringReferenceConfig.java │ │ │ └── SpringReferenceProperties.java │ │ └── web/ │ │ ├── WicketWebInitializer.java │ │ ├── WicketWebInitializerProperties.java │ │ ├── config/ │ │ │ ├── WicketWebInitializerAutoConfig.java │ │ │ └── WicketWebInitializerConfig.java │ │ └── servlet/ │ │ ├── standard/ │ │ │ └── StandardWicketWebInitializer.java │ │ └── websocket/ │ │ ├── DummyWicketSessionResolver.java │ │ ├── WebSocketMessageBroadcaster.java │ │ ├── WebSocketMessageSenderDefault.java │ │ ├── WebSocketWicketWebInitializer.java │ │ ├── WicketServerEndpointConfigRegister.java │ │ └── WicketSessionResolver.java │ └── resources/ │ ├── META-INF/ │ │ ├── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ ├── spring-devtools.properties │ │ └── spring.factories │ └── application-development.yml └── wicket-spring-boot-starter-example/ ├── .gitignore ├── pom.xml └── src/ ├── main/ │ ├── doc/ │ │ └── index.adoc │ ├── java/ │ │ └── com/ │ │ └── giffing/ │ │ └── wicket/ │ │ └── spring/ │ │ └── boot/ │ │ └── example/ │ │ ├── WicketApplication.java │ │ ├── WicketApplication.properties │ │ ├── model/ │ │ │ └── Customer.java │ │ ├── repository/ │ │ │ ├── DefaultFilter.java │ │ │ ├── Domain.java │ │ │ ├── Filter.java │ │ │ ├── FilterService.java │ │ │ ├── Sort.java │ │ │ └── services/ │ │ │ ├── DefaultRepositoryService.java │ │ │ └── customer/ │ │ │ ├── CustomerRepository.java │ │ │ ├── CustomerRepositoryService.java │ │ │ ├── CustomerRepositoryServiceImpl.java │ │ │ ├── filter/ │ │ │ │ ├── CustomerFilter.java │ │ │ │ └── CustomerSort.java │ │ │ └── specs/ │ │ │ └── CustomerSpecs.java │ │ └── web/ │ │ ├── SpringBootWebPackageIdentifier.java │ │ ├── assets/ │ │ │ └── base/ │ │ │ ├── CustomStylesCssRessourceReference.java │ │ │ └── custom.css │ │ ├── general/ │ │ │ ├── action/ │ │ │ │ └── panel/ │ │ │ │ ├── ActionPanel.html │ │ │ │ ├── ActionPanel.java │ │ │ │ └── items/ │ │ │ │ ├── AbstractActionItemLink.html │ │ │ │ ├── AbstractActionItemLink.java │ │ │ │ ├── AbstrractActionItem.java │ │ │ │ ├── links/ │ │ │ │ │ ├── ActionItemLink.html │ │ │ │ │ └── ActionItemLink.java │ │ │ │ └── yesno/ │ │ │ │ └── YesNoLink.java │ │ │ └── notify/ │ │ │ ├── NotyJSReference.java │ │ │ ├── NotyPackagedJSReference.java │ │ │ ├── NotyThemeBootstrapJSReference.java │ │ │ ├── jquery.noty.js │ │ │ └── noty.theme.bootstrap.js │ │ ├── html/ │ │ │ ├── autocomplete/ │ │ │ │ ├── AutoCompleteTextField.css │ │ │ │ └── AutoCompleteTextField.java │ │ │ ├── basic/ │ │ │ │ └── YesNoLabel.java │ │ │ ├── border/ │ │ │ │ └── behavior/ │ │ │ │ ├── ValidationMsgBehavior.html │ │ │ │ └── ValidationMsgBehavior.java │ │ │ ├── form/ │ │ │ │ ├── ValidationForm.java │ │ │ │ ├── ValidationFormVisitor.java │ │ │ │ └── focus/ │ │ │ │ └── FocusBehaviour.java │ │ │ ├── modal/ │ │ │ │ ├── YesNoModal.html │ │ │ │ ├── YesNoModal.java │ │ │ │ ├── YesNoPanel.html │ │ │ │ └── YesNoPanel.java │ │ │ ├── panel/ │ │ │ │ ├── FeedbackPanel.html │ │ │ │ └── FeedbackPanel.java │ │ │ └── repeater/ │ │ │ └── data/ │ │ │ └── table/ │ │ │ └── filter/ │ │ │ ├── AbstractCheckBoxFilter.html │ │ │ ├── AbstractCheckBoxFilter.java │ │ │ ├── AbstractTextFieldFilter.html │ │ │ └── AbstractTextFieldFilter.java │ │ ├── pages/ │ │ │ ├── BaseAuthenticatedPage.html │ │ │ ├── BaseAuthenticatedPage.java │ │ │ ├── BaseAuthenticatedPage.properties │ │ │ ├── BasePage.html │ │ │ ├── BasePage.java │ │ │ ├── WicketBootstrapInitializer.java │ │ │ ├── customers/ │ │ │ │ ├── CustomerListPage.html │ │ │ │ ├── CustomerListPage.java │ │ │ │ ├── create/ │ │ │ │ │ ├── CustomerCreatePage.html │ │ │ │ │ └── CustomerCreatePage.java │ │ │ │ ├── edit/ │ │ │ │ │ ├── CustomerEditPage.java │ │ │ │ │ └── CustomerEditPage.properties │ │ │ │ ├── events/ │ │ │ │ │ ├── CustomerChangedEvent.java │ │ │ │ │ └── CustomerDeletedEvent.java │ │ │ │ └── model/ │ │ │ │ ├── CustomerDataProvider.java │ │ │ │ ├── UsernameSearchTextField.java │ │ │ │ └── UsernameTextField.java │ │ │ ├── errors/ │ │ │ │ ├── AccessDeniedPage.html │ │ │ │ ├── AccessDeniedPage.java │ │ │ │ ├── ExpiredPage.html │ │ │ │ ├── ExpiredPage.java │ │ │ │ ├── InternalErrorPage.html │ │ │ │ └── InternalErrorPage.java │ │ │ ├── footer/ │ │ │ │ ├── Footer.html │ │ │ │ └── Footer.java │ │ │ ├── login/ │ │ │ │ ├── LoginPage.html │ │ │ │ └── LoginPage.java │ │ │ └── websocket/ │ │ │ ├── ChatPage.css │ │ │ ├── ChatPage.html │ │ │ ├── ChatPage.java │ │ │ ├── ChatPage.properties │ │ │ ├── ChatParticipant.java │ │ │ ├── ChatService.java │ │ │ └── events/ │ │ │ ├── CustomerMessageEvent.java │ │ │ ├── JoinChatEvent.java │ │ │ └── LeftChatEvent.java │ │ ├── security/ │ │ │ ├── SpringSecurityWicketSessionResolver.java │ │ │ └── WicketWebSecurityAdapterConfig.java │ │ └── wicket/ │ │ └── dataprovider/ │ │ └── DefaultDataProvider.java │ └── resources/ │ ├── META-INF/ │ │ └── spring-devtools.properties │ ├── application.yml │ ├── db/ │ │ └── changelog/ │ │ ├── db.changelog-master.xml │ │ ├── init.xml │ │ ├── table_customer.xml │ │ └── test-data.xml │ └── logback.xml └── test/ ├── java/ │ ├── com/ │ │ └── giffing/ │ │ └── wicket/ │ │ └── spring/ │ │ └── boot/ │ │ └── example/ │ │ └── web/ │ │ ├── WicketBaseIntTest.java │ │ ├── WicketBaseTest.java │ │ ├── WicketMockServletContext.java │ │ ├── pages/ │ │ │ └── customers/ │ │ │ ├── CustomerListIntTest.java │ │ │ ├── CustomerListPageTest.java │ │ │ ├── create/ │ │ │ │ ├── CustomerCreatePageIntTest.java │ │ │ │ └── CustomerCreatePageTest.java │ │ │ └── edit/ │ │ │ └── CustomerEditPageTest.java │ │ └── security/ │ │ └── WicketWebSecurityAdapterTestConfig.java │ └── test/ │ └── com/ │ └── giffing/ │ └── wicket/ │ └── spring/ │ └── boot/ │ └── example/ │ └── web/ │ └── WicketWebApplicationConfig.java └── resources/ ├── application.yml └── db/ └── changelog/ └── db.changelog-master-test.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/dependabot.yml ================================================ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 updates: - package-ecosystem: "maven" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "weekly" ================================================ FILE: .github/workflows/maven.yml ================================================ # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven # This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support # documentation. name: Java CI with Maven on: push: branches: [ "master" ] pull_request: branches: [ "master" ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' cache: maven - name: Build with Maven run: mvn -B package --file pom.xml # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive #- name: Update dependency graph # uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 ================================================ FILE: .gitignore ================================================ /target/ /.settings/ .classpath .project .idea/ *.iml ================================================ FILE: README.adoc ================================================ = Wicket autoconfiguration with Spring Boot Current release version: * 4.1.x - http://wicket.apache.org/[Wicket 10.6] with Spring Boot 3.5.x - Branch master * 4.0.x - http://wicket.apache.org/[Wicket 10.0] with Spring Boot 3.2.x - Branch master NOTE: http://search.maven.org/#search|ga|1|com.giffing.wicket.spring.boot NOTE: Example projects: https://github.com/MarcGiffing/wicket-spring-boot-examples * <> * <> * <> * <> * <> * <> * <> [[introduction]] == Introduction https://dzone.com/articles/enhance-wicket-with-spring-boot *by Andrea Del Bene* This project makes it easy to create a new Wicket projects with a minimum of configuration effort. It uses Spring Boot to autoconfigure Wickets core and its extension (related projects like wicketstuff, beanvalidation...) with an reasonable default value which can be overridden over property files. * Core Features ** Configures an embedded Servlet Container by default (Spring Boot Feature). ** Autoconfiguration of the needed link:wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/web/WicketWebInitializer.java[Wicket Servlet filters]. ** Autoconfiguration for Spring/Spring-Security and dependency injection support with @SpringBean. ** Autoconfiguration of Wicket <>. ** Faster development support with <> [[getting_started]] == Getting started To get started you have to create a new Maven Project (or another preferred dependency/build-management tool) and add the *wicket-spring-boot-starter* dependency to your configuration. [source,xml] ---- com.giffing.wicket.spring.boot.starter wicket-spring-boot-starter ---- Beside the Maven dependency configuration we need the following steps to do . Create a class which is marked with @SpringBootApplication - see http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-using-springbootapplication-annotation[Springs documentation] . Create your HomePage class (with HTML) which will me marked with @WicketHomePage [source,java] ---- @SpringBootApplication public class WicketApplication { public static void main(String[] args) throws Exception { new SpringApplicationBuilder() .sources(WicketApplication.class) .run(args); } } @WicketHomePage public class HomePage extends WebPage { } You content ---- Thats all! When you execute the main method you will get a fully working and configured Wicket application. An embedded Tomcat is automatically started. In a usal Wicket application you would like to provide custom configuration in Wickets WebApplication init() method. You could create your own 'extension' by creating a bean which implements WicketApplicationInitConfiguration [source,java] ---- @ApplicationInitExtension public class YouExtensionConif implements WicketApplicationInitConfiguration { @Override public void init(WebApplication webApplication) { // your custom configuration } } ---- If this configuration is not enough and you want to override special methods of Wickets WebApplication class you have to create a bean which extends one of the following two classes * WicketBootStandardWebApplication - Without Security * WicketBootSecuredWebApplication - With Security - You'll need a security provider like <> [source,java] ---- @Component public class WicketWebApplication extends WicketBootSecuredWebApplication { @Override protected void init() { super.init(); } } ---- The custom WicketWebApplication is automatically picked up instead of the default provided one. You can also override the getHomePage method() if you don't want to use the special @WicketHomePage annotations to mark the home page. To package the application as an executable jar you have to add the spring-boot-maven-plugin. [source,xml] ---- org.springframework.boot spring-boot-maven-plugin ---- Resource files (html, css, js, ...) are not copied to the target folder if placed in the *src/main/java* folder. You have to tell Maven to copy these files: [source,xml] ---- src/main/resources src/main/java ** **/*.java ---- [[how_does_it_work]] == How does it work? To fully understand how http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-developing-auto-configuration[Spring Boots autconfiguration] and in general Spring Boot works you should read the excellent http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/[documentation] from this fantastic http://projects.spring.io/spring-boot/[project]. As an an example we will look to the AnnotatedMountScanner configuration. The https://github.com/wicketstuff/core/wiki/Annotation[annotated mount scanner] is an project which supports bookmarkable URLs configured by annotations on WebPage classes. If you have this '@MountPath("login")' annotation on a WebPage then the Page is mounted to 'http://localhost/login'. In this project each configuration is separated in two classes to configure this particular feature/extension. The extension consists of a property and a configuration class. The property class holds properties to configure the specific feature. In the AnnotatedMountScannerProperties class we found two properties: [source,java] ---- @ConfigurationProperties(prefix = AnnotatedMountScannerProperties.PROPERTY_PREFIX) public class AnnotatedMountScannerProperties { public static final String PROPERTY_PREFIX = "wicket.stuff.annotationscan"; /** * @see AnnotatedMountScannerConfig */ private boolean enabled = true; /** * An alternative package name for scanning for mount path if the * WicketApplication should not used as the root scan package */ private String packagename; ---- This property file can be imported in the configuration class AnnotatedMountScannerConfig. [source,java] ---- /** * Auto configuration for the {@link AnnotatedMountScanner}. * * It uses the user defined {@link WebApplication} as the default package scan * root directory. * * Enables annotate mount scanner if the following two condition matches: * * 1. The {@link AnnotatedMountScanner} is in the classpath. * * 2. The property {@link AnnotatedMountScannerProperties#PROPERTY_PREFIX} * .enabled is true (default = true) * * * @author Marc Giffing * */ @ApplicationInitExtension @ConditionalOnProperty(prefix = AnnotatedMountScannerProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = org.wicketstuff.annotation.scan.AnnotatedMountScanner.class) @EnableConfigurationProperties({ AnnotatedMountScannerProperties.class }) public class AnnotatedMountScannerConfig implements WicketApplicationInitConfiguration { @Autowired private AnnotatedMountScannerProperties prop; @Override public void init(WebApplication webApplication) { String packagename = webApplication.getClass().getPackage().getName(); if (prop.getPackagename() != null) { packagename = prop.getPackagename(); } new AnnotatedMountScanner().scanPackage(packagename).mount(webApplication); } } ---- If all conditions on the AnnotatedMountScannerConfig matches the configuration class is configured as a spring bean. All Spring beans which implements the interface WicketApplicationInitConfiguration will be injected as a list in the default WicketBootWebApplication class. In the WicketBootWebApplication class we iterate in Wickets init method over the list and call on each the init method to configure the application. [source,java] ---- public class WicketBootWebApplication extends AuthenticatedWebApplication { @Autowired(required = false) private List configurations = new ArrayList<>(); @Override protected void init() { super.init(); for (WicketApplicationInitConfiguration configuration : configurations) { configuration.init(this); } } } ---- [[spring_profile_dev_configuration]] === Spring profile configuration The Wicket Spring Boot Starter project ships with a default development configuration. It can be activated by activating the 'development' Spring profile in the main class or over external JVM/Maven arguments. The default configuration can be overridden with a custom property file. See Spring Boots reference documentation http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config[here]. include::wicket-spring-boot-starter/src/main/resources/application-development.yml[] [source,yml] ---- wicket: core: settings: general: configuration-type: development debug: enabled: true component-use-check: true development-utilities-enabled: true stuff: htmlcompressor: enabled: false features: removeComments: false removeMultiSpaces: false removeIntertagSpaces: false removeQuotes: false compressJavaScript: false compressCss: false simpleDoctype: false removeScriptAttributes: false removeStyleAttributes: false removeLinkAttributes: false removeFormAttributes: false removeInputAttributes: false simpleBooleanAttributes: false removeJavaScriptProtocol: false removeHttpProtocol: false removeHttpsProtocol: false preserveLineBreaks: false external: development: devutils: statelesschecker: enabled: true interceptor: enable-live-sessions-page: true diskstorebrowser: enabled: true wicketsource: enabled: true ---- [[custom-conditions]] == Custom conditions This section lists custom conditional configuration like Spring Boot ones. === @ConditionalOnWicket With the ConditionOnWicket annotation you can check that configuration classes only apply on a specific Wicket major version. If some functionality is only available on Wicket 7 you can use this annotation. [source,java] ---- @ApplicationInitExtension @ConditionalOnWicket(value=7, range=Range.EQUALS_OR_HIGHER) public ConditionalConfig implements WicketApplicationInitConfiguration{ @Override public void init(WebApplication webApplication) { // configuration option which only apply to Wickets major version 7 or higher } } ---- [[extension]] == Extensions The following section describes the current extensions and the required dependencies. An extension is a custom labeling in this project which is used to auto-configure a specific part of an Wicket application. An extension may require an external dependency or is using Wickets core features. See section <> to get a deeper knowledge. * <> ** DEPLOYMENT-CONFIGURATIONS *** <> *** <> *** <> *** <> *** <> *** <> *** <> *** <> *** <> *** <> *** <> *** <> *** <> *** <> *** <> *** <> *** <> ** DEVELOPMENT-CONFIGURATIONS *** <> *** <> *** <> *** <> *** <> *** <> [[extension-general]] === General Wicket can be started in DEVELOPMENT and DEPLOYMENT mode. You can change the configuration type over the following property configuration. The given property is automatically mapped to Wickets ConfigurationType enumeration. [source,properties] ---- wicket.core.settings.general.configuration-type=development # development/deployment(default) wicket.web.servlet.filter-mapping-param=/* wicket.web.servlet.dispatcher-types=request, error, async # request, error, async, include, forward wicket.web.servlet.init-parameters.*= # map - configuration support for additional servlet init parameters #exception settings wicket.core.settings.exceptions.thread-dump-strategy=thread_holding_lock wicket.core.settings.exceptions.error-handling-strategy-during-ajax-requests=redirect_to_error_page wicket.core.settings.requestcycle.render-strategy=redirect-to-buffer # redirect-to-buffer / one-pass-render / redirect-to-render wicket.core.settings.requestcycle.buffer-response=true wicket.core.settings.requestcycle.gather-extended-browser-info=false wicket.core.settings.requestcycle.response-request-encoding=UTF-8 wicket.core.settings.requestcycle.timeout-size=1 wicket.core.settings.requestcycle.timeout-unit=minutes wicket.core.settings.requestcycle.exception-retry-count=10 #Markup - Settings wicket.core.settings.markup.default-markup-encoding=UTF-8 # if null it uses the system default wicket.core.settings.markup.automatic-linking=false wicket.core.settings.markup.compress-whitespace=false wicket.core.settings.markup.strip-comments=false wicket.core.settings.markup.strip-wicket-tags=true wicket.core.settings.markup.throw-exception-on-missing-xml-declaration=false #RequestLogger - Settings wicket.core.settings.requestlogger.enabled=false wicket.core.settings.requestlogger.record-session-size wicket.core.settings.requestlogger.requests-window-size wicket.core.requestmapper.cryptmapper.enabled=false # URL encryption support wicket.core.settings.pagestore.enabled=false # enables custom store settings wicket.core.settings.pagestore.session-size=2 wicket.core.settings.pagestore.session-unit=megabytes wicket.core.settings.pagestore.asynchronous= # overrides wickets default value only when set wicket.core.settings.pagestore.asynchronous-queue-capacity= # overrides wickets default value only when set wicket.core.settings.pagestore.file-store-folder= # overrides wickets default value only when set wicket.core.settings.pagestore.inmemory-cache-size= # overrides wickets default value only when set ---- If you insert e.g. developmentx you will get a startup error: [listing] Field error in object 'wicket' on field 'configurationType': rejected value [developmentx]; codes [typeMismatch.wicket.configurationType ==== Special Annotations * @WicketHomePage ** A Page marked with this annotation will be configured as the default home page. If multiple WicketHomePage annotation found an exception is thrown. * @WicketSignInPage ** A Page marked with this annotation will be configured as the default login page. A security provider like Spring Security is needed. If multiple annotations found an exception is thrown. * @WicketAccessDeniedPage ** A Page marked with this annotation will be configured as the default access denied page. * @WicketInternalErrorPage ** A Page marked with this annotation will be configured as the default internal error page. * @WicketExpiredPage ** A Page marked with this annotation will be configured as the default expired page. [[extension-spring-security]] === Spring Security This starter detects automatically Spring Security if the Spring Boot Starter Security dependency is added. Internally the WicketBootSecuredWebApplication is used instead of the WicketBootStandardWebApplication class. [source,xml] ---- org.springframework.boot spring-boot-starter-security ---- If you wan't to disable the Spring Security configuration for Wicket use the following property. [source,properties] ---- wicket.external.spring.security=false ---- [[extension-wicket-websocket]] === Wicket Native WebSockets https://ci.apache.org/projects/wicket/guide/7.x/guide/single.html#nativewebsockets[Documentation] This project provides an auto configuration support for native WebSockets. If the required dependencies are in the classpath a JavaxWebSocketFilter servlet filter is configured instead of the default WicketFilter. To simplify the usage of sending WebSocket messages a class named WebSocketMessageBroadcaster is automatically registered as a spring bean. You can inject the class anywhere with @SpringBean and use the 'send' method to send WebSocket messages. [source,properties] ---- wicket.external.websocket=true # enables WebSocket support - dependency required (move documentation to seperated section) ---- [source,xml] ---- org.apache.wicket wicket-native-websocket-javax ---- [[extension-beanvalidation]] === Bean validation Wicket support for JSR 303 Bean validation. See Wickets user guide https://ci.apache.org/projects/wicket/guide/7.x/guide/single.html[Validation with JSR 303] To enable Wickets bean validation you have to add the *wicket-bean-validation* dependency to your project. It will automatically configured and can be used in the project. [source,properties] ---- wicket.external.beanvalidation.enabled=true # enabled by default if bean validation project is present ---- [source,xml] ---- org.apache.wicket wicket-bean-validation ---- [[extension-core-csrf-prevention]] === Core - Prevention of CSRF Attacks [source,properties] ---- wicket.core.csrf.enabled=true wicket.core.csrf.no-origin-action=allow wicket.core.csrf.conflicting-origin-action=abort wicket.core.csrf.error-code=400 wicket.core.csrf.error-message=Origin does not correspond to request wicket.core.csrf.accepted-origins[0]=domain.name.tld # Just the domain name, no protocol wicket.core.csrf.accepted-origins[1]=other-domain.name.tld # Add more origins by increasing the index #TODO: There are some configuration options which should be added ---- [[extension-webjars]] === Webjars https://github.com/l0rdn1kk0n/wicket-webjars [source,properties] ---- wicket.external.webjars.enabled=true ---- [source,xml] ---- de.agilecoders.wicket.webjars wicket-webjars ---- [[extension-wicketstuff-annotationscan]] === Wicketstuff - annotationscan Use wicketstuff-annotation to use Java Annotations and class path searching to mount your Wicket pages. See https://github.com/wicketstuff/core/wiki/Annotation[documentation] [source,xml] ---- org.wicketstuff wicketstuff-annotation ---- [source,properties] ---- wicket.stuff.annotationscan.enabled=true wicket.stuff.annotationscan.packagename= ---- [[extension-wicketstuff-htmlcompressor]] === Wicketstuff - htmlcompressor See https://github.com/wicketstuff/core/wiki/Htmlcompressor[documentation] [source,xml] ---- org.wicketstuff wicketstuff-htmlcompressor com.yahoo.platform.yui yuicompressor ---- [source,properties] ---- wicket.stuff.htmlcompressor.enabled=true wicket.stuff.htmlcompressor.features.*= ---- [[extension-core-serializer-deflated]] === Core - serializer-deflated [source,properties] ---- wicket.core.serializer.deflated.enabled=false # has to be explicit enabled. deflates the outputstream, reducing page store size by up to a factor 8 at a price of about 2-20ms ---- [[extension-wicketstuff-serializer-fast2]] === Wicketstuff - serializer-fast2 See https://github.com/wicketstuff/core/wiki/FastSerializer2[documentation] [source,xml] ---- org.wicketstuff wicketstuff-serializer-fast2 ---- [source,properties] ---- wicket.stuff.serializer.fast2.enabled=true ---- [[extension-wicketstuff-serializer-kryo2]] === Wicketstuff - serializer-kryo2 See https://github.com/wicketstuff/core/wiki/Kryo-Serializer[documentation] [source,xml] ---- org.wicketstuff wicketstuff-serializer-kryo2 ---- [source,properties] ---- wicket.stuff.serializer.fast2.enabled=true ---- [[extension-wicketstuff-restannotations]] === Wicketstuff - restannotations See https://github.com/wicketstuff/core/tree/master/wicketstuff-restannotations-parent[documentation] [source,xml] ---- org.wicketstuff wicketstuff-restannotations org.wicketstuff wicketstuff-restannotations-json ---- [source,properties] ---- wicket.stuff.restannotations.enabled=true wicket.stuff.restannotations.packagename= # the package name to scan for project specific annotations ---- [[extension-general-debugsettings]] === General - debugsettings Wicket provides some debug settings which could be [source,properties] ---- wicket.core.settings.debug.enabled=false wicket.core.settings.debug.developmentUtilitiesEnabled=true # Enables all of the panels and pages, etc, from wicket-devutils package. wicket.core.settings.debug.ajaxDebugModeEnabled=true # if true: wicket-ajax-debug.js is added to header wicket.core.settings.debug.componentUseCheck=true wicket.core.settings.debug.outputMarkupContainerClassName=false wicket.core.settings.debug.componentPathAttributeName= ---- [[extension-datastore]] == Datastore [[extension-datastore-httpsession]] === Datastore HttpSession [source,properties] ---- wicket.core.datastore.httpsession.enabled=false wicket.core.datastore.httpsession.pagesNumber=20 # the maximum number of pages the data store can hold ---- [[extension-datastore-cassandra]] === Datastore cassandra See https://github.com/wicketstuff/core/wiki/DataStores[Documentation] [source,properties] ---- wicket.stuff.datastore.cassandra.enabled=true wicket.stuff.datastore.cassandra.contact-points= #comma-separated list wicket.stuff.datastore.cassandra.table-name=pagestore wicket.stuff.datastore.cassandra.keyspace-name=wicket wicket.stuff.datastore.cassandra.record-ttl=30 wicket.stuff.datastore.cassandra.record-ttl-unit=minutes wicket.stuff.datastore.cassandra.session-size=2 wicket.stuff.datastore.cassandra.session-unit=megabytes ---- [source,xml] ---- org.wicketstuff wicketstuff-datastore-cassandra ---- [[extension-datastore-hazelcast]] === Datastore hazelcast See https://github.com/wicketstuff/core/wiki/DataStores[Documentation] [source,properties] ---- wicket.stuff.datastore.hazelcast.enabled=true wicket.stuff.datastore.hazelcast.session-size=2L wicket.stuff.datastore.hazelcast.session-unit=megabytes ---- [source,xml] ---- org.wicketstuff wicketstuff-datastore-hazelcast com.hazelcast hazelcast ---- [[extension-datastore-memcached]] === Datastore memcached See https://github.com/wicketstuff/core/wiki/DataStores[Documentation] [source,properties] ---- wicket.stuff.datastore.memcached.enabled=true wicket.stuff.datastore.memcached.session-size=2L wicket.stuff.datastore.memcached.session-unit=megabytes wicket.stuff.datastore.memcached.expiration-time=30 wicket.stuff.datastore.memcached.port=11211 wicket.stuff.datastore.memcached.server-names= wicket.stuff.datastore.memcached.shutdown-timeout=30 wicket.stuff.datastore.memcached.shutdown-timeout-unit=minutes ---- [source,xml] ---- org.wicketstuff wicketstuff-datastore-memcached ---- [[extension-datastore-redis]] === Datastore redis See https://github.com/wicketstuff/core/wiki/DataStores[Documentation] [source,properties] ---- wicket.stuff.datastore.redis.enabled=true wicket.stuff.datastore.redis.session-size=2L wicket.stuff.datastore.redis.session-unit=megabytes wicket.stuff.datastore.redis.expiration-time=30 wicket.stuff.datastore.redis.port=11211 wicket.stuff.datastore.redis.server-names= wicket.stuff.datastore.redis.shutdown-timeout=30 wicket.stuff.datastore.redis.shutdown-timeout-unit=minutes ---- [source,xml] ---- org.wicketstuff wicketstuff-datastore-redis ---- [[extension-wicketstuff-jamon]] === Wicketstuff - JAMon Used to monitor page requests. Provides a statistic page. See https://github.com/wicketstuff/core/blob/master/jamon-parent/[Github] [source,properties] ---- wicket.stuff.monitoring.jamon.enabled=true wicket.stuff.monitoring.jamon.include-source-name-in-monitor-label=true wicket.stuff.monitoring.jamon.mountPage=/monitoring/jamon # the url to which the statistic page is mounted ---- [source,xml] ---- org.wicketstuff wicketstuff-jamon ---- NOTE: JAMon includes hazelcast to gather statistics. You may need to disable the datastore hazelcast support: <> [[extension-devutils]] === Devutils [source,xml] ---- org.apache.wicket wicket-devutils ---- [[extension-devutils-diskstorebrowser]] ==== Devutils - diskstorebrowser [source,properties] ---- wicket.external.development.devutils.diskstorebrowser.enabled=false wicket.external.development.devutils.diskstorebrowser.mountPage=devutils/diskstore/browser ---- [[extension-devutils-inspector]] ==== Devutils - inspector [source,properties] ---- wicket.external.development.devutils.diskstorebrowser.enabled=false wicket.external.development.devutils.diskstorebrowser.mountPage=devutils/diskstore/browser ---- [[extension-devutils-statelesschecker]] ==== Devutils - statelesschecker [source,properties] ---- wicket.external.development.devutils.interceptor.enableLiveSessionsPage=false wicket.external.development.devutils.interceptor.liveSessionPageMount=devutils/inspector/live-session-page ---- [[extension-wicketsource]] === Wicket-Source See https://github.com/jennybrown8/wicket-source/wiki[documentation] [source,xml] ---- com.github.jennybrown8.wicket-source wicket-source ---- [source,properties] ---- wicket.external.development.wicketsource.enabled=false ---- [[extension-springboot-devtools]] ==== Spring Boot - DevTools See https://spring.io/blog/2015/06/17/devtools-in-spring-boot-1-3[Spring Boot DevTools] The project tries to improve the development-time experience when working with Spring Boot. There is a problem with Wickets default and other serializer (fast2, kryo2...). See https://github.com/MarcGiffing/wicket-spring-boot/issues/29[Issue 29] If the spring-boot-devtools dependency is in the classpath a link:/wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/springboot/devtools/SpringDevToolsSerializer.java[special Spring serializer] will be activated. All other serializer will only be activated if the Spring Boot DevTools dependency is not in the classpath. [source,xml] ---- org.springframework.boot spring-boot-devtools ---- [[war_deployment]] == Configure as WAR for deploy on Servlet Container #115 To run the application as a *war* file in the Servlet Container like Tomcat you have to do the following steps. * Set the packaging to war in your build system (maven, gradle) * Mark the *spring-boot-starter-tomcat* dependency as provided * Use the Spring provided plugins to repackage the project * Extend from SpringBootServletInitializer ** Here you can optionally set configurations which only apply when deployed as a war in a Servlet Container ** http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#build-tool-plugins-maven-packaging[maven] ** http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#build-tool-plugins-gradle-repackage-configuration[gradle] [source,java] ---- @SpringBootApplication public class WicketApplication extends SpringBootServletInitializer { //Can be used while developing public static void main(String[] args) throws Exception { new SpringApplicationBuilder() .sources(WicketApplication.class) .run(args); } //Executed when deployed as a WAR in a Servlet container. @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { builder.properties( WebSocketWicketWebInitializerAutoConfiguration.REGISTER_SERVER_ENDPOINT_ENABLED + "=false" ); return super.configure( builder ); } } ---- If you already extend from a Wicket specific class you can create a separated class which extends from SpringBootServletInitializer (https://github.com/MarcGiffing/wicket-spring-boot/issues/115#issuecomment-311712298). ================================================ FILE: pom.xml ================================================ 4.0.0 com.giffing.wicket.spring.boot.starter wicket-spring-boot-starter-parent 4.1.1 pom Wicket Spring Boot Starter Parent This project (should) makes it easy to create Wicket projects with a minimum of configuration effort. Is uses Spring Boot to autoconfigure Wickets core and extension with an reasonable default value which can be overridden over property files. This parent project holds default configuration for the underlying sub modules org.springframework.boot spring-boot-starter-parent 3.5.5 https://github.com/MarcGiffing/wicket-spring-boot Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0 https://github.com/MarcGiffing/wicket-spring-boot/ scm:git:https://github.com/MarcGiffing/wicket-spring-boot.git wicket-spring-boot-starter-parent-3.1.2 mgiffing Marc Giffing Project lead UTF-8 17 2.0.5 10.6.0 10.6.0 com.giffing.wicket.spring.boot.starter wicket-spring-boot-context ${project.version} com.giffing.wicket.spring.boot.starter wicket-spring-boot-starter ${project.version} org.apache.wicket wicket-core ${wicket.version} org.apache.wicket wicket-auth-roles ${wicket.version} org.apache.wicket wicket-ioc ${wicket.version} org.apache.wicket wicket-extensions ${wicket.version} org.apache.wicket wicket-request ${wicket.version} org.apache.wicket wicket-spring ${wicket.version} org.apache.wicket wicket-util ${wicket.version} org.wicketstuff wicketstuff-annotation ${wicketstuff.version} org.wicketstuff wicketstuff-htmlcompressor ${wicketstuff.version} com.yahoo.platform.yui yuicompressor 2.4.8 org.wicketstuff wicketstuff-serializer-kryo2 ${wicketstuff.version} org.wicketstuff wicketstuff-serializer-fast2 ${wicketstuff.version} org.wicketstuff wicketstuff-restannotations ${wicketstuff.version} org.wicketstuff wicketstuff-restannotations-json ${wicketstuff.version} org.wicketstuff wicketstuff-springreference ${wicketstuff.version} org.wicketstuff.htmlvalidator wicketstuff-htmlvalidator ${wicketstuff.version} org.wicketstuff wicketstuff-jamon ${wicketstuff.version} org.apache.wicket wicket-bean-validation ${wicket.version} de.agilecoders.wicket.webjars wicket-webjars 4.0.11 org.wicketstuff wicketstuff-datastore-common ${wicketstuff.version} org.wicketstuff wicketstuff-datastore-cassandra ${wicketstuff.version} org.wicketstuff wicketstuff-datastore-hazelcast ${wicketstuff.version} org.wicketstuff wicketstuff-datastore-memcached ${wicketstuff.version} org.wicketstuff wicketstuff-datastore-redis ${wicketstuff.version} org.apache.wicket wicket-native-websocket-javax ${wicket.version} org.apache.wicket wicket-devutils ${wicket.version} com.github.jennybrown8.wicket-source wicket-source 9.0.0 org.apache.wicket wicket-tester ${wicket.version} org.wicketstuff wicketstuff-shiro ${wicketstuff.version} org.apache.shiro shiro-spring-boot-web-starter ${apache-shiro.version} ossrh https://s01.oss.sonatype.org/content/repositories/snapshots ossrh https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ release wicket-spring-boot-context wicket-spring-boot-starter wicket-spring-boot-starter-example org.apache.maven.plugins maven-gpg-plugin 3.2.8 sign-artifacts verify sign org.apache.maven.plugins maven-source-plugin 3.3.1 attach-sources jar-no-fork org.apache.maven.plugins maven-javadoc-plugin 3.11.3 attach-javadocs jar ${java.version} org.sonatype.plugins nexus-staging-maven-plugin 1.7.0 true ossrh https://ossrh-staging-api.central.sonatype.com true default true wicket-spring-boot-context wicket-spring-boot-starter wicket-spring-boot-starter-example org.sonatype.plugins nexus-staging-maven-plugin 1.7.0 true ossrh https://s01.oss.sonatype.org/ true apache.snapshots.https https://repository.apache.org/content/repositories/snapshots false true sonatype https://s01.oss.sonatype.org/content/repositories/snapshots false true ================================================ FILE: wicket-spring-boot-context/.gitignore ================================================ /target/ /.settings/ .classpath .project ================================================ FILE: wicket-spring-boot-context/README.md ================================================ # wicket-spring-boot-context Advanced Level, a typical spring-boot user does not need this module. ###Description This module is used to create **custom auto configuration classes**. All necessary code is decoupled, thus a developer does not depend on the wicket-spring-boot-starter module itself. All dependencies in the wicket-spring-boot-context module are scoped as `provided` so the user of the context module is not forced to use any spring/wicket dependencies. Each configuration class should be marked with the annotation **@ApplicationInitExtension** and has to implement `WicketApplicationInitConfiguration`. The module contains also common exceptions which should be used on e.g. configuration errors. Here is also the place for common configuration checks which can be used by other projects. The annotation **@ConditionalOnWicket** can be used to activate a configuration only on a specific version of `Apache Wicket`. ================================================ FILE: wicket-spring-boot-context/pom.xml ================================================ 4.0.0 com.giffing.wicket.spring.boot.starter wicket-spring-boot-starter-parent 4.1.1 wicket-spring-boot-context Context Provides several constructs which allow developers to build custom auto-configuration classes. org.apache.wicket wicket-core provided org.apache.wicket wicket-auth-roles provided org.springframework spring-context-support provided org.projectlombok lombok provided org.springframework.boot spring-boot-autoconfigure provided ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/condition/ConditionalOnWicket.java ================================================ package com.giffing.wicket.spring.boot.context.condition; import org.springframework.context.annotation.Conditional; import java.lang.annotation.*; /** * Conditional annotation to pre-check if an extension should be picked for autoconfiguration. *

* You can define the major Wicket version on which the extension * should or should not be executed. * * @author Marc Giffing */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(WicketSettingsCondition.class) public @interface ConditionalOnWicket { /** * @return The major java version to check with the current value */ int value(); /** * @return Defines how the given major version should be checked with the current version */ Range range() default Range.EQUALS_OR_HIGHER; enum Range { /** * The Wicket major version equals the */ EQUALS, /** * The Wicket major version equals or is newer */ EQUALS_OR_HIGHER, /** * The Wicket major version equals or is lower */ EQUALS_OR_LOWER, } } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/condition/WicketSettingsCondition.java ================================================ package com.giffing.wicket.spring.boot.context.condition; import com.giffing.wicket.spring.boot.context.condition.ConditionalOnWicket.Range; import org.apache.wicket.settings.FrameworkSettings; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.StringUtils; import java.util.Map; /** * Retrieves the current major Wicket Version from the classpath and matches it against * the given conditional value in the {@link ConditionalOnWicket} annotation. * * @author Marc Giffing */ public class WicketSettingsCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { String wicketVersion = retrieveWicketVersion(); Map attributes = metadata .getAnnotationAttributes(ConditionalOnWicket.class.getName()); Range range = (Range) attributes.get("range"); int expectedVersion = (int) attributes.get("value"); String[] splittedWicketVersion = wicketVersion.split("\\."); int majorWicketVersion = Integer.parseInt(splittedWicketVersion[0]); return getMatchOutcome(range, majorWicketVersion, expectedVersion); } protected ConditionOutcome getMatchOutcome(Range range, int runningVersion, int expectedVersion) { boolean match = matches(range, expectedVersion, runningVersion); return new ConditionOutcome(match, getMessage(match, range, runningVersion, expectedVersion)); } private boolean matches(Range range, int expectedVersion, int runningVersion) { return switch (range) { case EQUALS -> runningVersion == expectedVersion; case EQUALS_OR_LOWER -> runningVersion <= expectedVersion; case EQUALS_OR_HIGHER -> runningVersion >= expectedVersion; }; } private String getMessage(boolean matches, Range range, int runningVersion, int expectedVersion) { if (matches) { return "Wicket version matches current: " + runningVersion + " " + range + " expected: " + expectedVersion; } else { return "Wicket version does not match current: " + runningVersion + " " + range + " expected: " + expectedVersion; } } private String retrieveWicketVersion() { String implVersion = null; var pkg = FrameworkSettings.class.getPackage(); if (pkg != null) { implVersion = pkg.getImplementationVersion(); } return StringUtils.hasLength(implVersion) ? implVersion : "0"; } } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/exceptions/WicketSpringBootException.java ================================================ package com.giffing.wicket.spring.boot.context.exceptions; /** * General extension from which all exception should extend * * @author Marc Giffing */ public class WicketSpringBootException extends RuntimeException { public WicketSpringBootException() { super(); } public WicketSpringBootException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } public WicketSpringBootException(String message, Throwable cause) { super(message, cause); } public WicketSpringBootException(String message) { super(message); } public WicketSpringBootException(Throwable cause) { super(cause); } } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/exceptions/extensions/ExtensionMisconfigurationException.java ================================================ package com.giffing.wicket.spring.boot.context.exceptions.extensions; import com.giffing.wicket.spring.boot.context.exceptions.WicketSpringBootException; /** * This exception should be thrown if a extension misconfiguration is detected. *

* It can e.g. thrown if you detect an extension which configures the same aspect * as two serializers * * @author Marc Giffing */ public class ExtensionMisconfigurationException extends WicketSpringBootException { public ExtensionMisconfigurationException(String message) { super(message); } } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/extensions/ApplicationInitExtension.java ================================================ package com.giffing.wicket.spring.boot.context.extensions; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * To be independent of Springs annotation this annotation was introduced * which is a replacement for the {@link Component} annotation. *

* In the future, you may introduce different configuration options. * * @author Marc Giffing */ @Configuration @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Order(ApplicationInitExtension.DEFAULT_PRECEDENCE) public @interface ApplicationInitExtension { int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; int DEFAULT_PRECEDENCE = Integer.MAX_VALUE / 2; int LOWEST_PRECEDENCE = Integer.MAX_VALUE; String value() default ""; } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/extensions/WicketApplicationInitConfiguration.java ================================================ package com.giffing.wicket.spring.boot.context.extensions; import org.apache.wicket.protocol.http.WebApplication; /** * To provide custom modification of the init Method of Wickets {@link WebApplication} * class. An extension class should implement this interface. All classes implementing * this interface are injected in Wickets starter WicketBootWebApplication class as a list and on * each implementation the init method is called with the current {@link WebApplication}. *

* Every Spring Bean regardless to the package location which implements this interface * will be considered as an extension. This means that you can write your own extension * in a different project. You only have to ensure that the class will picked up by Springs * component scan (or other bean configuration possibilities. * * @author Marc Giffing */ public interface WicketApplicationInitConfiguration { /** * With the given {@link WebApplication} the * {@link WicketApplicationInitConfiguration}s can modify/extend the init() * method of the {@link WebApplication}. * * @param webApplication the current {@link WebApplication} */ void init(WebApplication webApplication); } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/extensions/boot/actuator/WicketAutoConfig.java ================================================ package com.giffing.wicket.spring.boot.context.extensions.boot.actuator; import java.io.Serializable; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import lombok.Getter; import org.springframework.util.Assert; @Getter public class WicketAutoConfig implements Serializable { private final String key; private final Map details; private WicketAutoConfig(Builder builder) { Assert.notNull(builder, "Builder must not be null"); this.key = builder.configurationClass != null ? builder.configurationClass.getSimpleName() : null; this.details = Collections.unmodifiableMap(builder.details); } public static class Builder { private final Class configurationClass; private Map details; public Builder() { this.configurationClass = null; this.details = new LinkedHashMap<>(); } public Builder(Class configurationClass) { Assert.notNull(configurationClass, "ConfigurationClass must not be null"); this.configurationClass = configurationClass; this.details = new LinkedHashMap<>(); } public Builder(Class configurationClass, Map details) { Assert.notNull(configurationClass, "ConfigurationClass must not be null"); Assert.notNull(details, "Details must not be null"); this.configurationClass = configurationClass; this.details = new LinkedHashMap<>(details); } public Builder withDetail(String key, Object value) { Assert.notNull(key, "Key must not be null"); this.details.put(key, value); return this; } public WicketAutoConfig build() { return new WicketAutoConfig(this); } } } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/extensions/boot/actuator/WicketEndpointRepository.java ================================================ package com.giffing.wicket.spring.boot.context.extensions.boot.actuator; import java.util.List; /** * This repository can be used to collect Wicket configuration information which can (e.g.) be displayed in * a Spring Boot Actuator Wicket endpoint. * * @author Marc Giffing */ public interface WicketEndpointRepository { void add(WicketAutoConfig autoconfig); List getConfigs(); } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/extensions/types/DurationUnit.java ================================================ package com.giffing.wicket.spring.boot.context.extensions.types; public enum DurationUnit { MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS, } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/extensions/types/SessionUnit.java ================================================ package com.giffing.wicket.spring.boot.context.extensions.types; public enum SessionUnit { BYTES, KILOBYTES, MEGABYTES, TERABYTES } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/extensions/types/TypeParser.java ================================================ package com.giffing.wicket.spring.boot.context.extensions.types; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.apache.wicket.util.lang.Bytes; import com.giffing.wicket.spring.boot.context.exceptions.WicketSpringBootException; import java.time.Duration; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class TypeParser { public static Bytes parse(Long size, SessionUnit sessionUnit){ return switch (sessionUnit) { case BYTES -> Bytes.bytes(size); case KILOBYTES -> Bytes.kilobytes(size); case MEGABYTES -> Bytes.megabytes(size); case TERABYTES -> Bytes.terabytes(size); }; } public static Duration parse(Long time, DurationUnit durationUnit){ return switch (durationUnit) { case DAYS -> Duration.ofDays(time); case HOURS -> Duration.ofHours(time); case MILLISECONDS -> Duration.ofMillis(time); case MINUTES -> Duration.ofMinutes(time); case SECONDS -> Duration.ofSeconds(time); }; } } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/scan/WicketAccessDeniedPage.java ================================================ package com.giffing.wicket.spring.boot.context.scan; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.apache.wicket.Page; import org.apache.wicket.settings.ApplicationSettings; /** * This annotation can be used to mark a {@link Page} which will automatically configured * as the access denied page in the {@link ApplicationSettings}. * * @author Marc Giffing */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface WicketAccessDeniedPage { } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/scan/WicketExpiredPage.java ================================================ package com.giffing.wicket.spring.boot.context.scan; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.apache.wicket.Page; import org.apache.wicket.settings.ApplicationSettings; /** * This annotation can be used to mark a {@link Page} which will automatically configured * as the page expired error page in the {@link ApplicationSettings}. * * @author Marc Giffing */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface WicketExpiredPage { } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/scan/WicketHomePage.java ================================================ package com.giffing.wicket.spring.boot.context.scan; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.apache.wicket.Page; import org.apache.wicket.protocol.http.WebApplication; /** * Should be used to mark the home page which should automatically configures in the {@link WebApplication}. * Only one {@link Page} should be marked with this annotation. * * @author Marc Giffing */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface WicketHomePage { } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/scan/WicketInternalErrorPage.java ================================================ package com.giffing.wicket.spring.boot.context.scan; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.apache.wicket.Page; import org.apache.wicket.settings.ApplicationSettings; /** * This annotation can be used to mark a {@link Page} which will automatically configured * as the internal error page in the {@link ApplicationSettings}. * * @author Marc Giffing */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface WicketInternalErrorPage { } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/scan/WicketSignInPage.java ================================================ package com.giffing.wicket.spring.boot.context.scan; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.apache.wicket.Page; import org.apache.wicket.protocol.http.WebApplication; /** * Should be used to mark the sign-in page which should be automatically configured in the {@link WebApplication} class. * Only one {@link Page} should be marked with this annotation. * * @author Marc Giffing */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface WicketSignInPage { } ================================================ FILE: wicket-spring-boot-context/src/main/java/com/giffing/wicket/spring/boot/context/security/AuthenticatedWebSessionConfig.java ================================================ package com.giffing.wicket.spring.boot.context.security; import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession; import org.apache.wicket.authroles.authentication.AuthenticatedWebApplication; /** * Enables the dynamic configuration of the {@link AbstractAuthenticatedWebSession} for the * {@link AuthenticatedWebApplication}. E.g. if spring security is present it provides a default WebSession * configuration. *

* Security providers should provide different implementations. * * @author Marc Giffing */ public interface AuthenticatedWebSessionConfig { Class getAuthenticatedWebSessionClass(); } ================================================ FILE: wicket-spring-boot-starter/.gitignore ================================================ /target/ /.settings/ /.classpath .project /.apt_generated/ .factorypath ================================================ FILE: wicket-spring-boot-starter/pom.xml ================================================ 4.0.0 com.giffing.wicket.spring.boot.starter wicket-spring-boot-starter-parent 4.1.1 .. wicket-spring-boot-starter Wicket Spring Boot Starter Enables and configures Apache Wicket from within a Spring Boot Application. Used **only** in combination with Spring Boot. com.giffing.wicket.spring.boot.starter wicket-spring-boot-context org.springframework spring-context-support org.springframework.boot spring-boot-autoconfigure org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security provided org.springframework.boot spring-boot-actuator provided org.apache.wicket wicket-core org.apache.wicket wicket-auth-roles org.apache.wicket wicket-ioc org.apache.wicket wicket-spring org.wicketstuff wicketstuff-annotation provided org.wicketstuff wicketstuff-htmlcompressor provided org.wicketstuff wicketstuff-serializer-kryo2 provided org.wicketstuff wicketstuff-serializer-fast2 provided org.apache.wicket wicket-bean-validation provided org.wicketstuff wicketstuff-restannotations provided org.wicketstuff wicketstuff-springreference provided org.wicketstuff wicketstuff-shiro provided org.apache.shiro shiro-spring-boot-web-starter provided org.wicketstuff wicketstuff-jamon provided de.agilecoders.wicket.webjars wicket-webjars provided org.wicketstuff wicketstuff-datastore-cassandra provided org.wicketstuff wicketstuff-datastore-hazelcast provided org.wicketstuff wicketstuff-datastore-memcached provided org.wicketstuff wicketstuff-datastore-redis provided org.apache.wicket wicket-native-websocket-javax provided com.github.jennybrown8.wicket-source wicket-source provided org.apache.wicket wicket-devutils provided org.projectlombok lombok provided org.springframework.boot spring-boot-devtools provided org.springframework.boot spring-boot-configuration-processor true src/main/resources src/main/java ** **/*.java ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/WicketAutoConfiguration.java ================================================ package com.giffing.wicket.spring.boot.starter; import com.giffing.wicket.spring.boot.starter.app.classscanner.ClassCandidateScannerConfiguration; import com.giffing.wicket.spring.boot.starter.app.verifier.WicketDependencyVersionChecker; import com.giffing.wicket.spring.boot.starter.configuration.CustomAnnotationBeanNameGenerator; import com.giffing.wicket.spring.boot.starter.configuration.extensions.WicketExtensionLocation; import com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.general.GeneralSettingsProperties; import com.giffing.wicket.spring.boot.starter.web.WicketWebInitializer; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; /** * The main starter configuration class which will be called by spring. * The class is configured in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports * * @author Marc Giffing */ @Configuration @Import({ WicketDependencyVersionChecker.class, ClassCandidateScannerConfiguration.class, WicketBootWebApplicationAutoConfiguration.class, WicketWebInitializer.class }) @EnableConfigurationProperties({GeneralSettingsProperties.class}) @ComponentScan(basePackageClasses = WicketExtensionLocation.class, nameGenerator = CustomAnnotationBeanNameGenerator.class) public class WicketAutoConfiguration { } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/WicketBootWebApplicationAutoConfiguration.java ================================================ package com.giffing.wicket.spring.boot.starter; import com.giffing.wicket.spring.boot.starter.app.WicketBootSecuredWebApplication; import com.giffing.wicket.spring.boot.starter.app.WicketBootStandardWebApplication; import com.giffing.wicket.spring.boot.starter.app.WicketBootWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Configures the standard {@link WicketBootWebApplication} configuration * without security options. *

* The {@link WicketBootStandardWebApplication} will only be activated if there * is no other {@link WicketBootWebApplication} present and if there is not * {@link WicketBootSecuredWebApplication} present. * * @author Marc Giffing */ @Configuration @ConditionalOnMissingBean({WicketBootSecuredWebApplication.class, WicketBootWebApplication.class}) public class WicketBootWebApplicationAutoConfiguration { @Bean public WicketBootStandardWebApplication wicketBootWebApplication() { return new WicketBootStandardWebApplication(); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/app/WicketBootSecuredWebApplication.java ================================================ package com.giffing.wicket.spring.boot.starter.app; import java.util.ArrayList; import java.util.List; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.wicket.Page; import org.apache.wicket.RuntimeConfigurationType; import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession; import org.apache.wicket.authroles.authentication.AuthenticatedWebApplication; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.spring.injection.annot.SpringComponentInjector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.context.scan.WicketHomePage; import com.giffing.wicket.spring.boot.context.scan.WicketSignInPage; import com.giffing.wicket.spring.boot.context.security.AuthenticatedWebSessionConfig; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidate; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder; import com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.general.GeneralSettingsProperties; /** * Default Wicket Application which should be subclassed by framework clients to * enable autoconfiguration with help of Spring Boot. * * It automatically configures the {@link SpringComponentInjector} to * enable injection of Spring components via the {@link SpringBean} annotation. * * Beside Springs injection support it also automatically enables security support. * You also have to add a security provider like Spring Security (@link SpringSecurityConfig) * or Apache shiro. * * To initialize the application in an decoupled way a list of {@link WicketApplicationInitConfiguration} * is injected as a list in this class. Each configuration item will be called in the init method. * * @author Marc Giffing * */ @RequiredArgsConstructor @Slf4j public class WicketBootSecuredWebApplication extends AuthenticatedWebApplication implements WicketBootWebApplication { @Autowired @Getter private ApplicationContext applicationContext; @Autowired @Getter private GeneralSettingsProperties generalSettingsProperties; /** * Injects all active extension which matches the predefined conditions. May be empty * if no extension matches the given preconditions. */ @Autowired @Getter private List configurations = new ArrayList<>(); @Autowired private WicketClassCandidatesHolder classCandidates; @Autowired private WicketEndpointRepository wicketEndpointRepository; @Override protected void init() { super.init(); getComponentInstantiationListeners().add(new SpringComponentInjector(this, applicationContext)); WicketAutoConfig.Builder builder = new WicketAutoConfig.Builder(this.getClass()); wicketEndpointRepository.add(builder .withDetail("signInPages", classCandidates.getSignInPageCandidates()) .withDetail("homePages", classCandidates.getHomePageCandidates()) .build()); for (WicketApplicationInitConfiguration configuration : configurations) { log.info("init-config: {}", configuration.getClass().getName()); configuration.init(this); } } @Override public RuntimeConfigurationType getConfigurationType() { return generalSettingsProperties.getConfigurationType(); } @Override protected Class getWebSessionClass() { return applicationContext.getBean(AuthenticatedWebSessionConfig.class).getAuthenticatedWebSessionClass(); } @SuppressWarnings("unchecked") @Override protected Class getSignInPageClass() { if(classCandidates.getSignInPageCandidates().isEmpty()){ throw new IllegalStateException("Couldn't find sign in page - please annotate the sign in page with @" + WicketSignInPage.class.getName()); } if(classCandidates.getSignInPageCandidates().size() > 1 ){ String message = "Multiple sign in pages found - please annotate exactly one class with @" + WicketSignInPage.class.getName(); message += "\n"; for (WicketClassCandidate classCandidate : classCandidates.getSignInPageCandidates()) { message += "\t" + classCandidate.getCandidate() + "\n"; } throw new IllegalStateException(message); } return classCandidates.getSignInPageCandidates().iterator().next().getCandidate(); } @SuppressWarnings("unchecked") @Override public Class getHomePage() { if(classCandidates.getHomePageCandidates().isEmpty()){ throw new IllegalStateException("Couldn't find home page - please annotate the home page with @" + WicketHomePage.class.getName()); } if(classCandidates.getHomePageCandidates().size() > 1 ){ String message = "Multiple home pages found - please annotate exactly one class with @" + WicketHomePage.class.getName(); message += "\n"; for(WicketClassCandidate classCandidate : classCandidates.getHomePageCandidates()) { message += "\t" + classCandidate.getCandidate() + "\n"; } throw new IllegalStateException(message); } return classCandidates.getHomePageCandidates().iterator().next().getCandidate(); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/app/WicketBootStandardWebApplication.java ================================================ package com.giffing.wicket.spring.boot.starter.app; import java.util.ArrayList; import java.util.List; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.wicket.Page; import org.apache.wicket.RuntimeConfigurationType; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.spring.injection.annot.SpringComponentInjector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Lazy; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.context.scan.WicketHomePage; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidate; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder; import com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.general.GeneralSettingsProperties; /** * Default Wicket Application which should be subclassed by framework clients to * enable autoconfiguration with help of Spring Boot. * * It automatically configures the {@link SpringComponentInjector} to * enable injection of Spring components via the {@link SpringBean} annotation. * * To initialize the application in an decoupled way a list of {@link WicketApplicationInitConfiguration} * is injected as a list in this class. Each configuration item will be called in the init method. * * @author Marc Giffing * */ @Lazy @Slf4j public class WicketBootStandardWebApplication extends WebApplication implements WicketBootWebApplication { @Autowired @Getter private ApplicationContext applicationContext; @Autowired @Getter private GeneralSettingsProperties generalSettingsProperties; /** * Injects all active extension which matches the predefined conditions. May be empty * if no extension matches the given preconditions. */ @Autowired(required = false) @Getter private List configurations = new ArrayList<>(); @Autowired @Getter private WicketClassCandidatesHolder classCandidates; @Autowired private WicketEndpointRepository wicketEndpointRepository; @Override protected void init() { super.init(); getComponentInstantiationListeners().add(new SpringComponentInjector(this, applicationContext)); WicketAutoConfig.Builder builder = new WicketAutoConfig.Builder(this.getClass()); wicketEndpointRepository.add(builder .withDetail("signInPages", classCandidates.getSignInPageCandidates()) .withDetail("homePages", classCandidates.getHomePageCandidates()) .build()); for (WicketApplicationInitConfiguration configuration : configurations) { log.info("init-config: {}", configuration.getClass().getName()); configuration.init(this); } } @Override public RuntimeConfigurationType getConfigurationType() { return generalSettingsProperties.getConfigurationType(); } @SuppressWarnings("unchecked") @Override public Class getHomePage() { if(classCandidates.getHomePageCandidates().isEmpty()){ throw new IllegalStateException("Couldn't find home page - please annotate the home page with @" + WicketHomePage.class.getName()); } if(classCandidates.getHomePageCandidates().size() > 1 ){ StringBuilder message = new StringBuilder("Multiple home pages found - please annotate exactly one class with @" + WicketHomePage.class.getName()); message.append("\n"); for(var classCandidate : classCandidates.getHomePageCandidates()) { message.append("\t").append(classCandidate.getCandidate()).append("\n"); } throw new IllegalStateException(message.toString()); } return classCandidates.getHomePageCandidates().iterator().next().getCandidate(); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/app/WicketBootWebApplication.java ================================================ package com.giffing.wicket.spring.boot.starter.app; import org.apache.wicket.protocol.http.WebApplication; import com.giffing.wicket.spring.boot.starter.web.WicketWebInitializer; /** * All Wicket Spring Boot Starter {@link WebApplication} classes should * implement this interface to access the current configures * {@link WebApplication} bean within spring. *

* Look at {@link WicketWebInitializer} to see the dynamical retrieval of the * bean name with use of this interface. * * @author Marc Giffing */ public interface WicketBootWebApplication { } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/app/classscanner/ClassCandidateScanner.java ================================================ package com.giffing.wicket.spring.boot.starter.app.classscanner; import com.giffing.wicket.spring.boot.context.scan.*; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidate; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder; import lombok.RequiredArgsConstructor; import org.apache.wicket.Page; import org.apache.wicket.markup.html.WebPage; import org.springframework.beans.factory.BeanFactory; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import java.util.Collection; import java.util.Collections; /** * The candidate class scanner is used to find class with special annotations. * * @author Marc Giffing */ @RequiredArgsConstructor public class ClassCandidateScanner { private final Environment environment; private final ResourceLoader resourceLoader; private final BeanFactory beanFactory; private final ClassLoader classLoader; private final WicketClassCandidatesHolder classCandidates; public void postConstruct() { var scanner = new ClassPathScanningCandidateComponentProvider(false); scanner.setEnvironment(this.environment); scanner.setResourceLoader(this.resourceLoader); scanner.addIncludeFilter(new AnnotationTypeFilter(SpringBootApplication.class)); scanner.addIncludeFilter(new AnnotationTypeFilter(WicketHomePage.class)); scanner.addIncludeFilter(new AnnotationTypeFilter(WicketSignInPage.class)); scanner.addIncludeFilter(new AnnotationTypeFilter(WicketAccessDeniedPage.class)); scanner.addIncludeFilter(new AnnotationTypeFilter(WicketExpiredPage.class)); scanner.addIncludeFilter(new AnnotationTypeFilter(WicketInternalErrorPage.class)); for (var basePackage : getMappingBasePackages(beanFactory)) { if (StringUtils.hasText(basePackage)) { classCandidates.getBasePackages().add(basePackage); var beanDefinitions = scanner.findCandidateComponents(basePackage); for (var beanDefinition : beanDefinitions) { Class beanClass; try { beanClass = ClassUtils.forName(beanDefinition.getBeanClassName(), classLoader); } catch (ClassNotFoundException e) { throw new IllegalStateException(e); } if (beanClass.isAnnotationPresent(WicketHomePage.class)) { classCandidates.getHomePageCandidates() .add(new WicketClassCandidate<>((Class) beanClass)); } if (beanClass.isAnnotationPresent(WicketSignInPage.class)) { classCandidates.getSignInPageCandidates() .add(new WicketClassCandidate<>((Class) beanClass)); } if (beanClass.isAnnotationPresent(WicketAccessDeniedPage.class)) { classCandidates.getAccessDeniedPageCandidates() .add(new WicketClassCandidate<>((Class) beanClass)); } if (beanClass.isAnnotationPresent(WicketExpiredPage.class)) { classCandidates.getExpiredPageCandidates() .add(new WicketClassCandidate<>((Class) beanClass)); } if (beanClass.isAnnotationPresent(WicketInternalErrorPage.class)) { classCandidates.getInternalErrorPageCandidates() .add(new WicketClassCandidate<>((Class) beanClass)); } if (beanClass.isAnnotationPresent(SpringBootApplication.class)) { classCandidates.setSpringBootMainClass(beanClass); } } } } } private static Collection getMappingBasePackages(BeanFactory beanFactory) { try { return AutoConfigurationPackages.get(beanFactory); } catch (IllegalStateException ex) { // no auto-configuration package registered yet return Collections.emptyList(); } } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/app/classscanner/ClassCandidateScannerConfiguration.java ================================================ package com.giffing.wicket.spring.boot.starter.app.classscanner; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder; @Configuration @RequiredArgsConstructor public class ClassCandidateScannerConfiguration { private final Environment environment; private final ResourceLoader resourceLoader; private final BeanFactory beanFactory; @Bean public WicketClassCandidatesHolder pageCandidates() { var result = new WicketClassCandidatesHolder(); // TODO Why is the ClassCandidateScanner initialized? new ClassCandidateScanner(environment, resourceLoader, beanFactory, getClass().getClassLoader(), result) .postConstruct(); return result; } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/app/classscanner/candidates/WicketClassCandidate.java ================================================ package com.giffing.wicket.spring.boot.starter.app.classscanner.candidates; import java.lang.ref.WeakReference; /** * @param candidate class type * @author Marc Giffing */ public class WicketClassCandidate { private WeakReference> candidate; public WicketClassCandidate(Class candidate) { this.candidate = new WeakReference<>(candidate); } public Class getCandidate() { return candidate.get(); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/app/classscanner/candidates/WicketClassCandidatesHolder.java ================================================ package com.giffing.wicket.spring.boot.starter.app.classscanner.candidates; import java.util.HashSet; import java.util.Set; import lombok.Getter; import lombok.Setter; import org.apache.wicket.Page; import org.apache.wicket.markup.html.WebPage; /** * Holds class candidates which should be configured in Wicket. * It holds e.g. home page classes which should be configured in Wicket. * * @author Marc Giffing * */ @Getter @Setter public class WicketClassCandidatesHolder { private Set basePackages = new HashSet<>(); private Class springBootMainClass = null; private Set> homePageCandidates = new HashSet<>(); private Set> signInPageCandidates = new HashSet<>(); private Set> accessDeniedPageCandidates = new HashSet<>(); private Set> expiredPageCandidates = new HashSet<>(); private Set> internalErrorPageCandidates = new HashSet<>(); } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/app/verifier/WicketDependencyMismatchDetectedException.java ================================================ package com.giffing.wicket.spring.boot.starter.app.verifier; import com.giffing.wicket.spring.boot.starter.app.verifier.WicketDependencyVersionChecker.MavenDependency; import lombok.Getter; import lombok.RequiredArgsConstructor; import java.util.List; /** * Should be thrown if the version of wicket dependencies doesn't match the core dependency version * * @author Marc Giffing */ @Getter @RequiredArgsConstructor public class WicketDependencyMismatchDetectedException extends RuntimeException { private final String wicketCoreVersion; private final List dependencies; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/app/verifier/WicketDependencyVersionChecker.java ================================================ package com.giffing.wicket.spring.boot.starter.app.verifier; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ResourceLoaderAware; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternUtils; @Configuration @ConditionalOnProperty(prefix = WicketDependencyVersionCheckerProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @EnableConfigurationProperties({WicketDependencyVersionCheckerProperties.class}) @Slf4j public class WicketDependencyVersionChecker implements ResourceLoaderAware { private static final String DEFAULT_RESOURCE_PATTERN = "/META-INF/maven/**/pom.properties"; private static final String WICKETSTUFF_GROUPID = "org.wicketstuff"; private static final String WICKET_JQUERYUI_GROUPID = "com.googlecode.wicket-jquery-ui"; private static final String WICKET_CORE_GROUPID = "org.apache.wicket"; private ResourcePatternResolver resourcePatternResolver; private final WicketDependencyVersionCheckerProperties props; @Autowired public WicketDependencyVersionChecker(WicketDependencyVersionCheckerProperties props) { this.props = props; } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); } @PostConstruct public void detectWicketDependencyVersionMismatch() { var wicketMavenDependencies = collectWicketMavenDependencies(); List mismatchVersionDependencies = new ArrayList<>(); var versionMismatchFound = new AtomicBoolean(false); Optional wicketCoreVersionOpt = findWicketCoreVersion(wicketMavenDependencies); wicketCoreVersionOpt.ifPresent(wicketCoreVersion -> { for (var mavenDependency : wicketMavenDependencies) { if (mavenDependency.groupId.equals(WICKET_CORE_GROUPID) && !mavenDependency.version.equals(wicketCoreVersion)) { log.error("########## INVALID WICKET VERSION DETECTED - CORE: {} - DEPENDENCY: {}", wicketCoreVersion, mavenDependency); versionMismatchFound.set(true); mismatchVersionDependencies.add(mavenDependency); } else if (mavenDependency.groupId.equals(WICKETSTUFF_GROUPID) || mavenDependency.groupId.equals(WICKET_JQUERYUI_GROUPID)) { var majorWicketCoreVersion = wicketCoreVersion.substring(0, wicketCoreVersion.indexOf('.')); var majorMavenDependencyVersion = mavenDependency.version.substring(0, mavenDependency.version.indexOf('.')); if (!majorWicketCoreVersion.equals(majorMavenDependencyVersion)) { log.error("########## INVALID {} MAJOR VERSION DETECTED - WICKET: {} - DEPENDENCY: {}", mavenDependency.groupId, majorWicketCoreVersion, majorMavenDependencyVersion); versionMismatchFound.set(true); mismatchVersionDependencies.add(mavenDependency); } } } }); if (versionMismatchFound.get() && props.isThrowExceptionOnDependencyVersionMismatch()) { throw new WicketDependencyMismatchDetectedException(wicketCoreVersionOpt.orElse("unknown"), mismatchVersionDependencies); } } private Optional findWicketCoreVersion(List wicketMavenDependencies) { for (var wicketMavenDependency : wicketMavenDependencies) { if (wicketMavenDependency.groupId.equals(WICKET_CORE_GROUPID) && wicketMavenDependency.artifactId.equals("wicket-core")) { return Optional.of(wicketMavenDependency.version); } } return Optional.empty(); } private List collectWicketMavenDependencies() { var packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + DEFAULT_RESOURCE_PATTERN; try { var resources = this.resourcePatternResolver.getResources(packageSearchPath); return collectWicketMavenDependencies(resources); } catch (IOException e) { // do not throw checked exception in @PostConstruct method to provide better JavaEE compatibility throw new IllegalStateException(e); } } private List collectWicketMavenDependencies(Resource[] resources) throws IOException { List wicketMavenDependencies = new ArrayList<>(); for (var resource : resources) { if (resource.isReadable() && resource.getURL().getPath().contains("wicket")) { var props = new Properties(); props.load(resource.getInputStream()); var groupId = (String) props.get("groupId"); var artifactId = (String) props.get("artifactId"); var version = (String) props.get("version"); wicketMavenDependencies.add(new MavenDependency(groupId, artifactId, version)); } } return wicketMavenDependencies; } @RequiredArgsConstructor public static class MavenDependency { public final String groupId; public final String artifactId; public final String version; @Override public String toString() { return groupId + ":" + artifactId + ":" + version; } } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/app/verifier/WicketDependencyVersionCheckerFailureAnalyzer.java ================================================ package com.giffing.wicket.spring.boot.starter.app.verifier; import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; import org.springframework.boot.diagnostics.FailureAnalysis; import com.giffing.wicket.spring.boot.starter.app.verifier.WicketDependencyVersionChecker.MavenDependency; public class WicketDependencyVersionCheckerFailureAnalyzer extends AbstractFailureAnalyzer { @Override protected FailureAnalysis analyze(Throwable rootFailure, WicketDependencyMismatchDetectedException cause) { var descriptionMessage = new StringBuilder("One or more Wicket dependencies (jars) doesn't match the wicket-core dependency.\n\r" + "Wicket Core Version: %s%s".formatted(cause.getWicketCoreVersion(), System.lineSeparator())); for (MavenDependency dependency : cause.getDependencies()) { descriptionMessage.append("\t").append(dependency).append(System.lineSeparator()); } String actionMessage = """ Please check the Wicket versions configured in your dependency management system (Maven, Gradle, ...) You can disable this check via the property: \twicket.verifier.dependencies.enabled=false. You can prevent throwing the exception but still log the detected problems via the property: \twicket.verifier.dependencies.throw-exception-on-dependency-version-mismatch=false"""; return new FailureAnalysis(descriptionMessage.toString(), actionMessage, cause); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/app/verifier/WicketDependencyVersionCheckerProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.app.verifier; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = WicketDependencyVersionCheckerProperties.PROPERTY_PREFIX) @Getter @Setter public class WicketDependencyVersionCheckerProperties { public static final String PROPERTY_PREFIX = "wicket.verifier.dependencies"; private boolean enabled = true; private boolean throwExceptionOnDependencyVersionMismatch = true; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/CustomAnnotationBeanNameGenerator.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.util.StringUtils; /** * To prevent spring bean naming clashes with user defined beans we will * prefix each bean name with wicketextension and capitalize the from spring * generated bean name. * * @author Marc Giffing */ public class CustomAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator { @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { return "wicketextension" + StringUtils.capitalize(super.generateBeanName(definition, registry)); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/WicketExtensionLocation.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions; /** * Marker interface to locate the Wicket Extensions. * This class is used for Springs component scanning. * * @author Marc Giffing */ public interface WicketExtensionLocation { } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/csrf/CsrfAttacksPreventionConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.csrf; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.FetchMetadataResourceIsolationPolicy; import org.apache.wicket.protocol.http.OriginResourceIsolationPolicy; import org.apache.wicket.protocol.http.ResourceIsolationRequestCycleListener; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; /** * Enables CSRF protection if the following condition matches. *

* 1. The {@link ResourceIsolationRequestCycleListener} class is in the classpath. *

* 2. The property {@link CsrfAttacksPreventionProperties#PROPERTY_PREFIX}.enabled has to be true (default = true) *

* The protection should be enabled by default cause the {@link ResourceIsolationRequestCycleListener} is located * in Wickets core project. * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = CsrfAttacksPreventionProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = org.apache.wicket.protocol.http.ResourceIsolationRequestCycleListener.class) @EnableConfigurationProperties({CsrfAttacksPreventionProperties.class}) @RequiredArgsConstructor public class CsrfAttacksPreventionConfig implements WicketApplicationInitConfiguration { private final CsrfAttacksPreventionProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var originResourceIsolationPolicy = new OriginResourceIsolationPolicy(); props.getAcceptedOrigins().forEach(originResourceIsolationPolicy::addAcceptedOrigin); var listener = new ResourceIsolationRequestCycleListener( new FetchMetadataResourceIsolationPolicy(), originResourceIsolationPolicy); listener.setUnknownOutcomeAction(props.getUnknownOutcomeAction()); listener.setDisallowedOutcomeAction(props.getDisallowedOutcomeAction()); listener.setErrorCode(props.getErrorCode()); listener.setErrorMessage(props.getErrorMessage()); webApplication.getRequestCycleListeners().add(listener); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/csrf/CsrfAttacksPreventionProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.csrf; import java.util.ArrayList; import java.util.List; import lombok.Getter; import lombok.Setter; import org.apache.wicket.protocol.http.ResourceIsolationRequestCycleListener.CsrfAction; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = CsrfAttacksPreventionProperties.PROPERTY_PREFIX) @Getter @Setter public class CsrfAttacksPreventionProperties { public static final String PROPERTY_PREFIX = "wicket.core.csrf"; /** * Action to perform when a request is disallowed by a resource isolation policy. * Default is {@link CsrfAction#ABORT}. */ private CsrfAction disallowedOutcomeAction = CsrfAction.ABORT; /** * Action to perform when none of the resource isolation policies can come to an outcome. * Default is {@link CsrfAction#ABORT}. */ private CsrfAction unknownOutcomeAction = CsrfAction.ABORT; /** * The error code to report when the action to take for a CSRF request is * {@link CsrfAction#ABORT}. Default {@code 400 BAD REQUEST}. */ private int errorCode = jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST; /** * The error message to report when the action to take for a CSRF request is {@code ERROR}. * Default {@code "Origin does not correspond to request"}. */ private String errorMessage = "Origin does not correspond to request"; /** * A white list of accepted origins (host names/domain names) presented as * <domainname>.<TLD>. The domain part can contain subdomains. */ private List acceptedOrigins = new ArrayList<>(); /** * Enables Wickets CSRF protection */ private boolean enabled = true; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/datastore/DataStoreHttpSessionConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.datastore; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import lombok.RequiredArgsConstructor; import org.apache.wicket.DefaultPageManagerProvider; import org.apache.wicket.pageStore.IPageStore; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.wicketstuff.shiro.wicket.page.store.SessionPageStore; @ApplicationInitExtension @ConditionalOnProperty(prefix = DataStoreHttpSessionProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = false) @EnableConfigurationProperties({DataStoreHttpSessionProperties.class}) @RequiredArgsConstructor public class DataStoreHttpSessionConfig implements WicketApplicationInitConfiguration { private final DataStoreHttpSessionProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { webApplication.setPageManagerProvider(new DefaultPageManagerProvider(webApplication) { @Override protected IPageStore newPersistentStore() { return new SessionPageStore(webApplication.getName(), props.getPagesNumber()); } }); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/datastore/DataStoreHttpSessionProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.datastore; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = DataStoreHttpSessionProperties.PROPERTY_PREFIX) @Getter @Setter public class DataStoreHttpSessionProperties { public static final String PROPERTY_PREFIX = "wicket.core.datastore.httpsession"; private boolean enabled = true; /** * the maximum number of pages the data store can hold */ private int pagesNumber = 20; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/requestmapper/CryptMapperConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.requestmapper; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import lombok.RequiredArgsConstructor; import org.apache.wicket.core.request.mapper.CryptoMapper; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.core.annotation.Order; @ApplicationInitExtension @ConditionalOnProperty(prefix = CryptMapperProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = false) @EnableConfigurationProperties({ CryptMapperProperties.class }) @Order(ApplicationInitExtension.DEFAULT_PRECEDENCE + 100) @RequiredArgsConstructor public class CryptMapperConfig implements WicketApplicationInitConfiguration { private final CryptMapperProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { webApplication.setRootRequestMapper(new CryptoMapper(webApplication.getRootRequestMapper(), webApplication)); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/requestmapper/CryptMapperProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.requestmapper; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(CryptMapperProperties.PROPERTY_PREFIX) public class CryptMapperProperties { public static final String PROPERTY_PREFIX = "wicket.core.requestmapper.cryptmapper"; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/resources/SpringBootMainClassResourceRegistration.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.resources; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.resource.loader.ClassStringResourceLoader; @ApplicationInitExtension @RequiredArgsConstructor public class SpringBootMainClassResourceRegistration implements WicketApplicationInitConfiguration { private final WicketClassCandidatesHolder classCandidates; @Override public void init(WebApplication webApplication) { if (classCandidates.getSpringBootMainClass() != null) { webApplication.getResourceSettings().getStringResourceLoaders().add(new ClassStringResourceLoader(classCandidates.getSpringBootMainClass())); } } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/resourcesettings/packageresourceguard/PackageResourceGuardConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.resourcesettings.packageresourceguard; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import lombok.RequiredArgsConstructor; import org.apache.wicket.markup.html.SecurePackageResourceGuard; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; @ApplicationInitExtension @EnableConfigurationProperties({PackageResourceGuardProperties.class}) @RequiredArgsConstructor public class PackageResourceGuardConfig implements WicketApplicationInitConfiguration { private final PackageResourceGuardProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var packageResourceGuard = webApplication.getResourceSettings().getPackageResourceGuard(); if (packageResourceGuard instanceof SecurePackageResourceGuard guard) { for (String pattern : props.getPattern()) { guard.addPattern(pattern); } } wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/resourcesettings/packageresourceguard/PackageResourceGuardProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.resourcesettings.packageresourceguard; import java.util.ArrayList; import java.util.List; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(PackageResourceGuardProperties.PROPERTY_PREFIX) @Getter @Setter public class PackageResourceGuardProperties { public static final String PROPERTY_PREFIX = "wicket.core.resourcesettings.packageresourceguard"; private List pattern = new ArrayList<>(); } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/serializer/deflated/DeflatedJavaSerializerConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.serializer.deflated; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.serialize.ISerializer; import org.apache.wicket.serialize.java.DeflatedJavaSerializer; import org.apache.wicket.serialize.java.JavaSerializer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import com.giffing.wicket.spring.boot.context.exceptions.extensions.ExtensionMisconfigurationException; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; /** * Enables the deflated java serializer if the following condition matches. *

* 1. The property {@link DeflatedJavaSerializerProperties#PROPERTY_PREFIX}.enabled has to be true (default = false) * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = DeflatedJavaSerializerProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = false) @EnableConfigurationProperties({DeflatedJavaSerializerProperties.class}) @RequiredArgsConstructor public class DeflatedJavaSerializerConfig implements WicketApplicationInitConfiguration { private final DeflatedJavaSerializerProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var currentSerializer = webApplication.getFrameworkSettings().getSerializer(); if (currentSerializer instanceof JavaSerializer) { webApplication.getFrameworkSettings().setSerializer(new DeflatedJavaSerializer(webApplication.getApplicationKey())); } else { throw new ExtensionMisconfigurationException("DeflatedJavaSerializer: There is already another serializer configured " + currentSerializer); } wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/serializer/deflated/DeflatedJavaSerializerProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.serializer.deflated; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(DeflatedJavaSerializerProperties.PROPERTY_PREFIX) @Getter @Setter public class DeflatedJavaSerializerProperties { public static final String PROPERTY_PREFIX = "wicket.core.serializer.deflated"; /** * Toggle debug settings */ private boolean enabled = false; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/application/ApplicationSettingsConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.application; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig.Builder; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.context.scan.WicketAccessDeniedPage; import com.giffing.wicket.spring.boot.context.scan.WicketExpiredPage; import com.giffing.wicket.spring.boot.context.scan.WicketInternalErrorPage; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidate; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder; import lombok.RequiredArgsConstructor; import org.apache.wicket.Page; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.settings.ApplicationSettings; import java.util.ArrayList; import java.util.List; @ApplicationInitExtension @RequiredArgsConstructor public class ApplicationSettingsConfig implements WicketApplicationInitConfiguration { private final WicketClassCandidatesHolder holder; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var applicationSettings = webApplication.getApplicationSettings(); var builder = new WicketAutoConfig.Builder(this.getClass()); configureExpiredPage(applicationSettings, new ArrayList<>(holder.getExpiredPageCandidates()), builder); configureAccessDeniedPage(applicationSettings, new ArrayList<>(holder.getAccessDeniedPageCandidates()), builder); configureInternalErrorPage(applicationSettings, new ArrayList<>(holder.getInternalErrorPageCandidates()), builder); wicketEndpointRepository.add(builder.build()); } private void configureInternalErrorPage(ApplicationSettings applicationSettings, List> internalErrorPageCandidates, Builder builder) { Class candidate = null; if (!internalErrorPageCandidates.isEmpty()) { if (internalErrorPageCandidates.size() == 1) { WicketClassCandidate internalErrorPage = internalErrorPageCandidates.get(0); candidate = internalErrorPage.getCandidate(); applicationSettings.setInternalErrorPage(internalErrorPage.getCandidate()); } else { throwExceptionOnMultipleAnnotations(WicketInternalErrorPage.class, internalErrorPageCandidates); } } builder.withDetail("internalErrorPage", candidate); } private void configureAccessDeniedPage(ApplicationSettings applicationSettings, List> accessDeniedPageCandidates, Builder builder) { Class candidate = null; if (!accessDeniedPageCandidates.isEmpty()) { if (accessDeniedPageCandidates.size() == 1) { WicketClassCandidate accessDeniedPage = accessDeniedPageCandidates.get(0); candidate = accessDeniedPage.getCandidate(); applicationSettings.setAccessDeniedPage(accessDeniedPage.getCandidate()); } else { throwExceptionOnMultipleAnnotations(WicketAccessDeniedPage.class, accessDeniedPageCandidates); } } builder.withDetail("accessDeniedPage", candidate); } private void configureExpiredPage(ApplicationSettings applicationSettings, List> expiredPageCandidates, Builder builder) { Class candidate = null; if (!expiredPageCandidates.isEmpty()) { if (expiredPageCandidates.size() == 1) { WicketClassCandidate expiredPageCandidate = expiredPageCandidates.get(0); applicationSettings.setPageExpiredErrorPage(expiredPageCandidate.getCandidate()); candidate = expiredPageCandidate.getCandidate(); builder.withDetail("expiredPage", expiredPageCandidate.getCandidate().getName()); } else { throwExceptionOnMultipleAnnotations(WicketExpiredPage.class, expiredPageCandidates); } } builder.withDetail("expiredPage", candidate); } private void throwExceptionOnMultipleAnnotations(Class pageClass, List> expiredPageCandidates) throws IllegalAccessError { var message = new StringBuilder("Multiple annotation of %s found%n".formatted(pageClass.getName())); for (WicketClassCandidate classCandidate : expiredPageCandidates) { message.append("\t").append(classCandidate.getCandidate()).append("\n"); } throw new IllegalAccessError(message.toString()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/debug/DebugSettingsConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.debug; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.settings.DebugSettings; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; /** * Enables debug settings if the following condition matches. *

* 1. The property {@link DebugSettingsProperties#PROPERTY_PREFIX}.enabled has to be true (default = false) * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = DebugSettingsProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = false) @EnableConfigurationProperties({DebugSettingsProperties.class}) @RequiredArgsConstructor public class DebugSettingsConfig implements WicketApplicationInitConfiguration { private final DebugSettingsProperties properties; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { if (properties.isEnabled()) { DebugSettings debugSettings = webApplication.getDebugSettings(); debugSettings.setDevelopmentUtilitiesEnabled(properties.isDevelopmentUtilitiesEnabled()); debugSettings.setAjaxDebugModeEnabled(properties.isAjaxDebugModeEnabled()); debugSettings.setComponentUseCheck(properties.isComponentUseCheck()); debugSettings.setLinePreciseReportingOnAddComponentEnabled(properties.isLinePreciseReportingOnAddComponentEnabled()); debugSettings.setLinePreciseReportingOnNewComponentEnabled(properties.isLinePreciseReportingOnNewComponentEnabled()); debugSettings.setOutputMarkupContainerClassName(properties.isOutputMarkupContainerClassName()); debugSettings.setComponentPathAttributeName(properties.getComponentPathAttributeName()); } wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", properties) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/debug/DebugSettingsProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.debug; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(DebugSettingsProperties.PROPERTY_PREFIX) @Getter @Setter public class DebugSettingsProperties { public static final String PROPERTY_PREFIX = "wicket.core.settings.debug"; /** * Toggle debug settings */ private boolean enabled; private boolean developmentUtilitiesEnabled = true; private boolean ajaxDebugModeEnabled = true; private boolean componentUseCheck = true; private boolean outputMarkupContainerClassName = false; private String componentPathAttributeName; private boolean linePreciseReportingOnNewComponentEnabled; private boolean linePreciseReportingOnAddComponentEnabled; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/exceptions/ExceptionSettingsConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.exceptions; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.settings.ExceptionSettings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; /** * Configuration options for Wickets exception settings. I don't know if someone needs this * settings and when they don't need my javadoc :-) * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = ExceptionSettingsProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @EnableConfigurationProperties({ExceptionSettingsProperties.class}) @RequiredArgsConstructor public class ExceptionSettingsConfig implements WicketApplicationInitConfiguration { private final ExceptionSettingsProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var exceptionSettings = webApplication.getExceptionSettings(); exceptionSettings.setAjaxErrorHandlingStrategy(props.getErrorHandlingStrategyDuringAjaxRequests()); exceptionSettings.setThreadDumpStrategy(props.getThreadDumpStrategy()); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/exceptions/ExceptionSettingsProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.exceptions; import lombok.Getter; import lombok.Setter; import org.apache.wicket.settings.ExceptionSettings.AjaxErrorStrategy; import org.apache.wicket.settings.ExceptionSettings.ThreadDumpStrategy; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(ExceptionSettingsProperties.PROPERTY_PREFIX) @Getter @Setter public class ExceptionSettingsProperties { public static final String PROPERTY_PREFIX = "wicket.core.settings.exceptions"; private ThreadDumpStrategy threadDumpStrategy = ThreadDumpStrategy.THREAD_HOLDING_LOCK; private AjaxErrorStrategy errorHandlingStrategyDuringAjaxRequests = AjaxErrorStrategy.REDIRECT_TO_ERROR_PAGE; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/general/GeneralSettingsProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.general; import lombok.Getter; import lombok.Setter; import org.apache.wicket.RuntimeConfigurationType; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "wicket.core.settings.general") @Getter @Setter public class GeneralSettingsProperties { /** * Defines the configuration startup mode. It uses Wickets * {@link RuntimeConfigurationType} */ private RuntimeConfigurationType configurationType = RuntimeConfigurationType.DEPLOYMENT; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/markup/MarkupSettingsConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.markup; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.settings.MarkupSettings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; /** * Configuration for the markup settings. * * @author Marc Giffing */ @ApplicationInitExtension @EnableConfigurationProperties({MarkupSettingsProperties.class}) @RequiredArgsConstructor public class MarkupSettingsConfig implements WicketApplicationInitConfiguration { private final MarkupSettingsProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var markupSettings = webApplication.getMarkupSettings(); if (props.getDefaultMarkupEncoding() != null) { markupSettings.setDefaultMarkupEncoding(props.getDefaultMarkupEncoding()); } markupSettings.setAutomaticLinking(props.isAutomaticLinking()); markupSettings.setCompressWhitespace(props.isCompressWhitespace()); markupSettings.setStripComments(props.isStripComments()); markupSettings.setStripWicketTags(props.isStripWicketTags()); markupSettings.setThrowExceptionOnMissingXmlDeclaration(props.isThrowExceptionOnMissingXmlDeclaration()); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/markup/MarkupSettingsProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.markup; import lombok.Getter; import lombok.Setter; import org.apache.wicket.markup.MarkupFactory; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = MarkupSettingsProperties.PROPERTY_PREFIX) @Getter @Setter public class MarkupSettingsProperties { public static final String PROPERTY_PREFIX = "wicket.core.settings.markup"; /** * Application default for automatically resolving hrefs */ private boolean automaticLinking = false; /** * True if multiple tabs/spaces should be compressed to a single space */ private boolean compressWhitespace = false; /** * Default markup encoding. If null, the OS default will be used */ private String defaultMarkupEncoding; /** * Factory for creating markup parsers */ private MarkupFactory markupFactory; /** * if true than throw an exception if the xml declaration is missing from the markup file */ private boolean throwExceptionOnMissingXmlDeclaration = false; /** * Should HTML comments be stripped during rendering? */ private boolean stripComments = false; /** * If true, wicket tags ( ) and wicket:id attributes we be removed from output */ private boolean stripWicketTags = true; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/pagestore/StoreSettingsConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.pagestore; import java.io.File; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.settings.StoreSettings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.context.extensions.types.TypeParser; @ApplicationInitExtension @ConditionalOnProperty(prefix = StoreSettingsProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @EnableConfigurationProperties({StoreSettingsProperties.class}) @RequiredArgsConstructor public class StoreSettingsConfig implements WicketApplicationInitConfiguration { private final StoreSettingsProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var storeSettings = webApplication.getStoreSettings(); if (props.getAsynchronous() != null) { storeSettings.setAsynchronous(props.getAsynchronous()); } if (props.getAsynchronousQueueCapacity() != null) { storeSettings.setAsynchronousQueueCapacity(props.getAsynchronousQueueCapacity()); } if (props.getFileStoreFolder() != null) { storeSettings.setFileStoreFolder(new File(props.getFileStoreFolder())); } storeSettings.setMaxSizePerSession(TypeParser.parse(props.getSessionSize(), props.getSessionUnit())); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/pagestore/StoreSettingsProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.pagestore; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import com.giffing.wicket.spring.boot.context.extensions.types.SessionUnit; @ConfigurationProperties(StoreSettingsProperties.PROPERTY_PREFIX) @Getter @Setter public class StoreSettingsProperties { public static final String PROPERTY_PREFIX = "wicket.core.settings.pagestore"; /** * Toggle debug settings */ private boolean enabled; private Long sessionSize = 2L; private SessionUnit sessionUnit = SessionUnit.MEGABYTES; private Boolean asynchronous; private Integer asynchronousQueueCapacity; private String fileStoreFolder; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/requestlogger/RequestLoggerSettingsConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.requestlogger; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; /** * Configuration options for Wickets exception settings. I don't know if someone needs this * settings and when they don't need my javadoc :-) * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = RequestLoggerSettingsProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = false) @EnableConfigurationProperties({RequestLoggerSettingsProperties.class}) @RequiredArgsConstructor public class RequestLoggerSettingsConfig implements WicketApplicationInitConfiguration { private final RequestLoggerSettingsProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var wicketsRequestLoggerSettings = webApplication.getRequestLoggerSettings(); wicketsRequestLoggerSettings.setRequestLoggerEnabled(props.isEnabled()); wicketsRequestLoggerSettings.setRecordSessionSize(props.isRecordSessionSize()); wicketsRequestLoggerSettings.setRequestsWindowSize(props.getRequestsWindowSize()); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/requestlogger/RequestLoggerSettingsProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.requestlogger; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(RequestLoggerSettingsProperties.PROPERTY_PREFIX) @Getter @Setter public class RequestLoggerSettingsProperties { public static final String PROPERTY_PREFIX = "wicket.core.settings.requestlogger"; private boolean enabled = false; private boolean recordSessionSize = true; private int requestsWindowSize = 0; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/requrestcycle/RequestCycleSettingsConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.requrestcycle; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.settings.RequestCycleSettings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.context.extensions.types.TypeParser; @ApplicationInitExtension @ConditionalOnProperty(prefix = RequestCycleSettingsProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @EnableConfigurationProperties({RequestCycleSettingsProperties.class}) @RequiredArgsConstructor public class RequestCycleSettingsConfig implements WicketApplicationInitConfiguration { private final RequestCycleSettingsProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var requestCycleSettings = webApplication.getRequestCycleSettings(); requestCycleSettings.setRenderStrategy(props.getRenderStrategy()); requestCycleSettings.setBufferResponse(props.isBufferResponse()); requestCycleSettings.setExceptionRetryCount(props.getExceptionRetryCount()); requestCycleSettings.setGatherExtendedBrowserInfo(props.isGatherExtendedBrowserInfo()); requestCycleSettings.setResponseRequestEncoding(props.getResponseRequestEncoding()); requestCycleSettings.setTimeout(TypeParser.parse(props.getTimeoutSize(), props.getTimeoutUnit())); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/requrestcycle/RequestCycleSettingsProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.requrestcycle; import lombok.Getter; import lombok.Setter; import org.apache.wicket.settings.RequestCycleSettings.RenderStrategy; import org.springframework.boot.context.properties.ConfigurationProperties; import com.giffing.wicket.spring.boot.context.extensions.types.DurationUnit; @ConfigurationProperties(prefix = RequestCycleSettingsProperties.PROPERTY_PREFIX) @Getter @Setter public class RequestCycleSettingsProperties { public static final String PROPERTY_PREFIX = "wicket.core.settings.requestcycle"; private RenderStrategy renderStrategy = RenderStrategy.REDIRECT_TO_BUFFER; private boolean bufferResponse = true; private boolean gatherExtendedBrowserInfo = false; private String responseRequestEncoding = "UTF-8"; private Long timeoutSize = 1L; private DurationUnit timeoutUnit = DurationUnit.MINUTES; private int exceptionRetryCount = 10; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/resource/ResourceSettingsConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.resource; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import java.time.Duration; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; @ApplicationInitExtension @ConditionalOnProperty(prefix = ResourceSettingsProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = false) @EnableConfigurationProperties({ResourceSettingsProperties.class}) @RequiredArgsConstructor public class ResourceSettingsConfig implements WicketApplicationInitConfiguration { private final ResourceSettingsProperties properties; @Override public void init(WebApplication webApplication) { webApplication.getResourceSettings().setUseMinifiedResources(properties.isUseMinifiedResources()); if (properties.getResourcePollFrequencySeconds() > 0) { webApplication.getResourceSettings().setResourcePollFrequency(Duration.ofSeconds(properties.getResourcePollFrequencySeconds())); } } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/core/settings/resource/ResourceSettingsProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.resource; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(ResourceSettingsProperties.PROPERTY_PREFIX) @Getter @Setter public class ResourceSettingsProperties { public static final String PROPERTY_PREFIX = "wicket.core.settings.resource"; private boolean useMinifiedResources = true; private int resourcePollFrequencySeconds; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/beanvalidation/BeanValidationConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.beanvalidation; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import lombok.RequiredArgsConstructor; import org.apache.wicket.bean.validation.BeanValidationConfiguration; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.stereotype.Component; /** * Enables the bean validation support if the following condition matches *

* 1. The {@link BeanValidationConfiguration} class is in the classpath. side * note: Bean validation required an validation implementation like hibernate * validator. *

* 2. The property wicket.beanvalidation.enabled has to be true (default = true) * * @author Marc Giffing */ @ApplicationInitExtension @Component @ConditionalOnProperty(prefix = BeanValidationProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = org.apache.wicket.bean.validation.BeanValidationConfiguration.class) @EnableConfigurationProperties({BeanValidationProperties.class}) @RequiredArgsConstructor public class BeanValidationConfig implements WicketApplicationInitConfiguration { private final BeanValidationProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { new BeanValidationConfiguration().configure(webApplication); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/beanvalidation/BeanValidationProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.beanvalidation; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = BeanValidationProperties.PROPERTY_PREFIX) @Getter @Setter public class BeanValidationProperties { public static final String PROPERTY_PREFIX = "wicket.external.beanvalidation"; /** * Enables or disables the bean validation */ private boolean enabled; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/devutils/diskstorebrowser/DiskStoreBrowserConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.devutils.diskstorebrowser; import lombok.RequiredArgsConstructor; import org.apache.wicket.DefaultPageManagerProvider; import org.apache.wicket.devutils.pagestore.PageStorePage; import org.apache.wicket.markup.html.pages.BrowserInfoPage; import org.apache.wicket.pageStore.DiskPageStore; import org.apache.wicket.pageStore.IPageStore; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.settings.StoreSettings; import org.apache.wicket.util.lang.Bytes; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import java.io.File; /** * Mounts the {@link PageStorePage} if the following condition matches * * 1. The {@link PageStorePage} is in the classpath * * 2. The disk store browser page is enabled in the property (default=false) * * @author Marc Giffing * */ @ApplicationInitExtension @ConditionalOnProperty(prefix = DiskStoreBrowserProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = false) @ConditionalOnClass(value = PageStorePage.class) @EnableConfigurationProperties({ DiskStoreBrowserProperties.class }) @RequiredArgsConstructor public class DiskStoreBrowserConfig implements WicketApplicationInitConfiguration { private final DiskStoreBrowserProperties properties; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { webApplication.setPageManagerProvider(new DefaultPageManagerProvider(webApplication) { @Override protected IPageStore newPersistentStore() { var storeSettings = application.getStoreSettings(); var fileStoreFolder = storeSettings.getFileStoreFolder(); var maxSizePerSession = storeSettings.getMaxSizePerSession(); return new DiskPageStore(webApplication.getName(), fileStoreFolder, maxSizePerSession); } }); webApplication.mountPage(properties.getMountPage(), BrowserInfoPage.class); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", properties) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/devutils/diskstorebrowser/DiskStoreBrowserProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.devutils.diskstorebrowser; import lombok.Getter; import lombok.Setter; import org.apache.wicket.devutils.pagestore.PageStorePage; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = DiskStoreBrowserProperties.PROPERTY_PREFIX) @Getter @Setter public class DiskStoreBrowserProperties { public static final String PROPERTY_PREFIX = "wicket.external.development.devutils.diskstorebrowser"; /** * If enabled the {@link PageStorePage} should be mounted test page. *

* It is required that the deployment configuration is set to DEVELOPMENT. */ private boolean enabled; /** * The default mount page for the disk store browser. */ private String mountPage = "devutils/diskstore/browser"; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/devutils/inspector/InspectorConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.devutils.inspector; import lombok.RequiredArgsConstructor; import org.apache.wicket.devutils.inspector.LiveSessionsPage; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.context.properties.EnableConfigurationProperties; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; /** * Mounts pages from the devutils inspector package. Currently only the * {@link LiveSessionsPage} is supported and is mounted if the following * condition matches. *

* 1. The {@link LiveSessionsPage} is in the classpath. *

* 2. The property enableLiveSessionsPage is enabled * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnClass(value = {org.apache.wicket.devutils.inspector.LiveSessionsPage.class,}) @EnableConfigurationProperties({InspectorProperties.class}) @RequiredArgsConstructor public class InspectorConfig implements WicketApplicationInitConfiguration { private final InspectorProperties properties; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { if (properties.isEnableLiveSessionsPage()) { webApplication.mountPage(properties.getLiveSessionPageMount(), LiveSessionsPage.class); } wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", properties) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/devutils/inspector/InspectorProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.devutils.inspector; import lombok.Getter; import lombok.Setter; import org.apache.wicket.devutils.inspector.LiveSessionsPage; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = InspectorProperties.PROPERTY_PREFIX) @Getter @Setter public class InspectorProperties { public static final String PROPERTY_PREFIX = "wicket.external.development.devutils.interceptor"; /** * Enables or disabled the mounting of the {@link LiveSessionsPage}. */ private boolean enableLiveSessionsPage; /** * The relative address on which the {@link LiveSessionsPage} should be mounted */ private String liveSessionPageMount = "devutils/inspector/live-session-page"; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/devutils/statelesschecker/StatelessCheckerConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.devutils.statelesschecker; import lombok.RequiredArgsConstructor; import org.apache.wicket.devutils.stateless.StatelessChecker; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; /** * Enables the states checker from the Wicket devutils. Its only enabled if the * following condition matches *

* 1. The {@link StatelessChecker} is present in the classpath *

* 2. the {@link StatelessCheckerProperties}.statelessCheckerEnabled is set to true * (default=false) * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = StatelessCheckerProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = false) @ConditionalOnClass(value = org.apache.wicket.devutils.stateless.StatelessChecker.class) @EnableConfigurationProperties({StatelessCheckerProperties.class}) @RequiredArgsConstructor public class StatelessCheckerConfig implements WicketApplicationInitConfiguration { private final StatelessCheckerProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { webApplication.getComponentPostOnBeforeRenderListeners().add(new StatelessChecker()); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/devutils/statelesschecker/StatelessCheckerProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.devutils.statelesschecker; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = StatelessCheckerProperties.PROPERTY_PREFIX) @Getter @Setter public class StatelessCheckerProperties { public static final String PROPERTY_PREFIX = "wicket.external.development.devutils.statelesschecker"; private boolean enabled = false; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/springboot/devtools/SpringDevToolsProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.springboot.devtools; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = SpringDevToolsProperties.PROPERTY_PREFIX) @Getter @Setter public class SpringDevToolsProperties { public static final String PROPERTY_PREFIX = "spring.devtools.restart"; private boolean enabled = true; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/springboot/devtools/SpringDevToolsSerializer.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.springboot.devtools; import org.apache.wicket.serialize.ISerializer; import org.springframework.core.convert.converter.Converter; import org.springframework.core.serializer.DefaultDeserializer; import org.springframework.core.serializer.support.DeserializingConverter; import org.springframework.core.serializer.support.SerializingConverter; /** * A custom serializer is needed to support Spring Boot Devtools. Spring Boot Devtools * has some limitation with support of serialisation/deserialization support. So we have to * provide a custom Wicket {@link ISerializer}. * *

* 20.2.6 Known limitations
* Restart functionality does not work well with objects that are deserialized using a * standard ObjectInputStream. If you need to deserialize data, you may need to use Spring’s * ConfigurableObjectInputStream in combination with Thread.currentThread().getContextClassLoader(). * Unfortunately, several third-party libraries deserialize without considering the context classloader. * If you find such a problem, you will need to request a fix with the original authors. *

* * @author Marc Giffing * @see Spring Boot Devtools Serializer Issue */ public class SpringDevToolsSerializer implements ISerializer { private final Converter serializer = new SerializingConverter(); private final Converter deserializer; public SpringDevToolsSerializer() { this.deserializer = new DeserializingConverter(new DefaultDeserializer(Thread.currentThread().getContextClassLoader())); } @Override public byte[] serialize(Object object) { return serializer.convert(object); } @Override public Object deserialize(byte[] data) { return deserializer.convert(data); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/springboot/devtools/SpringDevtoolsSerializerConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.springboot.devtools; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration; import org.springframework.boot.devtools.restart.ConditionalOnInitializedRestarter; /** * The Wicket serializer does not working with Spring Boot Devtools so we have to provide a custom {@link SpringDevToolsSerializer}. * It should be active by default if the following conditions matches. *

* 1. The {@link LocalDevToolsAutoConfiguration} class is in the classpath. This means that the Spring Boot Devtools is available in the classpath. *

* 2. The property "spring.devtools.restart.enabled" is set to true. (default is true). There is maybe a better option to check *

* 3. Restarter condition is active {@link ConditionalOnInitializedRestarter}. * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = SpringDevToolsProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(LocalDevToolsAutoConfiguration.class) @EnableConfigurationProperties({SpringDevToolsProperties.class}) @ConditionalOnInitializedRestarter @RequiredArgsConstructor public class SpringDevtoolsSerializerConfig implements WicketApplicationInitConfiguration { private final SpringDevToolsProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { webApplication.getFrameworkSettings().setSerializer(new SpringDevToolsSerializer()); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/springboot/devtools/WicketDevToolsPropertyDefaultsPostProcessor.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.springboot.devtools; import org.springframework.boot.SpringApplication; import org.springframework.boot.devtools.restart.Restarter; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import java.util.Collections; import java.util.HashMap; import java.util.Map; @Order(Ordered.LOWEST_PRECEDENCE) //TODO add feature request in the Spring Boot project for adding custom development properties public class WicketDevToolsPropertyDefaultsPostProcessor implements EnvironmentPostProcessor { private static final Map PROPERTIES; static { Map properties = new HashMap<>(); properties.put("wicket.core.settings.general.configuration-type", "development"); properties.put("wicket.core.settings.markup.strip-wicket-tags", "false"); properties.put("wicket.core.settings.debug.component-use-check", "true"); properties.put("wicket.core.settings.debug.development-utilities-enabled", "true"); properties.put("wicket.core.settings.debug.ajax-debug-mode-enabled", "true"); PROPERTIES = Collections.unmodifiableMap(properties); } @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { if (Boolean.TRUE.equals(isWicketDevToolsEnabled(environment) && isLocalApplication(environment)) && canAddProperties(environment)) { var propertySource = new MapPropertySource("wicketrefresh", PROPERTIES); environment.getPropertySources().addLast(propertySource); } } private Boolean isWicketDevToolsEnabled(ConfigurableEnvironment environment) { var devToolsEnabled = Boolean.TRUE; var devToolsEnabledString = environment.getProperty("wicket." + SpringDevToolsProperties.PROPERTY_PREFIX + ".enabled"); if (devToolsEnabledString != null) { devToolsEnabled = devToolsEnabledString.equalsIgnoreCase("true"); } return devToolsEnabled; } private boolean isLocalApplication(ConfigurableEnvironment environment) { return environment.getPropertySources().get("remoteUrl") == null; } private boolean canAddProperties(Environment environment) { return isRestarterInitialized() || isRemoteRestartEnabled(environment); } private boolean isRestarterInitialized() { try { Restarter restarter = Restarter.getInstance(); return (restarter != null && restarter.getInitialUrls() != null); } catch (NoClassDefFoundError | Exception ex) { return false; } } private boolean isRemoteRestartEnabled(Environment environment) { return environment.containsProperty("spring.devtools.remote.secret"); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/wicketsource/WicketSourceConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.wicketsource; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.github.jennybrown8.wicketsource.WicketSource; /** * Enables wicket-source support if the following two condition matches: *

* 1. The WicketSource is in the classpath. *

* 2. The property wicket.wicketsource.enabled is true (default = true) * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = WicketSourceProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = com.github.jennybrown8.wicketsource.WicketSource.class) @EnableConfigurationProperties({WicketSourceProperties.class}) @RequiredArgsConstructor public class WicketSourceConfig implements WicketApplicationInitConfiguration { private final WicketSourceProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { WicketSource.configure(webApplication); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/development/wicketsource/WicketSourceProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.wicketsource; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = WicketSourceProperties.PROPERTY_PREFIX) @Getter @Setter public class WicketSourceProperties { public static final String PROPERTY_PREFIX = "wicket.external.development.wicketsource"; private boolean enabled = false; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/spring/boot/actuator/WicketEndpoint.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.boot.actuator; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import lombok.RequiredArgsConstructor; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.HashMap; import java.util.Map; @ConfigurationProperties(prefix = "endpoints.wicket") @Endpoint(id = "wicket") @RequiredArgsConstructor public class WicketEndpoint { private final WicketEndpointRepository repository; @ReadOperation public Map wicketExtensions() { Map result = new HashMap<>(); result.put("extensions", repository.getConfigs()); return result; } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/spring/boot/actuator/WicketEndpointConfiguration.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.boot.actuator; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; @Configuration public class WicketEndpointConfiguration { @Bean public WicketEndpoint wicketEndpoint() { return new WicketEndpoint(wicketEndpointRepositoryDefault()); } @Bean @ConditionalOnMissingBean public WicketEndpointRepository wicketEndpointRepositoryDefault() { return new WicketEndpointRepositoryDefault(); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/spring/boot/actuator/WicketEndpointRepositoryDefault.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.boot.actuator; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import lombok.Getter; import java.util.ArrayList; import java.util.List; @Getter public class WicketEndpointRepositoryDefault implements WicketEndpointRepository { private final List configs = new ArrayList<>(); public void add(WicketAutoConfig autoconfig) { this.configs.add(autoconfig); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/spring/security/SecureWebSession.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.security; import java.io.Serializable; import org.apache.wicket.authroles.authentication.AuthenticatedWebSession; import org.apache.wicket.authroles.authorization.strategies.role.Roles; import org.apache.wicket.injection.Injector; import org.apache.wicket.protocol.http.WebSession; import org.apache.wicket.request.Request; import org.apache.wicket.spring.injection.annot.SpringBean; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; /** * Spring Security Implementation of Wickets {@link AuthenticatedWebSession}. * * @author Marc Giffing */ public class SecureWebSession extends AuthenticatedWebSession implements Serializable { private static final String SPRING_SECURITY_CONTEXT_KEY = HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY; @SpringBean(name = "authenticationManager") private AuthenticationManager authenticationManager; public SecureWebSession(Request request) { super(request); Injector.get().inject(this); } @Override public boolean authenticate(String username, String password) { try { var auth = authenticationManager .authenticate(new UsernamePasswordAuthenticationToken(username, password)); if (auth.isAuthenticated()) { SecurityContextHolder.getContext().setAuthentication(auth); WebSession httpSession = WebSession.get(); if (httpSession != null) { httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext()); } return true; } return false; } catch (AuthenticationException e) { return false; } } @Override public Roles getRoles() { Roles roles = new Roles(); if (isSignedIn()) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); authentication.getAuthorities().forEach(authority -> roles.add(authority.getAuthority())); } return roles; } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/spring/security/SpringSecurityConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.security; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.security.AuthenticatedWebSessionConfig; import com.giffing.wicket.spring.boot.starter.app.WicketBootSecuredWebApplication; import com.giffing.wicket.spring.boot.starter.app.WicketBootWebApplication; @ApplicationInitExtension @ConditionalOnProperty(prefix = SpringSecurityProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = { org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession.class, org.springframework.security.core.Authentication.class, org.springframework.security.web.SecurityFilterChain.class }) @EnableConfigurationProperties({SpringSecurityProperties.class}) @ConditionalOnMissingBean(WicketBootWebApplication.class) public class SpringSecurityConfig { @Bean public WicketBootSecuredWebApplication wicketBootWebApplication() { return new WicketBootSecuredWebApplication(); } @Bean public AuthenticatedWebSessionConfig authenticatedWebSessionConfig() { return () -> SecureWebSession.class; } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/spring/security/SpringSecurityProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.security; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = SpringSecurityProperties.PROPERTY_PREFIX) public class SpringSecurityProperties { public static final String PROPERTY_PREFIX = "wicket.external.spring.security"; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/webjars/WebjarsConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.webjars; import jdk.jfr.Registered; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import com.giffing.wicket.spring.boot.context.condition.ConditionalOnWicket; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import de.agilecoders.wicket.webjars.WicketWebjars; import de.agilecoders.wicket.webjars.settings.WebjarsSettings; /** * Enables webjars support if the following two condition matches: *

* 1. The {@link WicketWebjars} is in the classpath. *

* 2. The property wicket.webjars.enabled is true (default = true) * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = WebjarsProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = de.agilecoders.wicket.webjars.WicketWebjars.class) @EnableConfigurationProperties({WebjarsProperties.class}) @ConditionalOnWicket(value = 10) @RequiredArgsConstructor public class WebjarsConfig implements WicketApplicationInitConfiguration { private final WebjarsProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var settings = new WebjarsSettings(); WicketWebjars.install(webApplication, settings); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/external/webjars/WebjarsProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.external.webjars; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = WebjarsProperties.PROPERTY_PREFIX) @Getter @Setter public class WebjarsProperties { public static final String PROPERTY_PREFIX = "wicket.external.webjars"; /** * Enables webjars support */ private boolean enabled = true; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/annotationscan/AnnotatedMountScannerConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.annotationscan; import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.core.annotation.Order; import org.wicketstuff.annotation.scan.AnnotatedMountScanner; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder; /** * Auto configuration for the {@link AnnotatedMountScanner}. *

* It uses the user defined {@link WebApplication} as the default package scan * root directory. *

* Enables annotate mount scanner if the following two condition matches: *

* 1. The {@link AnnotatedMountScanner} is in the classpath. *

* 2. The property {@link AnnotatedMountScannerProperties#PROPERTY_PREFIX} * .enabled is true (default = true) * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = AnnotatedMountScannerProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = org.wicketstuff.annotation.scan.AnnotatedMountScanner.class) @EnableConfigurationProperties({AnnotatedMountScannerProperties.class}) @Order(ApplicationInitExtension.DEFAULT_PRECEDENCE + 50) @RequiredArgsConstructor public class AnnotatedMountScannerConfig implements WicketApplicationInitConfiguration { private final AnnotatedMountScannerProperties prop; private final WicketClassCandidatesHolder candidates; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { List packagesToScan = new ArrayList<>(); var annotatedMountScanner = new AnnotatedMountScanner(); var packagename = webApplication.getClass().getPackage().getName(); if (prop.getPackagename() != null) { packagename = prop.getPackagename(); } packagesToScan.add(packagename); annotatedMountScanner.scanPackage(packagename).mount(webApplication); if (!candidates.getBasePackages().isEmpty()) { packagesToScan.addAll(candidates.getBasePackages()); for (String basePackage : candidates.getBasePackages()) { annotatedMountScanner.scanPackage(basePackage).mount(webApplication); } } wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", prop) .withDetail("packagesToScan", packagesToScan) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/annotationscan/AnnotatedMountScannerProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.annotationscan; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = AnnotatedMountScannerProperties.PROPERTY_PREFIX) @Getter @Setter public class AnnotatedMountScannerProperties { public static final String PROPERTY_PREFIX = "wicket.stuff.annotationscan"; /** * @see AnnotatedMountScannerConfig */ private boolean enabled = true; /** * An alternative package name for scanning for mount path if the * WicketApplication should not used as the root scan package */ private String packagename; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/datastore/cassandra/DataStoreCassandraConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.datastore.cassandra; import lombok.RequiredArgsConstructor; import org.apache.wicket.DefaultPageManagerProvider; import org.apache.wicket.pageStore.IPageStore; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.wicketstuff.datastores.cassandra.CassandraDataStore; import org.wicketstuff.datastores.cassandra.CassandraSettings; import org.wicketstuff.datastores.cassandra.ICassandraSettings; import org.wicketstuff.datastores.common.SessionQuotaManagingDataStore; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.context.extensions.types.TypeParser; /** * Data store auto configuration for the cassandra database *

* Enables cassandra data store if the following two condition matches: *

* 1. The "com.datastax.driver.core.Session" is in the classpath. *

* 2. The property {@link DataStoreCassandraProperties#PROPERTY_PREFIX}.enabled * is true (default = true) * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = DataStoreCassandraProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(name = "com.datastax.driver.core.Session", value = {CassandraDataStore.class}) @EnableConfigurationProperties({DataStoreCassandraProperties.class}) @AutoConfigureAfter(CassandraAutoConfiguration.class) @RequiredArgsConstructor public class DataStoreCassandraConfig implements WicketApplicationInitConfiguration { private final DataStoreCassandraProperties prop; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var settings = new CassandraSettings(); settings.getContactPoints().addAll(prop.getContactPoints()); settings.setTableName(prop.getTableName()); settings.setKeyspaceName(prop.getKeyspaceName()); settings.setRecordTtl(TypeParser.parse(prop.getRecordTtl(), prop.getRecordTtlUnit())); webApplication.setPageManagerProvider(new DefaultPageManagerProvider(webApplication) { @Override protected IPageStore newPersistentStore() { var delegate = new CassandraDataStore(webApplication.getName(), settings); return new SessionQuotaManagingDataStore(delegate, TypeParser.parse(prop.getSessionSize(), prop.getSessionUnit())); } }); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", prop) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/datastore/cassandra/DataStoreCassandraProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.datastore.cassandra; import com.giffing.wicket.spring.boot.context.extensions.types.DurationUnit; import com.giffing.wicket.spring.boot.context.extensions.types.SessionUnit; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.List; @ConfigurationProperties(prefix = DataStoreCassandraProperties.PROPERTY_PREFIX) @Getter @Setter public class DataStoreCassandraProperties { public static final String PROPERTY_PREFIX = "wicket.stuff.datastore.cassandra"; private boolean enabled = true; private List contactPoints; private String tableName = "pagestore"; private String keyspaceName = "wicket"; private Long recordTtl = 30L; private DurationUnit recordTtlUnit = DurationUnit.MINUTES; private Long sessionSize = 2L; private SessionUnit sessionUnit = SessionUnit.MEGABYTES; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/datastore/hazelcast/DataStoreHazelcastConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.datastore.hazelcast; import lombok.RequiredArgsConstructor; import org.apache.wicket.DefaultPageManagerProvider; import org.apache.wicket.pageStore.IPageStore; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.wicketstuff.datastores.common.SessionQuotaManagingDataStore; import org.wicketstuff.datastores.hazelcast.HazelcastDataStore; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.context.extensions.types.TypeParser; import com.hazelcast.core.HazelcastInstance; /** * Data store auto configuration for the hazelcast database *

* Enables hazelcast data store if the following two condition matches: *

* 1. The {@link HazelcastInstance} is in the classpath. *

* 2. The property {@link DataStoreHazelcastProperties#PROPERTY_PREFIX}.enabled * is true (default = true) * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = DataStoreHazelcastProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnBean(HazelcastInstance.class) @ConditionalOnClass({HazelcastInstance.class, HazelcastDataStore.class}) @EnableConfigurationProperties({DataStoreHazelcastProperties.class}) @AutoConfigureAfter(HazelcastAutoConfiguration.class) @RequiredArgsConstructor public class DataStoreHazelcastConfig implements WicketApplicationInitConfiguration { private final DataStoreHazelcastProperties prop; private final HazelcastInstance hazelcastInstance; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { webApplication.setPageManagerProvider(new DefaultPageManagerProvider(webApplication) { @Override protected IPageStore newPersistentStore() { var hazelcastDataStore = new HazelcastDataStore(webApplication.getName(), hazelcastInstance); return new SessionQuotaManagingDataStore(hazelcastDataStore, TypeParser.parse(prop.getSessionSize(), prop.getSessionUnit())); } }); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", prop) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/datastore/hazelcast/DataStoreHazelcastProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.datastore.hazelcast; import com.giffing.wicket.spring.boot.context.extensions.types.SessionUnit; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = DataStoreHazelcastProperties.PROPERTY_PREFIX) @Getter @Setter public class DataStoreHazelcastProperties { public static final String PROPERTY_PREFIX = "wicket.stuff.datastore.hazelcast"; private boolean enabled = true; private Long sessionSize = 2L; private SessionUnit sessionUnit = SessionUnit.MEGABYTES; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/datastore/memcached/DataStoreMemcachedConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.datastore.memcached; import lombok.RequiredArgsConstructor; import org.apache.wicket.DefaultPageManagerProvider; import org.apache.wicket.pageStore.IPageStore; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.wicketstuff.datastores.common.SessionQuotaManagingDataStore; import org.wicketstuff.datastores.memcached.IMemcachedSettings; import org.wicketstuff.datastores.memcached.MemcachedDataStore; import org.wicketstuff.datastores.memcached.MemcachedSettings; import com.giffing.wicket.spring.boot.context.exceptions.WicketSpringBootException; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.context.extensions.types.TypeParser; import net.spy.memcached.MemcachedClient; /** * Data store auto configuration for the memcached database *

* Enables memcached data store if the following two condition matches: *

* 1. The {@link MemcachedClient} is in the classpath. *

* 2. The property {@link DataStoreMemcachedProperties#PROPERTY_PREFIX}.enabled * is true (default = true) * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = DataStoreMemcachedProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass({MemcachedClient.class, MemcachedDataStore.class}) @EnableConfigurationProperties({DataStoreMemcachedProperties.class}) @RequiredArgsConstructor public class DataStoreMemcachedConfig implements WicketApplicationInitConfiguration { private final DataStoreMemcachedProperties prop; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { webApplication.setPageManagerProvider(new DefaultPageManagerProvider(webApplication) { @Override protected IPageStore newPersistentStore() { var settings = new MemcachedSettings(); settings.setExpirationTime(prop.getExpirationTime()); settings.setPort(prop.getPort()); settings.setServerNames(prop.getServerNames()); settings.setShutdownTimeout(TypeParser.parse(prop.getShutdownTimeout(), prop.getShutdownTimeoutUnit())); try { var memcachedDataStore = new MemcachedDataStore(webApplication.getName(), settings); return new SessionQuotaManagingDataStore(memcachedDataStore, TypeParser.parse(prop.getSessionSize(), prop.getSessionUnit())); } catch (Exception e) { throw new WicketSpringBootException(e); } } }); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", prop) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/datastore/memcached/DataStoreMemcachedProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.datastore.memcached; import com.giffing.wicket.spring.boot.context.extensions.types.DurationUnit; import com.giffing.wicket.spring.boot.context.extensions.types.SessionUnit; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = DataStoreMemcachedProperties.PROPERTY_PREFIX) @Getter @Setter public class DataStoreMemcachedProperties { public static final String PROPERTY_PREFIX = "wicket.stuff.datastore.memcached"; private boolean enabled = true; private Long sessionSize = 2L; private SessionUnit sessionUnit = SessionUnit.MEGABYTES; private int expirationTime = 30; private int port = 11211; private String serverNames; private long shutdownTimeout = 30; private DurationUnit shutdownTimeoutUnit = DurationUnit.MINUTES; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/datastore/redis/DataStoreRedisConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.datastore.redis; import lombok.RequiredArgsConstructor; import org.apache.wicket.DefaultPageManagerProvider; import org.apache.wicket.pageStore.IPageStore; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.wicketstuff.datastores.common.SessionQuotaManagingDataStore; import org.wicketstuff.datastores.redis.IRedisSettings; import org.wicketstuff.datastores.redis.RedisDataStore; import org.wicketstuff.datastores.redis.RedisSettings; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.context.extensions.types.TypeParser; import redis.clients.jedis.Jedis; /** * Data store auto configuration for the redis database *

* Enables redis data store if the following two condition matches: *

* 1. The {@link Jedis} is in the classpath. *

* 2. The property {@link DataStoreRedisProperties#PROPERTY_PREFIX}.enabled * is true (default = true) * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = DataStoreRedisProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass({Jedis.class, RedisDataStore.class}) @EnableConfigurationProperties({DataStoreRedisProperties.class}) @AutoConfigureAfter(RedisAutoConfiguration.class) @RequiredArgsConstructor public class DataStoreRedisConfig implements WicketApplicationInitConfiguration { private final DataStoreRedisProperties prop; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { webApplication.setPageManagerProvider(new DefaultPageManagerProvider(webApplication) { @Override protected IPageStore newPersistentStore() { var settings = new RedisSettings(); settings.setHostname(prop.getHostname()); settings.setPort(prop.getPort()); settings.setRecordTtl(TypeParser.parse(prop.getRecordTtl(), prop.getRecordTtlUnit())); var redisDataStore = new RedisDataStore(webApplication.getName(), settings); return new SessionQuotaManagingDataStore(redisDataStore, TypeParser.parse(prop.getSessionSize(), prop.getSessionUnit())); } }); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", prop) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/datastore/redis/DataStoreRedisProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.datastore.redis; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import com.giffing.wicket.spring.boot.context.extensions.types.DurationUnit; import com.giffing.wicket.spring.boot.context.extensions.types.SessionUnit; @ConfigurationProperties(prefix = DataStoreRedisProperties.PROPERTY_PREFIX) @Getter @Setter public class DataStoreRedisProperties { public static final String PROPERTY_PREFIX = "wicket.stuff.datastore.redis"; private boolean enabled = true; private Long sessionSize = 2L; private SessionUnit sessionUnit = SessionUnit.MEGABYTES; private String hostname = "127.0.0.1"; private int port = 6379; private Long recordTtl = 30L; private DurationUnit recordTtlUnit = DurationUnit.MINUTES; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/htmlcompressor/HTMLCompressingConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.htmlcompressor; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.googlecode.htmlcompressor.compressor.HtmlCompressor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; import org.wicketstuff.htmlcompressor.HtmlCompressingMarkupFactory; import java.lang.reflect.Method; /** * Enables the HTML markup compression if the following two conditions matches: *

* 1. The {@link HtmlCompressingMarkupFactory} class is present *

* 2. The {@link HTMLCompressingProperties}.compressHTMLEnabled is set to true * (default is true). * * @author Marc Giffing */ @ApplicationInitExtension @ConditionalOnProperty(prefix = HTMLCompressingProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = org.wicketstuff.htmlcompressor.HtmlCompressingMarkupFactory.class) @EnableConfigurationProperties({HTMLCompressingProperties.class}) @RequiredArgsConstructor @Slf4j public class HTMLCompressingConfig implements WicketApplicationInitConfiguration { private final HTMLCompressingProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var compressor = new HtmlCompressor(); setFeatureConfiguration(compressor); webApplication.getMarkupSettings().setMarkupFactory(new HtmlCompressingMarkupFactory(compressor)); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } private void setFeatureConfiguration(HtmlCompressor compressor) { for (var entrySet : props.getFeatures().entrySet()) { Method method = null; var capitalizedKey = StringUtils.capitalize(entrySet.getKey()); var methodname = "set" + capitalizedKey; try { method = compressor.getClass().getMethod(methodname, boolean.class); method.setAccessible(true); ReflectionUtils.invokeMethod(method, compressor, entrySet.getValue()); } catch (Exception e) { log.warn("failed to invoke method: {} with value {}. Exception: {}", methodname, entrySet.getValue(), e.getMessage()); } } } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/htmlcompressor/HTMLCompressingProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.htmlcompressor; import com.googlecode.htmlcompressor.compressor.HtmlCompressor; import lombok.Getter; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.HashMap; import java.util.Map; @ConfigurationProperties(prefix = HTMLCompressingProperties.PROPERTY_PREFIX) @Getter public class HTMLCompressingProperties { public static final String PROPERTY_PREFIX = "wicket.stuff.htmlcompressor"; /** * Indicates if the HTML compression should be enabled. It is only enable if a * HTML compression library is present. * * @see HTMLCompressingConfig */ private boolean enabled = true; /** * Sets additional features of the {@link HtmlCompressor} class. It uses * reflection to set boolean properties on public methods. *

* You can for example use compressCSS->true to invoke the public method setCompressCSS(true). *

* The main goal is to provide an API independent solution to configure the {@link HtmlCompressor}. * API changes can be handled over configuration changes */ private final Map features = new HashMap<>(); public HTMLCompressingProperties() { features.put("removeComments", Boolean.TRUE); features.put("removeMultiSpaces", Boolean.TRUE); features.put("removeIntertagSpaces", Boolean.TRUE); features.put("removeQuotes", Boolean.TRUE); features.put("compressJavaScript", Boolean.TRUE); features.put("compressCss", Boolean.TRUE); features.put("simpleDoctype", Boolean.TRUE); features.put("removeScriptAttributes", Boolean.FALSE); features.put("removeStyleAttributes", Boolean.FALSE); features.put("removeLinkAttributes", Boolean.FALSE); features.put("removeFormAttributes", Boolean.FALSE); features.put("removeInputAttributes", Boolean.FALSE); features.put("simpleBooleanAttributes", Boolean.FALSE); features.put("removeJavaScriptProtocol", Boolean.FALSE); features.put("removeHttpProtocol", Boolean.FALSE); features.put("removeHttpsProtocol", Boolean.FALSE); features.put("preserveLineBreaks", Boolean.FALSE); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/monitoring/jamon/BootJamonAdminPage.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.monitoring.jamon; import org.apache.wicket.markup.head.CssHeaderItem; import org.apache.wicket.markup.head.IHeaderResponse; import org.wicketstuff.jamon.component.JamonAdminPage; public class BootJamonAdminPage extends JamonAdminPage { @Override public void renderHead(IHeaderResponse response) { super.renderHead(response); response.render(CssHeaderItem.forReference(JamonCssResourceReference.INSTANCE)); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/monitoring/jamon/JamonConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.monitoring.jamon; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.wicketstuff.jamon.request.cycle.JamonAwareRequestCycleListener; @ApplicationInitExtension @ConditionalOnProperty(prefix = JamonProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = JamonAwareRequestCycleListener.class) @EnableConfigurationProperties({JamonProperties.class}) @RequiredArgsConstructor public class JamonConfig implements WicketApplicationInitConfiguration { private final JamonProperties prop; @Override public void init(WebApplication webApplication) { webApplication.getRequestCycleListeners().add(new JamonAwareRequestCycleListener(webApplication, prop.isIncludeSourceNameInMonitorLabel())); webApplication.mountPage(prop.getMountPage(), BootJamonAdminPage.class); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/monitoring/jamon/JamonCssResourceReference.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.monitoring.jamon; import org.apache.wicket.request.resource.CssResourceReference; public class JamonCssResourceReference extends CssResourceReference { /** * Singleton instance of this reference */ public static final JamonCssResourceReference INSTANCE = new JamonCssResourceReference(); /** * Private constructor. */ private JamonCssResourceReference() { super(JamonCssResourceReference.class, "css/jamon.css"); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/monitoring/jamon/JamonProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.monitoring.jamon; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = JamonProperties.PROPERTY_PREFIX) @Getter @Setter public class JamonProperties { public static final String PROPERTY_PREFIX = "wicket.stuff.monitoring.jamon"; private boolean includeSourceNameInMonitorLabel = true; private String mountPage = "/monitoring/jamon"; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/monitoring/jamon/css/jamon.css ================================================ .jamonTable { background-color: #03487f; border-width: 2px; border-style: solid; border-color: white; font-weight: bold; } .jamonAdminTable { background-color: #03487f; border-width: 2px; border-style: solid; border-color: white; font-weight: bold; } .jamonDetailTable { background-color: #03487f; border-width: 2px; border-style: solid; border-color: white; font-weight: bold; } .jamonLinkToDetailPanel { color: white; font-weight: bold; } .jamonDetailTitle { font-weight: bold; font-size: 18px; color: #03487f; text-decoration: underline; } .headers a { color: white; font-weight: bold; } .headers { color: white; font-weight: bold; } .odd { background-color: #3b6588; } .even { background-color: #c5d4e4; } .selected { background-color: #03487f; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/restannotations/RestAnnotationsConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.restannotations; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.wicketstuff.rest.utils.mounting.PackageScanner; @ApplicationInitExtension @ConditionalOnProperty(prefix = RestAnnotationsProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = PackageScanner.class) @EnableConfigurationProperties({RestAnnotationsProperties.class}) @RequiredArgsConstructor public class RestAnnotationsConfig implements WicketApplicationInitConfiguration { private final RestAnnotationsProperties prop; private final WicketClassCandidatesHolder candidates; @Override public void init(WebApplication webApplication) { var packagename = webApplication.getClass().getPackage().getName(); if (prop.getPackagename() != null) { packagename = prop.getPackagename(); } PackageScanner.scanPackage(webApplication, packagename); for (String basePackage : candidates.getBasePackages()) { PackageScanner.scanPackage(webApplication, basePackage); } } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/restannotations/RestAnnotationsProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.restannotations; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = RestAnnotationsProperties.PROPERTY_PREFIX) @Getter @Setter public class RestAnnotationsProperties { public static final String PROPERTY_PREFIX = "wicket.stuff.restannotations"; private boolean enabled = true; private String packagename; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/serializer/fast2/WicketSerializerFast2Config.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.serializer.fast2; import com.giffing.wicket.spring.boot.context.exceptions.extensions.ExtensionMisconfigurationException; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.springboot.devtools.SpringDevtoolsSerializerConfig; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.serialize.ISerializer; import org.apache.wicket.serialize.java.JavaSerializer; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.wicketstuff.pageserializer.fast2.Fast2WicketSerializer; @ApplicationInitExtension @ConditionalOnProperty(prefix = WicketSerializerFast2Properties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = org.wicketstuff.pageserializer.fast2.Fast2WicketSerializer.class) @EnableConfigurationProperties({WicketSerializerFast2Properties.class}) @ConditionalOnMissingBean(SpringDevtoolsSerializerConfig.class) @RequiredArgsConstructor public class WicketSerializerFast2Config implements WicketApplicationInitConfiguration { private final WicketSerializerFast2Properties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { ISerializer currentSerializer = webApplication.getFrameworkSettings().getSerializer(); if (currentSerializer.getClass().equals(JavaSerializer.class)) { webApplication.getFrameworkSettings().setSerializer(new Fast2WicketSerializer()); } else { throw new ExtensionMisconfigurationException("Fast2Config: There is already another serializer configured " + currentSerializer); } wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/serializer/fast2/WicketSerializerFast2Properties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.serializer.fast2; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = WicketSerializerFast2Properties.PROPERTY_PREFIX) public class WicketSerializerFast2Properties { public static final String PROPERTY_PREFIX = "wicket.stuff.serializer.fast2"; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/serializer/kryo2/WicketSerializerKryo2Config.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.serializer.kryo2; import com.giffing.wicket.spring.boot.context.exceptions.extensions.ExtensionMisconfigurationException; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.springboot.devtools.SpringDevtoolsSerializerConfig; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.serialize.java.JavaSerializer; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.wicketstuff.pageserializer.kryo2.KryoSerializer; @ApplicationInitExtension @ConditionalOnProperty(prefix = WicketSerializerKryo2Properties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = org.wicketstuff.pageserializer.kryo2.KryoSerializer.class) @EnableConfigurationProperties({WicketSerializerKryo2Properties.class}) @ConditionalOnMissingBean(SpringDevtoolsSerializerConfig.class) @RequiredArgsConstructor public class WicketSerializerKryo2Config implements WicketApplicationInitConfiguration { private final WicketSerializerKryo2Properties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { var currentSerializer = webApplication.getFrameworkSettings().getSerializer(); if (currentSerializer.getClass().equals(JavaSerializer.class)) { webApplication.getFrameworkSettings().setSerializer(new KryoSerializer()); } else { throw new ExtensionMisconfigurationException("Kryo2Config: There is already another serializer configured " + currentSerializer); } wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/serializer/kryo2/WicketSerializerKryo2Properties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.serializer.kryo2; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = WicketSerializerKryo2Properties.PROPERTY_PREFIX) public class WicketSerializerKryo2Properties { public static final String PROPERTY_PREFIX = "wicket.stuff.serializer.kryo2"; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/shiro/ShiroSecurityConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.shiro; import lombok.RequiredArgsConstructor; import org.apache.shiro.spring.boot.autoconfigure.ShiroAutoConfiguration; import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition; import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition; import org.apache.wicket.Page; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.wicketstuff.shiro.annotation.AnnotationsShiroAuthorizationStrategy; import org.wicketstuff.shiro.authz.ShiroUnauthorizedComponentListener; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.scan.WicketAccessDeniedPage; import com.giffing.wicket.spring.boot.context.scan.WicketSignInPage; import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder; //TODO check if property shiro.enabled is true @ApplicationInitExtension @ConditionalOnProperty(prefix = ShiroSecurityProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = { AnnotationsShiroAuthorizationStrategy.class, ShiroAutoConfiguration.class, }) @EnableConfigurationProperties({ShiroSecurityProperties.class}) @RequiredArgsConstructor public class ShiroSecurityConfig implements WicketApplicationInitConfiguration { private final WicketClassCandidatesHolder classCandidates; @Bean ShiroFilterChainDefinition shiroFilterChainDefinition() { return new DefaultShiroFilterChainDefinition(); } @Override public void init(WebApplication webApplication) { var authz = new AnnotationsShiroAuthorizationStrategy(); webApplication.getSecuritySettings().setAuthorizationStrategy(authz); if (classCandidates.getSignInPageCandidates().isEmpty()) { throw new IllegalStateException("Couln't find sign in page - please annotate the sign in page with @" + WicketSignInPage.class.getName()); } if (classCandidates.getAccessDeniedPageCandidates().isEmpty()) { throw new IllegalStateException("Couln't find access denied in page - please annotate the sign in page with @" + WicketAccessDeniedPage.class.getName()); } var signInPage = classCandidates.getSignInPageCandidates().iterator().next().getCandidate(); var accessDeniedPage = classCandidates.getAccessDeniedPageCandidates().iterator().next().getCandidate(); webApplication.getSecuritySettings() .setUnauthorizedComponentInstantiationListener(new ShiroUnauthorizedComponentListener(signInPage, accessDeniedPage, authz)); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/shiro/ShiroSecurityProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.shiro; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = ShiroSecurityProperties.PROPERTY_PREFIX) @Getter @Setter public class ShiroSecurityProperties { public static final String PROPERTY_PREFIX = "wicket.stuff.restannotations"; private boolean enabled = true; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/springreference/SpringReferenceConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.springreference; import com.giffing.wicket.spring.boot.context.extensions.ApplicationInitExtension; import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.http.WebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.wicketstuff.springreference.SpringReferenceSupporter; @ApplicationInitExtension @ConditionalOnProperty(prefix = SpringReferenceProperties.PROPERTY_PREFIX, value = "enabled", matchIfMissing = true) @ConditionalOnClass(value = SpringReferenceSupporter.class) @EnableConfigurationProperties({SpringReferenceProperties.class}) @RequiredArgsConstructor public class SpringReferenceConfig implements WicketApplicationInitConfiguration { private final SpringReferenceProperties props; private final WicketEndpointRepository wicketEndpointRepository; @Override public void init(WebApplication webApplication) { SpringReferenceSupporter.register(webApplication); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("properties", props) .build()); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/configuration/extensions/stuff/springreference/SpringReferenceProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.configuration.extensions.stuff.springreference; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = SpringReferenceProperties.PROPERTY_PREFIX) @Getter @Setter public class SpringReferenceProperties { public static final String PROPERTY_PREFIX = "wicket.stuff.springreference"; private boolean enabled = true; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/web/WicketWebInitializer.java ================================================ package com.giffing.wicket.spring.boot.starter.web; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketAutoConfig; import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; import com.giffing.wicket.spring.boot.starter.app.WicketBootWebApplication; import com.giffing.wicket.spring.boot.starter.web.config.WicketWebInitializerAutoConfig; import com.giffing.wicket.spring.boot.starter.web.config.WicketWebInitializerConfig; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.protocol.http.WicketFilter; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import java.util.EnumSet; /** * Primary container configuration to configure Wicket requests. * * @author Marc Giffing */ @Configuration @Import(value = {WicketWebInitializerAutoConfig.class}) @EnableConfigurationProperties({WicketWebInitializerProperties.class}) public class WicketWebInitializer { public static final String WICKET_FILTERNAME = "wicket-filter"; @Bean public FilterRegistrationBean wicketFilter( WicketWebInitializerConfig wicketWebInitializerConfig, WicketWebInitializerProperties props, WicketEndpointRepository wicketEndpointRepository, WicketBootWebApplication wicketBootWebApplication ) { var wicketFilter = wicketWebInitializerConfig.createWicketFilter((WebApplication) wicketBootWebApplication); var filter = new FilterRegistrationBean<>(wicketFilter); filter.setName(WICKET_FILTERNAME); filter.addUrlPatterns(props.getFilterMappingParam()); filter.setDispatcherTypes(EnumSet.copyOf(props.getDispatcherTypes())); filter.setMatchAfter(props.isFilterMatchAfter()); filter.addInitParameter(WicketFilter.FILTER_MAPPING_PARAM, props.getFilterMappingParam()); props.getInitParameters().forEach(filter::addInitParameter); wicketEndpointRepository.add(new WicketAutoConfig.Builder(this.getClass()) .withDetail("wicketFilterName", WICKET_FILTERNAME) .withDetail("wicketFilterClass", wicketFilter.getClass()) .withDetail("properties", props) .build()); return filter; } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/web/WicketWebInitializerProperties.java ================================================ package com.giffing.wicket.spring.boot.starter.web; import jakarta.servlet.DispatcherType; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.HashMap; import java.util.List; import java.util.Map; @ConfigurationProperties(prefix = WicketWebInitializerProperties.PROPERTY_PREFIX) @Getter @Setter public class WicketWebInitializerProperties { public static final String PROPERTY_PREFIX = "wicket.web.servlet"; private String filterMappingParam = "/*"; // Adds possibility to add init parameters dynamically private Map initParameters = new HashMap<>(); private List dispatcherTypes = List.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC); private boolean filterMatchAfter; } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/web/config/WicketWebInitializerAutoConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.web.config; import com.giffing.wicket.spring.boot.starter.web.servlet.standard.StandardWicketWebInitializer; import com.giffing.wicket.spring.boot.starter.web.servlet.websocket.*; import org.apache.wicket.Application; import org.apache.wicket.protocol.ws.javax.JavaxWebSocketFilter; import org.apache.wicket.protocol.ws.javax.WicketServerEndpointConfig; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Configuration of the {@link WicketWebInitializerConfig} depending on property * conditions and dependency existence. * * @author Marc Giffing */ @Configuration public class WicketWebInitializerAutoConfig { /** * The {@link StandardWicketWebInitializer} should be configured if no other * {@link WicketWebInitializerConfig} is configured. Its quasi the standard * configuration. * * @author Marc Giffing */ @Configuration @ConditionalOnMissingBean(WicketWebInitializerConfig.class) @AutoConfigureAfter(WebSocketWicketWebInitializerAutoConfiguration.class) public static class StandardWicketWebInitializerAutoConfiguration { @Bean public WicketWebInitializerConfig wicketWebInitializerConfig() { return new StandardWicketWebInitializer(); } } /** * @author Marc Giffing */ @Configuration @ConditionalOnClass({JavaxWebSocketFilter.class}) @ConditionalOnProperty(prefix = "wicket.external.websocket", value = "enabled", matchIfMissing = true) public static class WebSocketWicketWebInitializerAutoConfiguration { public static final String REGISTER_SERVER_ENDPOINT = "wicket.external.websocket.registerServerEndpoint"; public static final String REGISTER_SERVER_ENDPOINT_ENABLED = REGISTER_SERVER_ENDPOINT + ".enabled"; @Bean public WicketWebInitializerConfig wicketWebInitializerConfig() { return new WebSocketWicketWebInitializer(); } /** * @return the wicket server endpoint config register which registers the {@link WicketServerEndpointConfig} */ @Bean @ConditionalOnProperty(prefix = REGISTER_SERVER_ENDPOINT, value = "enabled", matchIfMissing = true) public WicketServerEndpointConfigRegister wicketServerEndpointConfigRegister() { return new WicketServerEndpointConfigRegister(); } @Bean @ConditionalOnMissingBean(WicketSessionResolver.class) public WicketSessionResolver dummyWicketSessionResolver() { return new DummyWicketSessionResolver(); } @Bean public WebSocketMessageSenderDefault webSocketEventHandler(Application application, WicketSessionResolver wicketSessionResolver) { return new WebSocketMessageSenderDefault(application, wicketSessionResolver); } } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/web/config/WicketWebInitializerConfig.java ================================================ package com.giffing.wicket.spring.boot.starter.web.config; import com.giffing.wicket.spring.boot.starter.web.WicketWebInitializer; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.protocol.http.WicketFilter; /** * Dynamic configuration support which will be used in {@link WicketWebInitializer}. *

* The configuration {@link WicketWebInitializerAutoConfig} is responsible to detect which {@link WicketWebInitializerConfig} * should be configured. * * @author Marc Giffing */ public interface WicketWebInitializerConfig { /** * Returns a {@link WicketFilter} which will be configured in the {@link WicketWebInitializer} */ WicketFilter createWicketFilter(WebApplication application); } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/web/servlet/standard/StandardWicketWebInitializer.java ================================================ package com.giffing.wicket.spring.boot.starter.web.servlet.standard; import com.giffing.wicket.spring.boot.starter.web.config.WicketWebInitializerConfig; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.protocol.http.WicketFilter; /** * The {@link StandardWicketWebInitializer} will be configured when no other * {@link WicketWebInitializerConfig} is present. It uses the standard {@link WicketFilter}. * * @author Marc Giffing */ public class StandardWicketWebInitializer implements WicketWebInitializerConfig { @Override public WicketFilter createWicketFilter(WebApplication application) { return new WicketFilter(application); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/web/servlet/websocket/DummyWicketSessionResolver.java ================================================ package com.giffing.wicket.spring.boot.starter.web.servlet.websocket; import java.util.List; public class DummyWicketSessionResolver implements WicketSessionResolver { @Override public List resolve(Object value) { return List.of(); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/web/servlet/websocket/WebSocketMessageBroadcaster.java ================================================ package com.giffing.wicket.spring.boot.starter.web.servlet.websocket; import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage; /** * Used to broadcast {@link IWebSocketPushMessage} messages. * * @author Marc Giffing */ public interface WebSocketMessageBroadcaster { /** * Broadcasts a push message to the Wicket page (and it's components) * associated with all connections. The components can then send messages or * component updates to client by adding them to the target. * * @param event */ void sendToAll(IWebSocketPushMessage event); /** * Sends a message to a specific identifier. The {@link WicketSessionResolver} is responsible to * the identifier and return the corresponding session list to inform the user. *

* The identifier can be an username or a user group. Its up to the user to decide how the session ids will * resolved. */ void sendTo(Object identifier, IWebSocketPushMessage event); } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/web/servlet/websocket/WebSocketMessageSenderDefault.java ================================================ package com.giffing.wicket.spring.boot.starter.web.servlet.websocket; import org.apache.wicket.Application; import org.apache.wicket.protocol.ws.WebSocketSettings; import org.apache.wicket.protocol.ws.api.IWebSocketConnection; import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class WebSocketMessageSenderDefault implements WebSocketMessageBroadcaster { private final Logger log = LoggerFactory.getLogger(getClass()); private Application application; private WicketSessionResolver wicketSessionResolver; public WebSocketMessageSenderDefault(Application application, WicketSessionResolver wicketSessionResolver) { this.application = application; this.wicketSessionResolver = wicketSessionResolver; } @Override public void sendToAll(IWebSocketPushMessage event) { var webSocketSettings = WebSocketSettings.Holder.get(application); var connectionRegistry = webSocketSettings.getConnectionRegistry(); var connections = connectionRegistry.getConnections(application); log.trace("sending event to {} connections", connections.size()); for (IWebSocketConnection connection : connections) { connection.sendMessage(event); } } @Override public void sendTo(Object identifier, IWebSocketPushMessage event) { if (identifier == null) { return; } var webSocketSettings = WebSocketSettings.Holder.get(application); var connectionRegistry = webSocketSettings.getConnectionRegistry(); wicketSessionResolver.resolve(identifier).forEach(sessionId -> { var connections = connectionRegistry.getConnections(application, sessionId); log.trace("sending event to {} connections", connections.size()); for (IWebSocketConnection connection : connections) { connection.sendMessage(event); } }); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/web/servlet/websocket/WebSocketWicketWebInitializer.java ================================================ package com.giffing.wicket.spring.boot.starter.web.servlet.websocket; import com.giffing.wicket.spring.boot.starter.web.config.WicketWebInitializerConfig; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.protocol.http.WicketFilter; import org.apache.wicket.protocol.ws.javax.JavaxWebSocketFilter; /** * This initializer will be used if the JSR 356 - Java API for WebSocket is * used. * * @author Marc Giffing */ public class WebSocketWicketWebInitializer implements WicketWebInitializerConfig { @Override public WicketFilter createWicketFilter(WebApplication application) { return new JavaxWebSocketFilter(application); } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/web/servlet/websocket/WicketServerEndpointConfigRegister.java ================================================ package com.giffing.wicket.spring.boot.starter.web.servlet.websocket; import com.giffing.wicket.spring.boot.context.exceptions.WicketSpringBootException; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.websocket.DeploymentException; import jakarta.websocket.Endpoint; import jakarta.websocket.server.ServerContainer; import org.apache.wicket.protocol.ws.javax.WicketServerEndpointConfig; /** * Automatically registers the {@link WicketServerEndpointConfig} as an {@link Endpoint} * in the {@link ServerContainer} * * @author Marc Giffing */ public class WicketServerEndpointConfigRegister implements ServletContextListener { private static final String SERVER_CONTAINER_ATTRIBUTE = ServerContainer.class.getName(); @Override public void contextInitialized(ServletContextEvent sce) { var container = sce.getServletContext(); var serverContainer = (ServerContainer) container.getAttribute(SERVER_CONTAINER_ATTRIBUTE); try { serverContainer.addEndpoint(new WicketServerEndpointConfig()); } catch (DeploymentException e) { throw new WicketSpringBootException(e); } } } ================================================ FILE: wicket-spring-boot-starter/src/main/java/com/giffing/wicket/spring/boot/starter/web/servlet/websocket/WicketSessionResolver.java ================================================ package com.giffing.wicket.spring.boot.starter.web.servlet.websocket; import java.util.List; public interface WicketSessionResolver { /** * Retrieve the session id from the principle object. * * @param identifier * @return */ List resolve(Object identifier); } ================================================ FILE: wicket-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ================================================ com.giffing.wicket.spring.boot.starter.WicketAutoConfiguration ================================================ FILE: wicket-spring-boot-starter/src/main/resources/META-INF/spring-devtools.properties ================================================ restart.include.wicket-annotation=/wicketstuff-annotation-[\\w-\.]+\.jar restart.include.wicket-spring=/wicket-spring-[\\w-\.]+\.jar ================================================ FILE: wicket-spring-boot-starter/src/main/resources/META-INF/spring.factories ================================================ org.springframework.boot.env.EnvironmentPostProcessor=com.giffing.wicket.spring.boot.starter.configuration.extensions.external.development.springboot.devtools.WicketDevToolsPropertyDefaultsPostProcessor org.springframework.boot.diagnostics.FailureAnalyzer=com.giffing.wicket.spring.boot.starter.app.verifier.WicketDependencyVersionCheckerFailureAnalyzer ================================================ FILE: wicket-spring-boot-starter/src/main/resources/application-development.yml ================================================ spring: h2: console: enabled: true devtools: restart: additional-exclude: /**/*.js, /**/*.css, /**/*.html, /**/*.htm wicket: core: settings: general: configuration-type: development debug: enabled: true component-use-check: true development-utilities-enabled: true markup: strip-wicket-tags: false stuff: htmlcompressor: enabled: false features: removeComments: false removeMultiSpaces: false removeIntertagSpaces: false removeQuotes: false compressJavaScript: false compressCss: false simpleDoctype: false removeScriptAttributes: false removeStyleAttributes: false removeLinkAttributes: false removeFormAttributes: false removeInputAttributes: false simpleBooleanAttributes: false removeJavaScriptProtocol: false removeHttpProtocol: false removeHttpsProtocol: false preserveLineBreaks: false external: development: devutils: statelesschecker: enabled: true interceptor: enable-live-sessions-page: true diskstorebrowser: enabled: true wicketsource: enabled: true ================================================ FILE: wicket-spring-boot-starter-example/.gitignore ================================================ /target/ /.settings/ .classpath .project .springBeans /src/main/resources/public/docs/ ================================================ FILE: wicket-spring-boot-starter-example/pom.xml ================================================ 4.0.0 wicket-spring-boot-starter-example com.giffing.wicket.spring.boot.starter wicket-spring-boot-starter-parent 4.1.1 .. Spring boot starter example An example project which uses the wicket-spring-boot-starter autoconfiguration project true true 7.0.11 7.1.0.Final com.giffing.wicket.spring.boot.starter wicket-spring-boot-starter org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-actuator org.springframework.session spring-session-jdbc com.h2database h2 org.wicketstuff wicketstuff-annotation de.agilecoders.wicket wicket-bootstrap-core ${wicket-bootstrap.version} de.agilecoders.wicket wicket-bootstrap-extensions ${wicket-bootstrap.version} de.agilecoders.wicket wicket-bootstrap-sass ${wicket-bootstrap.version} de.agilecoders.wicket wicket-bootstrap-themes ${wicket-bootstrap.version} org.wicketstuff wicketstuff-htmlcompressor com.yahoo.platform.yui yuicompressor org.apache.wicket wicket-extensions org.apache.wicket wicket-bean-validation org.springframework.boot spring-boot-starter-validation de.agilecoders.wicket.webjars wicket-webjars jakarta.xml.bind jakarta.xml.bind-api org.wicketstuff wicketstuff-serializer-fast2 de.agilecoders.wicket jquery-selectors 4.0.6 org.liquibase liquibase-core org.apache.commons commons-lang3 org.wicketstuff wicketstuff-restannotations org.wicketstuff wicketstuff-restannotations-json org.springframework.boot spring-boot-starter-test test org.apache.wicket wicket-tester test org.projectlombok lombok provided org.hibernate.orm hibernate-processor ${hibernate-processor.version} provided org.apache.wicket wicket-native-websocket-javax release development true development org.springframework.boot spring-boot-devtools org.apache.wicket wicket-devutils org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.apache.maven.plugins maven-release-plugin none org.apache.maven.plugins maven-deploy-plugin true org.springframework.boot spring-boot-maven-plugin org.apache.maven.plugins maven-compiler-plugin 3.13.0 17 17 org.projectlombok lombok ${lombok.version} org.hibernate.orm hibernate-processor ${hibernate-processor.version} org.codehaus.mojo build-helper-maven-plugin add-source generate-sources add-source target/generated-sources/apt org.asciidoctor asciidoctor-maven-plugin 3.1.0 output-html generate-resources process-asciidoc src/main/doc target/docs html maven-resources-plugin copy-asciidoc-resources generate-resources copy-resources ${basedir}/src/main/resources/public/docs/ target/docs true src/main/resources src/main/java ** **/*.java org.eclipse.m2e lifecycle-mapping 1.0.0 org.asciidoctor asciidoctor-maven-plugin [1.5.2,) process-asciidoc org.bsc.maven maven-processor-plugin [${maven-processor-plugin.version},) process ================================================ FILE: wicket-spring-boot-starter-example/src/main/doc/index.adoc ================================================ = Example Title == Sub Section 1 == Sub Section 2 ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/WicketApplication.java ================================================ package com.giffing.wicket.spring.boot.example; import com.giffing.wicket.spring.boot.example.repository.services.DefaultRepositoryService; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @SpringBootApplication @EnableJpaRepositories(basePackageClasses = {DefaultRepositoryService.class}) public class WicketApplication { public static void main(String[] args) { new SpringApplicationBuilder() .sources(WicketApplication.class) .run(args); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/WicketApplication.properties ================================================ login.label=Login ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/model/Customer.java ================================================ package com.giffing.wicket.spring.boot.example.model; import com.giffing.wicket.spring.boot.example.repository.Domain; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import lombok.Getter; import lombok.Setter; import java.io.Serializable; @Entity @Getter @Setter public class Customer implements Domain, Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String firstname; private String lastname; private boolean active; } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/repository/DefaultFilter.java ================================================ package com.giffing.wicket.spring.boot.example.repository; public abstract class DefaultFilter implements Filter { private Sort sort; private boolean ascending; @Override public Sort sort() { return sort; } @Override public boolean isAscending() { return ascending; } @Override public void setSort(Sort sort, boolean ascending) { this.sort = sort; this.ascending = ascending; } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/repository/Domain.java ================================================ package com.giffing.wicket.spring.boot.example.repository; public interface Domain { ID getId(); } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/repository/Filter.java ================================================ package com.giffing.wicket.spring.boot.example.repository; import java.io.Serializable; public interface Filter extends Serializable { Sort sort(); boolean isAscending(); void setSort(Sort sort, boolean ascending); } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/repository/FilterService.java ================================================ package com.giffing.wicket.spring.boot.example.repository; import java.util.List; public interface FilterService { MODEL findById(ID id); List findAll(Long page, Long count, FILTER_MODEL filter); long count(FILTER_MODEL filter); } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/repository/Sort.java ================================================ package com.giffing.wicket.spring.boot.example.repository; public interface Sort { String getFieldName(); } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/repository/services/DefaultRepositoryService.java ================================================ package com.giffing.wicket.spring.boot.example.repository.services; import com.giffing.wicket.spring.boot.example.repository.Filter; import com.giffing.wicket.spring.boot.example.repository.FilterService; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; public abstract class DefaultRepositoryService implements FilterService { public Sort getSort(FILTER filter) { var sort = Sort.unsorted(); if (filter.sort() != null) { Direction direction = filter.isAscending() ? Direction.ASC : Direction.DESC; sort = Sort.by(direction, filter.sort().getFieldName()); } return sort; } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/repository/services/customer/CustomerRepository.java ================================================ package com.giffing.wicket.spring.boot.example.repository.services.customer; import com.giffing.wicket.spring.boot.example.model.Customer; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.ListCrudRepository; public interface CustomerRepository extends ListCrudRepository, JpaSpecificationExecutor { int countByUsernameIgnoreCase(String username); Customer findByUsernameIgnoreCase(String username); } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/repository/services/customer/CustomerRepositoryService.java ================================================ package com.giffing.wicket.spring.boot.example.repository.services.customer; import com.giffing.wicket.spring.boot.example.model.Customer; import com.giffing.wicket.spring.boot.example.repository.FilterService; import com.giffing.wicket.spring.boot.example.repository.services.customer.filter.CustomerFilter; import java.util.List; public interface CustomerRepositoryService extends FilterService { void delete(Long id); List findUsernames(int count, String usernamePart); Customer save(Customer customer); Customer findByUsername(String username); /** * @param username the username * @return true if the username already exists */ boolean usernameExists(String username); } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/repository/services/customer/CustomerRepositoryServiceImpl.java ================================================ package com.giffing.wicket.spring.boot.example.repository.services.customer; import com.giffing.wicket.spring.boot.example.model.Customer; import com.giffing.wicket.spring.boot.example.model.Customer_; import com.giffing.wicket.spring.boot.example.repository.services.DefaultRepositoryService; import com.giffing.wicket.spring.boot.example.repository.services.customer.filter.CustomerFilter; import com.giffing.wicket.spring.boot.example.repository.services.customer.specs.CustomerSpecs; import jakarta.annotation.Resource; import jakarta.persistence.EntityManager; import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Repository; import java.util.ArrayList; import java.util.List; import java.util.Optional; @Repository public class CustomerRepositoryServiceImpl extends DefaultRepositoryService implements CustomerRepositoryService { private CustomerRepository customerRepository; @Resource private EntityManager em; public CustomerRepositoryServiceImpl(CustomerRepository customerRepository) { this.customerRepository = customerRepository; } @Override public List findAll(Long page, Long count, CustomerFilter filter) { PageRequest pr = PageRequest.of(page.intValue(), count.intValue(), getSort(filter)); return customerRepository.findAll(filter(filter), pr).getContent(); } @Override public long count(CustomerFilter filter) { return customerRepository.count(filter(filter)); } private Specification filter(CustomerFilter filter) { List> specs = new ArrayList<>(); if (filter.getId() != null) { specs.add(CustomerSpecs.hasId(filter.getId())); } if (isNotEmpty(filter.getUsername())) { specs.add(CustomerSpecs.hasUsername(filter.getUsername())); } if (isNotEmpty(filter.getUsernameLike())) { specs.add(CustomerSpecs.hasUsernameLike(filter.getUsernameLike())); } if (isNotEmpty(filter.getFirstnameLike())) { specs.add(CustomerSpecs.hasFirstnameLike(filter.getFirstnameLike())); } if (isNotEmpty(filter.getLastnameLike())) { specs.add(CustomerSpecs.hasLastnameLike(filter.getLastnameLike())); } if (filter.isActive()) { specs.add(CustomerSpecs.hasActive(filter.isActive())); } Specification spec = null; for (Specification specification : specs) { if (spec == null) { spec = Specification.where(specification); } else { spec = spec.and(specification); } } return spec; } boolean isNotEmpty(String toCheck) { if (toCheck != null && toCheck.length() > 0) { return true; } return false; } @Override public Customer findById(Long id) { Optional customer = customerRepository.findById(id); return customer.isPresent() ? customer.get() : null; } @Override public void delete(Long id) { customerRepository.deleteById(id); } @Override public List findUsernames(int count, String usernamePart) { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery query = builder.createQuery(String.class); Root customerRoot = query.from(Customer.class); query = query.select(customerRoot.get(Customer_.username)); Predicate predicate = CustomerSpecs.hasUsernameLike(usernamePart).toPredicate(customerRoot, query, builder); query = query.where(predicate); TypedQuery createQuery = em.createQuery(query); createQuery.setMaxResults(count); return createQuery.getResultList(); } @Override public Customer save(Customer customer) { return customerRepository.save(customer); } @Override public boolean usernameExists(String username) { return customerRepository.countByUsernameIgnoreCase(username) >= 1; } @Override public Customer findByUsername(String username) { return customerRepository.findByUsernameIgnoreCase(username); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/repository/services/customer/filter/CustomerFilter.java ================================================ package com.giffing.wicket.spring.boot.example.repository.services.customer.filter; import com.giffing.wicket.spring.boot.example.repository.DefaultFilter; import lombok.Getter; import lombok.Setter; @Getter @Setter public class CustomerFilter extends DefaultFilter { private Long id; /** * Filtering for the exact user name */ private String username; /** * Filtering by lowercase username with wildcards at the start and the end. */ private String usernameLike; private String firstnameLike; private String lastnameLike; private boolean active; } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/repository/services/customer/filter/CustomerSort.java ================================================ package com.giffing.wicket.spring.boot.example.repository.services.customer.filter; import com.giffing.wicket.spring.boot.example.repository.Sort; public enum CustomerSort implements Sort { ID("id"), USERNAME("username"), FIRSTNAME("firstname"), LASTNAME("lastname"), ACTIVE("active"); private final String sortName; CustomerSort(String sortName) { this.sortName = sortName; } @Override public String getFieldName() { return this.sortName; } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/repository/services/customer/specs/CustomerSpecs.java ================================================ package com.giffing.wicket.spring.boot.example.repository.services.customer.specs; import com.giffing.wicket.spring.boot.example.model.Customer; import com.giffing.wicket.spring.boot.example.model.Customer_; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.springframework.data.jpa.domain.Specification; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class CustomerSpecs { public static Specification hasId(final Long id) { return (root, query, builder) -> builder.equal(root.get(Customer_.id), id); } public static Specification hasUsername(final String username) { return (root, query, builder) -> builder.equal(builder.lower(root.get(Customer_.username)), username.toLowerCase()); } public static Specification hasUsernameLike(final String username) { return (root, query, builder) -> builder.like(builder.lower(root.get(Customer_.username)), "%" + username.toLowerCase() + "%"); } public static Specification hasFirstnameLike(final String firstname) { return (root, query, builder) -> builder.like(builder.lower(root.get(Customer_.firstname)), "%" + firstname.toLowerCase() + "%"); } public static Specification hasLastnameLike(final String lastname) { return (root, query, builder) -> builder.like(builder.lower(root.get(Customer_.lastname)), "%" + lastname.toLowerCase() + "%"); } public static Specification hasActive(final boolean active) { return (root, query, builder) -> builder.equal(root.get(Customer_.active), active); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/SpringBootWebPackageIdentifier.java ================================================ package com.giffing.wicket.spring.boot.example.web; public interface SpringBootWebPackageIdentifier { } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/assets/base/CustomStylesCssRessourceReference.java ================================================ package com.giffing.wicket.spring.boot.example.web.assets.base; import org.apache.wicket.request.resource.CssResourceReference; public class CustomStylesCssRessourceReference extends CssResourceReference { public static final CustomStylesCssRessourceReference INSTANCE = new CustomStylesCssRessourceReference(); public CustomStylesCssRessourceReference() { super(CustomStylesCssRessourceReference.class, "custom.css"); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/assets/base/custom.css ================================================ .navbar { --bs-navbar-padding-x: 1; } .items { padding-right: 5px; } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/general/action/panel/ActionPanel.html ================================================ ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/general/action/panel/ActionPanel.java ================================================ package com.giffing.wicket.spring.boot.example.web.general.action.panel; import com.giffing.wicket.spring.boot.example.web.general.action.panel.items.AbstrractActionItem; import de.agilecoders.wicket.core.markup.html.bootstrap.list.BootstrapListView; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.panel.Panel; import java.util.List; public class ActionPanel extends Panel { public ActionPanel(String id, List items) { super(id); add(new BootstrapListView<>("items", items) { @Override protected void populateItem(ListItem item) { item.add(item.getModel().getObject()); } }); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/general/action/panel/items/AbstractActionItemLink.html ================================================ ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/general/action/panel/items/AbstractActionItemLink.java ================================================ package com.giffing.wicket.spring.boot.example.web.general.action.panel.items; import de.agilecoders.wicket.core.markup.html.bootstrap.image.Icon; import de.agilecoders.wicket.core.markup.html.bootstrap.image.IconType; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; public abstract class AbstractActionItemLink extends AbstrractActionItem { protected AbstractActionItemLink(IconType iconType) { AjaxLink link = new AjaxLink("link") { @Override public void onClick(AjaxRequestTarget target) { AbstractActionItemLink.this.onClick(target); } }; add(link); link.add(new Icon("icon", iconType)); } public abstract void onClick(AjaxRequestTarget target); } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/general/action/panel/items/AbstrractActionItem.java ================================================ package com.giffing.wicket.spring.boot.example.web.general.action.panel.items; import org.apache.wicket.markup.html.panel.Panel; public abstract class AbstrractActionItem extends Panel { protected AbstrractActionItem() { super("item"); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/general/action/panel/items/links/ActionItemLink.html ================================================ ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/general/action/panel/items/links/ActionItemLink.java ================================================ package com.giffing.wicket.spring.boot.example.web.general.action.panel.items.links; import com.giffing.wicket.spring.boot.example.web.general.action.panel.items.AbstrractActionItem; import de.agilecoders.wicket.core.markup.html.bootstrap.image.Icon; import de.agilecoders.wicket.core.markup.html.bootstrap.image.IconType; import org.apache.wicket.markup.html.link.AbstractLink; public class ActionItemLink extends AbstrractActionItem { public ActionItemLink(IconType iconType, AbstractLink link) { add(link); link.add(new Icon("icon", iconType)); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/general/action/panel/items/yesno/YesNoLink.java ================================================ package com.giffing.wicket.spring.boot.example.web.general.action.panel.items.yesno; import com.giffing.wicket.spring.boot.example.web.general.action.panel.items.AbstractActionItemLink; import com.giffing.wicket.spring.boot.example.web.html.modal.YesNoModal; import com.giffing.wicket.spring.boot.example.web.pages.BasePage; import de.agilecoders.wicket.core.markup.html.bootstrap.image.IconType; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.html.panel.EmptyPanel; public abstract class YesNoLink extends AbstractActionItemLink { protected YesNoLink(IconType iconType) { super(iconType); } @Override public void onClick(AjaxRequestTarget target) { YesNoModal yesNoModal = new YesNoModal("defaultModal") { @Override protected void yesClicked(AjaxRequestTarget target) { YesNoLink.this.yesClicked(target); close(target); ((BasePage) getPage()).replaceDefaultModal(new EmptyPanel("defaultModal"), target); } }; ((BasePage) getPage()).replaceDefaultModal(yesNoModal, target); yesNoModal.appendShowDialogJavaScript(target); } protected abstract void yesClicked(AjaxRequestTarget target); } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/general/notify/NotyJSReference.java ================================================ package com.giffing.wicket.spring.boot.example.web.general.notify; import org.apache.wicket.resource.JQueryPluginResourceReference; public class NotyJSReference extends JQueryPluginResourceReference { public static final NotyJSReference INSTANCE = new NotyJSReference(); public NotyJSReference() { super(NotyJSReference.class, "jquery.noty.js"); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/general/notify/NotyPackagedJSReference.java ================================================ package com.giffing.wicket.spring.boot.example.web.general.notify; import org.apache.wicket.resource.JQueryPluginResourceReference; public class NotyPackagedJSReference extends JQueryPluginResourceReference { public static final NotyPackagedJSReference INSTANCE = new NotyPackagedJSReference(); public NotyPackagedJSReference() { super(NotyPackagedJSReference.class, "jquery.noty.packaged.min.js"); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/general/notify/NotyThemeBootstrapJSReference.java ================================================ package com.giffing.wicket.spring.boot.example.web.general.notify; import org.apache.wicket.resource.JQueryPluginResourceReference; public class NotyThemeBootstrapJSReference extends JQueryPluginResourceReference { public static final NotyThemeBootstrapJSReference INSTANCE = new NotyThemeBootstrapJSReference(); public NotyThemeBootstrapJSReference() { super(NotyThemeBootstrapJSReference.class, "noty.theme.bootstrap.js"); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/general/notify/jquery.noty.js ================================================ /*! @package noty - jQuery Notification Plugin @version version: 2.4.1 @contributors https://github.com/needim/noty/graphs/contributors @documentation Examples and Documentation - http://needim.github.com/noty/ @license Licensed under the MIT licenses: http://www.opensource.org/licenses/mit-license.php */ if (typeof Object.create !== 'function') { Object.create = function (o) { function F() { } F.prototype = o; return new F(); }; } var NotyObject = { init: function (options) { // Mix in the passed in options with the default options this.options = $.extend({}, $.noty.defaults, options); this.options.layout = (this.options.custom) ? $.noty.layouts['inline'] : $.noty.layouts[this.options.layout]; if ($.noty.themes[this.options.theme]) { this.options.theme = $.noty.themes[this.options.theme]; if (this.options.theme.template) this.options.template = this.options.theme.template; if (this.options.theme.animation) this.options.animation = this.options.theme.animation; } else { this.options.themeClassName = this.options.theme; } this.options = $.extend({}, this.options, this.options.layout.options); if (this.options.id) { if ($.noty.store[this.options.id]) { return $.noty.store[this.options.id]; } } else { this.options.id = 'noty_' + (new Date().getTime() * Math.floor(Math.random() * 1000000)); } // Build the noty dom initial structure this._build(); // return this so we can chain/use the bridge with less code. return this; }, // end init _build: function () { // Generating noty bar var $bar = $('

').attr('id', this.options.id); $bar.append(this.options.template).find('.noty_text').html(this.options.text); this.$bar = (this.options.layout.parent.object !== null) ? $(this.options.layout.parent.object).css(this.options.layout.parent.css).append($bar) : $bar; if (this.options.themeClassName) this.$bar.addClass(this.options.themeClassName).addClass('noty_container_type_' + this.options.type); // Set buttons if available if (this.options.buttons) { var $buttons; // Try find container for buttons in presented template, and create it if not found if (this.$bar.find('.noty_buttons').length > 0) { $buttons = this.$bar.find('.noty_buttons'); } else { $buttons = $('
').addClass('noty_buttons'); (this.options.layout.parent.object !== null) ? this.$bar.find('.noty_bar').append($buttons) : this.$bar.append($buttons); } var self = this; $.each(this.options.buttons, function (i, button) { var $button = $('
================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/customers/CustomerListPage.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers; import java.util.ArrayList; import java.util.List; import com.giffing.wicket.spring.boot.context.scan.WicketHomePage; import com.giffing.wicket.spring.boot.example.web.general.action.panel.items.AbstrractActionItem; import com.giffing.wicket.spring.boot.example.web.pages.BaseAuthenticatedPage; import com.giffing.wicket.spring.boot.example.web.pages.customers.events.CustomerDeletedEvent; import com.giffing.wicket.spring.boot.starter.web.servlet.websocket.WebSocketMessageBroadcaster; import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapBookmarkablePageLink; import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons; import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome6IconType; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation; import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable; import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator; import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.filter.FilterForm; import org.apache.wicket.extensions.markup.html.repeater.data.table.filter.FilterToolbar; import org.apache.wicket.extensions.markup.html.repeater.data.table.filter.FilteredPropertyColumn; import org.apache.wicket.markup.html.form.Button; import org.apache.wicket.markup.html.form.CheckBox; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.link.BookmarkablePageLink; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.ResourceModel; import org.apache.wicket.protocol.ws.api.WebSocketBehavior; import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.validation.validator.StringValidator; import org.wicketstuff.annotation.mount.MountPath; import com.giffing.wicket.spring.boot.example.model.Customer; import com.giffing.wicket.spring.boot.example.repository.services.customer.CustomerRepositoryService; import com.giffing.wicket.spring.boot.example.repository.services.customer.filter.CustomerFilter; import com.giffing.wicket.spring.boot.example.repository.services.customer.filter.CustomerSort; import com.giffing.wicket.spring.boot.example.web.general.action.panel.ActionPanel; import com.giffing.wicket.spring.boot.example.web.general.action.panel.items.links.ActionItemLink; import com.giffing.wicket.spring.boot.example.web.general.action.panel.items.yesno.YesNoLink; import com.giffing.wicket.spring.boot.example.web.html.basic.YesNoLabel; import com.giffing.wicket.spring.boot.example.web.html.form.ValidationForm; import com.giffing.wicket.spring.boot.example.web.html.panel.FeedbackPanel; import com.giffing.wicket.spring.boot.example.web.html.repeater.data.table.filter.AbstractCheckBoxFilter; import com.giffing.wicket.spring.boot.example.web.html.repeater.data.table.filter.AbstractTextFieldFilter; import com.giffing.wicket.spring.boot.example.web.pages.customers.create.CustomerCreatePage; import com.giffing.wicket.spring.boot.example.web.pages.customers.edit.CustomerEditPage; import com.giffing.wicket.spring.boot.example.web.pages.customers.events.CustomerChangedEvent; import com.giffing.wicket.spring.boot.example.web.pages.customers.model.CustomerDataProvider; import com.giffing.wicket.spring.boot.example.web.pages.customers.model.UsernameSearchTextField; @WicketHomePage @MountPath("custsomers") @AuthorizeInstantiation("USER") public class CustomerListPage extends BaseAuthenticatedPage { @SpringBean private CustomerRepositoryService customerRepositoryService; @SpringBean private WebSocketMessageBroadcaster webSocketMessageBroadcaster; private IModel customerFilterModel; private FilterForm filterForm; DataTable dataTable; public CustomerListPage() { super(new PageParameters()); FeedbackPanel feedbackPanel = new FeedbackPanel("feedback"); feedbackPanel.setOutputMarkupId(true); add(feedbackPanel); add(getWebSocketBehavior(feedbackPanel)); customerFilterModel = new CompoundPropertyModel<>(new CustomerFilter()); CustomerDataProvider customerDataProvider = new CustomerDataProvider(customerFilterModel); queue(new BootstrapBookmarkablePageLink("create", CustomerCreatePage.class, Buttons.Type.Link) .setIconType(FontAwesome6IconType.plus_s) .setSize(Buttons.Size.Large)); queue(new ValidationForm<>("form", customerFilterModel)); queueFormComponent(new TextField<>("id")); queueFormComponent(new UsernameSearchTextField("usernameLike")); queueFormComponent(new TextField("firstnameLike").add(StringValidator.minimumLength(3))); queueFormComponent(new TextField("lastnameLike").add(StringValidator.minimumLength(3))); queueFormComponent(new CheckBox("active")); queue(cancelButton()); customerDataTable(customerDataProvider); } private WebSocketBehavior getWebSocketBehavior(FeedbackPanel feedbackPanel) { return new WebSocketBehavior() { @Override protected void onPush(WebSocketRequestHandler handler, IWebSocketPushMessage message) { if (message instanceof CustomerChangedEvent event) { info("Customer changed " + event.getCustomer().getFirstname() + " " + event.getCustomer().getLastname()); handler.add(feedbackPanel); } if (message instanceof CustomerDeletedEvent event) { warn("Customer deleted: " + event.getCustomer().getFirstname() + " " + event.getCustomer().getLastname()); handler.add(feedbackPanel); } } }; } private Button cancelButton() { Button cancelButton = new Button("cancel") { @Override public void onSubmit() { customerFilterModel.setObject(new CustomerFilter()); getForm().clearInput(); filterForm.clearInput(); } }; cancelButton.setDefaultFormProcessing(false); return cancelButton; } private void customerDataTable(CustomerDataProvider customerDataProvider) { filterForm = new FilterForm<>("filterForm", customerDataProvider); queue(filterForm); List> columns = new ArrayList<>(); columns.add(idColumn()); columns.add(usernameColumn()); columns.add(firstnameColumn()); columns.add(lastnameColumn()); columns.add(activeColumn()); columns.add(actionColumn()); dataTable = new AjaxFallbackDefaultDataTable<>("table", columns, customerDataProvider, 10); dataTable.setOutputMarkupId(true); FilterToolbar filterToolbar = new FilterToolbar(dataTable, filterForm); dataTable.addTopToolbar(filterToolbar); queue(dataTable); } private PropertyColumn idColumn() { return new PropertyColumn<>(Model.of("Id"), CustomerSort.ID, CustomerSort.ID.getFieldName()); } private FilteredPropertyColumn usernameColumn() { return new FilteredPropertyColumn<>(new ResourceModel("username"), CustomerSort.USERNAME, CustomerSort.USERNAME.getFieldName()) { @Override public Component getFilter(String componentId, FilterForm form) { return new AbstractTextFieldFilter(componentId, new PropertyModel<>(form.getModel(), "usernameLike"), form) { @Override public TextField createTextFieldComponent(String componentId, IModel model) { return new UsernameSearchTextField(componentId, model); } }; } }; } private FilteredPropertyColumn firstnameColumn() { return new FilteredPropertyColumn<>(new ResourceModel("firstname"), CustomerSort.FIRSTNAME, CustomerSort.FIRSTNAME.getFieldName()) { @Override public Component getFilter(String componentId, FilterForm form) { return new AbstractTextFieldFilter(componentId, new PropertyModel<>(form.getModel(), "firstnameLike"), form) { @Override public TextField createTextFieldComponent(String componentId, IModel model) { return new TextField<>(componentId, model); } }; } }; } private FilteredPropertyColumn lastnameColumn() { return new FilteredPropertyColumn<>(new ResourceModel("lastname"), CustomerSort.LASTNAME, CustomerSort.LASTNAME.getFieldName()) { @Override public Component getFilter(String componentId, FilterForm form) { return new AbstractTextFieldFilter(componentId, new PropertyModel<>(form.getModel(), "lastnameLike"), form) { @Override public TextField createTextFieldComponent(String componentId, IModel model) { return new TextField<>(componentId, model); } }; } }; } private FilteredPropertyColumn activeColumn() { return new FilteredPropertyColumn<>(new ResourceModel("active"), CustomerSort.ACTIVE, CustomerSort.ACTIVE.getFieldName()) { @Override public Component getFilter(String componentId, FilterForm form) { return new AbstractCheckBoxFilter(componentId, new PropertyModel<>(form.getModel(), "active"), form); } @Override public void populateItem(Item> item, String componentId, IModel rowModel) { item.add(new YesNoLabel(componentId, (IModel) getDataModel(rowModel))); } }; } private AbstractColumn actionColumn() { return new AbstractColumn<>(Model.of("Action")) { @Override public void populateItem(Item> cellItem, String componentId, IModel rowModel) { List abstractItems = new ArrayList<>(); var params = new PageParameters(); params.add(CustomerEditPage.CUSTOMER_ID_PARAM, rowModel.getObject().getId()); params.add(CustomerEditPage.PAGE_REFERENCE_ID, getPageId()); abstractItems.add(editActionItem(params)); abstractItems.add(deleteActionItem(rowModel)); cellItem.add(new ActionPanel(componentId, abstractItems)); } private static ActionItemLink editActionItem(PageParameters params) { return new ActionItemLink( FontAwesome6IconType.pen_s, new BookmarkablePageLink("link", CustomerEditPage.class, params) ); } private YesNoLink deleteActionItem(IModel rowModel) { return new YesNoLink<>(FontAwesome6IconType.trash_s) { @Override protected void yesClicked(AjaxRequestTarget target) { customerRepositoryService.delete(rowModel.getObject().getId()); webSocketMessageBroadcaster.sendToAll(new CustomerDeletedEvent(rowModel.getObject())); target.add(dataTable); } }; } }; } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/customers/create/CustomerCreatePage.html ================================================

Customer Create


================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/customers/create/CustomerCreatePage.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers.create; import com.giffing.wicket.spring.boot.example.model.Customer; import com.giffing.wicket.spring.boot.example.repository.services.customer.CustomerRepositoryService; import com.giffing.wicket.spring.boot.example.web.html.form.ValidationForm; import com.giffing.wicket.spring.boot.example.web.pages.BaseAuthenticatedPage; import com.giffing.wicket.spring.boot.example.web.pages.customers.CustomerListPage; import com.giffing.wicket.spring.boot.example.web.pages.customers.events.CustomerChangedEvent; import com.giffing.wicket.spring.boot.example.web.pages.customers.model.UsernameTextField; import com.giffing.wicket.spring.boot.starter.web.servlet.websocket.WebSocketMessageBroadcaster; import lombok.Getter; import lombok.Setter; import org.apache.wicket.Component; import org.apache.wicket.PageReference; import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation; import org.apache.wicket.markup.html.form.Button; import org.apache.wicket.markup.html.form.CheckBox; import org.apache.wicket.markup.html.form.FormComponent; import org.apache.wicket.markup.html.form.RequiredTextField; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.spring.injection.annot.SpringBean; import org.wicketstuff.annotation.mount.MountPath; @MountPath("customers/create") @AuthorizeInstantiation("USER") public class CustomerCreatePage extends BaseAuthenticatedPage { @SpringBean private CustomerRepositoryService service; @SpringBean private WebSocketMessageBroadcaster webSocketMessageBroadcaster; @Getter CompoundPropertyModel customerModel; @Setter private Integer pageReferenceId; public CustomerCreatePage(Integer pageId) { super(new PageParameters()); this.pageReferenceId = pageId; } public CustomerCreatePage() { super(new PageParameters()); customerModel = new CompoundPropertyModel<>(new Customer()); queue(new ValidationForm<>("form", customerModel)); queueFormComponent(usernameField()); queueFormComponent(new RequiredTextField<>("firstname")); queueFormComponent(new RequiredTextField<>("lastname")); queueFormComponent(new CheckBox("active")); queue(submitButton()); queue(cancelButton()); } private FormComponent usernameField() { return new UsernameTextField("username") { @Override public boolean isEnabled() { return isCreatePage(); } }; } public boolean isCreatePage() { return true; } private Component submitButton() { return new Button("submit") { @Override public void onSubmit() { var customer = customerModel.getObject(); service.save(customer); webSocketMessageBroadcaster.sendToAll(new CustomerChangedEvent(customer)); if (pageReferenceId != null) { setResponsePage(new PageReference(pageReferenceId).getPage()); } else { setResponsePage(CustomerListPage.class); } } }; } private Component cancelButton() { Button cancelButton = new Button("cancel") { @Override public void onSubmit() { if (pageReferenceId != null) { setResponsePage(new PageReference(pageReferenceId).getPage()); } else { setResponsePage(CustomerListPage.class); } } }; cancelButton.setDefaultFormProcessing(false); return cancelButton; } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/customers/edit/CustomerEditPage.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers.edit; import com.giffing.wicket.spring.boot.example.model.Customer; import com.giffing.wicket.spring.boot.example.repository.services.customer.CustomerRepositoryService; import com.giffing.wicket.spring.boot.example.web.pages.customers.CustomerListPage; import com.giffing.wicket.spring.boot.example.web.pages.customers.create.CustomerCreatePage; import org.apache.commons.lang3.StringUtils; import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.util.string.StringValue; import org.wicketstuff.annotation.mount.MountPath; import java.text.MessageFormat; @MountPath("customers/edit/${" + CustomerEditPage.CUSTOMER_ID_PARAM + "}") @AuthorizeInstantiation("USER") public class CustomerEditPage extends CustomerCreatePage { public static final String CUSTOMER_ID_PARAM = "id"; public static final String PAGE_REFERENCE_ID = "referenceId"; @SpringBean private CustomerRepositoryService service; public CustomerEditPage(PageParameters params) { super(); var customerIdParam = params.get(CUSTOMER_ID_PARAM); if (customerIdParam.isEmpty() || !StringUtils.isNumeric(customerIdParam.toString())) { getSession().error(MessageFormat.format(getString("param.customer.id.missing"), customerIdParam)); // "Missing customer id " + stringValue setResponsePage(CustomerListPage.class); } Long customerId = customerIdParam.toLong(); var customer = service.findById(customerId); if (customer == null) { getSession().error(MessageFormat.format(getString("customer.not-found"), customerId.toString())); setResponsePage(CustomerListPage.class); } var pageReferfenceIdParam = params.get(PAGE_REFERENCE_ID); if (!pageReferfenceIdParam.isEmpty() || StringUtils.isNumeric(pageReferfenceIdParam.toString())) { setPageReferenceId(pageReferfenceIdParam.toInteger()); } getCustomerModel().setObject(customer); } @Override public boolean isCreatePage() { return false; } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/customers/edit/CustomerEditPage.properties ================================================ param.customer.id.missing=Missing customer id {0} customer.not-found=Customer not found {0} ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/customers/events/CustomerChangedEvent.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers.events; import com.giffing.wicket.spring.boot.example.model.Customer; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage; @Getter @RequiredArgsConstructor public class CustomerChangedEvent implements IWebSocketPushMessage { private final Customer customer; } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/customers/events/CustomerDeletedEvent.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers.events; import com.giffing.wicket.spring.boot.example.model.Customer; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage; @Getter @RequiredArgsConstructor public class CustomerDeletedEvent implements IWebSocketPushMessage { private final Customer customer; } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/customers/model/CustomerDataProvider.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers.model; import com.giffing.wicket.spring.boot.example.model.Customer; import com.giffing.wicket.spring.boot.example.repository.services.customer.CustomerRepositoryService; import com.giffing.wicket.spring.boot.example.repository.services.customer.filter.CustomerFilter; import com.giffing.wicket.spring.boot.example.repository.services.customer.filter.CustomerSort; import com.giffing.wicket.spring.boot.example.web.wicket.dataprovider.DefaultDataProvider; import lombok.Getter; import lombok.Setter; import org.apache.wicket.injection.Injector; import org.apache.wicket.model.IModel; import org.apache.wicket.spring.injection.annot.SpringBean; @Getter @Setter public class CustomerDataProvider extends DefaultDataProvider { @SpringBean private CustomerRepositoryService filterService; private IModel customerFilterModel; public CustomerDataProvider(IModel customerFilterModel) { this.customerFilterModel = customerFilterModel; Injector.get().inject(this); } @Override public CustomerFilter getFilter() { return customerFilterModel.getObject(); } @Override public void setFilter(CustomerFilter filterModel) { customerFilterModel.setObject(filterModel); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/customers/model/UsernameSearchTextField.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers.model; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteSettings; import org.apache.wicket.model.IModel; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.validation.validator.StringValidator; import com.giffing.wicket.spring.boot.example.repository.services.customer.CustomerRepositoryService; import com.giffing.wicket.spring.boot.example.web.html.autocomplete.AutoCompleteTextField; public class UsernameSearchTextField extends AutoCompleteTextField { private static final Integer MINIMUM_INPUT_LENGTH = 3; @SpringBean private CustomerRepositoryService service; public UsernameSearchTextField(String id) { super(id, settings()); initComponent(); } public UsernameSearchTextField(String componentId, IModel model) { super(componentId, model, settings()); initComponent(); } private void initComponent() { add(StringValidator.minimumLength(MINIMUM_INPUT_LENGTH)); } @Override protected Iterator getChoices(String username) { List result = new ArrayList<>(); if(username != null && username.length() >= MINIMUM_INPUT_LENGTH){ result = service.findUsernames(10, username); } return result.iterator(); } private static AutoCompleteSettings settings(){ return new AutoCompleteSettings().setMinInputLength(MINIMUM_INPUT_LENGTH); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/customers/model/UsernameTextField.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers.model; import org.apache.wicket.injection.Injector; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.validation.IValidatable; import org.apache.wicket.validation.IValidator; import org.apache.wicket.validation.ValidationError; import org.apache.wicket.validation.validator.StringValidator; import com.giffing.wicket.spring.boot.example.repository.services.customer.CustomerRepositoryService; public class UsernameTextField extends TextField{ @SpringBean private CustomerRepositoryService service; public UsernameTextField(String id) { super(id); Injector.get().inject(this); add(StringValidator.minimumLength(3)); setRequired(true); add(new UsernameExistsValidator()); } public class UsernameExistsValidator implements IValidator{ @Override public void validate(IValidatable validatable) { final String field = validatable.getValue(); if(service.usernameExists(field)){ ValidationError error = new ValidationError(); error.setMessage(getString(getClass().getSimpleName())); validatable.error(error); } } } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/errors/AccessDeniedPage.html ================================================ Access Denied!

Access Denied!

...
================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/errors/AccessDeniedPage.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.errors; import com.giffing.wicket.spring.boot.context.scan.WicketAccessDeniedPage; import com.giffing.wicket.spring.boot.example.web.pages.BasePage; import jakarta.servlet.http.HttpServletResponse; import org.apache.wicket.request.http.WebResponse; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.wicketstuff.annotation.mount.MountPath; @MountPath("problem") @WicketAccessDeniedPage public class AccessDeniedPage extends BasePage { public AccessDeniedPage(PageParameters parameters) { super(parameters); } @Override protected void setHeaders(final WebResponse response) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); } @Override public boolean isErrorPage() { return true; } @Override public boolean isVersioned() { return false; } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/errors/ExpiredPage.html ================================================ Expired

Expired!

...
================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/errors/ExpiredPage.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.errors; import com.giffing.wicket.spring.boot.example.web.pages.BasePage; import jakarta.servlet.http.HttpServletResponse; import org.apache.wicket.request.http.WebResponse; import com.giffing.wicket.spring.boot.context.scan.WicketExpiredPage; import org.apache.wicket.request.mapper.parameter.PageParameters; @WicketExpiredPage public class ExpiredPage extends BasePage { public ExpiredPage(PageParameters parameters) { super(parameters); } @Override protected void setHeaders(final WebResponse response) { response.setStatus(HttpServletResponse.SC_GONE); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/errors/InternalErrorPage.html ================================================ Internal Error

Internal Error!

...
================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/errors/InternalErrorPage.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.errors; import com.giffing.wicket.spring.boot.context.scan.WicketInternalErrorPage; import com.giffing.wicket.spring.boot.example.web.pages.BasePage; import org.apache.wicket.request.mapper.parameter.PageParameters; @WicketInternalErrorPage public class InternalErrorPage extends BasePage { public InternalErrorPage(PageParameters parameters) { super(parameters); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/footer/Footer.html ================================================ Footer
================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/footer/Footer.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.footer; import org.apache.wicket.markup.html.panel.Panel; public class Footer extends Panel { public Footer(String markupId) { super(markupId); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/login/LoginPage.html ================================================ Spring Boot - Apache Wicket Quickstart

login


  • Username
  • Password
  • admin
  • admin
  • customer
  • customer
================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/login/LoginPage.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.login; import com.giffing.wicket.spring.boot.context.scan.WicketSignInPage; import com.giffing.wicket.spring.boot.example.web.html.form.focus.FocusBehaviour; import com.giffing.wicket.spring.boot.example.web.html.panel.FeedbackPanel; import com.giffing.wicket.spring.boot.example.web.pages.BasePage; import com.giffing.wicket.spring.boot.example.web.pages.customers.CustomerListPage; import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession; import org.apache.wicket.authroles.authentication.AuthenticatedWebSession; import org.apache.wicket.markup.html.form.PasswordTextField; import org.apache.wicket.markup.html.form.RequiredTextField; import org.apache.wicket.markup.html.form.StatelessForm; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.wicketstuff.annotation.mount.MountPath; /** * Default login page. * * @author Marc Giffing */ @WicketSignInPage @MountPath("login") public class LoginPage extends BasePage { public LoginPage(PageParameters parameters) { super(parameters); if (((AbstractAuthenticatedWebSession) getSession()).isSignedIn()) { continueToOriginalDestination(); setResponsePage(CustomerListPage.class); } add(new LoginForm("loginForm")); } private class LoginForm extends StatelessForm { private String username; private String password; public LoginForm(String id) { super(id); setModel(new CompoundPropertyModel<>(this)); add(new FeedbackPanel("feedback")); queueFormComponent(usernameField()); queueFormComponent(new PasswordTextField("password")); } private RequiredTextField usernameField() { var usernameField = new RequiredTextField("username"); usernameField.setOutputMarkupId(true); usernameField.add(new FocusBehaviour()); return usernameField; } @Override protected void onSubmit() { AuthenticatedWebSession session = AuthenticatedWebSession.get(); if (session.signIn(username, password)) { setResponsePage(CustomerListPage.class); } else { error("Login failed"); } } } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/websocket/ChatPage.css ================================================ ul { list-style-type: none; } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/websocket/ChatPage.html ================================================ Chat

Chat


:

================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/websocket/ChatPage.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.websocket; import com.giffing.wicket.spring.boot.example.web.html.panel.FeedbackPanel; import com.giffing.wicket.spring.boot.example.web.pages.BaseAuthenticatedPage; import com.giffing.wicket.spring.boot.example.web.pages.websocket.events.CustomerMessageEvent; import com.giffing.wicket.spring.boot.example.web.pages.websocket.events.JoinChatEvent; import com.giffing.wicket.spring.boot.example.web.pages.websocket.events.LeftChatEvent; import com.giffing.wicket.spring.boot.starter.web.servlet.websocket.WebSocketMessageBroadcaster; import de.agilecoders.wicket.jquery.JQuery; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.form.AjaxButton; import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation; import org.apache.wicket.markup.head.CssHeaderItem; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.RequiredTextField; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.protocol.ws.api.WebSocketBehavior; import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; import org.apache.wicket.protocol.ws.api.message.ClosedMessage; import org.apache.wicket.protocol.ws.api.message.ConnectedMessage; import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.request.resource.PackageResourceReference; import org.apache.wicket.spring.injection.annot.SpringBean; import org.springframework.security.core.context.SecurityContextHolder; import org.wicketstuff.annotation.mount.MountPath; import java.io.Serializable; import java.util.ArrayList; import java.util.UUID; @MountPath("chat") @AuthorizeInstantiation("USER") public class ChatPage extends BaseAuthenticatedPage { @SpringBean private WebSocketMessageBroadcaster webSocketMessageBroadcaster; @SpringBean private ChatService chatService; private WebMarkupContainer chatMessageContainer; private ListView messages; private RequiredTextField messageInput; private DropDownChoice participantChoice; public ChatPage(PageParameters pageParameters) { super(pageParameters); add(new FeedbackPanel("feedback").setOutputMarkupId(true)); addWebsocketBehaviour(); addChatForm(); } private void addChatForm() { IModel to = Model.of(""); IModel message = Model.of(""); var form = new Form("form"); participantChoice = new DropDownChoice<>("participants", to, () -> chatService.getParticipants().stream().toList()); participantChoice.setOutputMarkupId(true); messageInput = new RequiredTextField<>("message", message); messageInput.setOutputMarkupId(true); AjaxButton submitButton = new AjaxButton("submit") { @Override protected void onSubmit(AjaxRequestTarget target) { String name = SecurityContextHolder.getContext().getAuthentication().getName(); webSocketMessageBroadcaster.sendToAll(new CustomerMessageEvent(name, message.getObject())); messageInput.setModelObject(null); target.add(messageInput); } }; form.setDefaultButton(submitButton); messages = new ListView<>("messages", new ArrayList<>()) { @Override protected void populateItem(ListItem item) { ChatMessage modelObject = item.getModelObject(); item.add(new Label("from", modelObject.getFrom())); item.add(new Label("message", modelObject.getMessage())); } }; messages.setOutputMarkupId(true); chatMessageContainer = new WebMarkupContainer("chatMessageContainer"); chatMessageContainer.setOutputMarkupId(true); chatMessageContainer.add(messages); add(form); form.add(submitButton); form.add(participantChoice); form.add(messageInput); form.add(chatMessageContainer); } private void addWebsocketBehaviour() { var currentUsername = SecurityContextHolder.getContext().getAuthentication().getName(); var browserTabIdentifier = UUID.randomUUID().toString(); var chatParticipant = new ChatParticipant(browserTabIdentifier, currentUsername); add(new WebSocketBehavior() { @Override protected void onPush(WebSocketRequestHandler handler, IWebSocketPushMessage message) { if (message instanceof CustomerMessageEvent event) { messages.getModelObject().add(new ChatMessage(event.getSender(), event.getMessage())); handler.add(chatMessageContainer); } if (message instanceof JoinChatEvent event) { var notifyStatus = "success"; var notifyMessage = "%s joined the chat".formatted(event.getUsername()); addNotifyMessage(handler, notifyStatus, notifyMessage); handler.add(participantChoice); } if (message instanceof LeftChatEvent event) { String notifyStatus = "warning"; String notifyMessage = "%s left the chat".formatted(event.getUsername()); addNotifyMessage(handler, notifyStatus, notifyMessage); handler.add(participantChoice); } } private void addNotifyMessage(WebSocketRequestHandler handler, String notifyStatus, String notifyMessage) { handler.appendJavaScript(JQuery .plain("noty({text: '%s', type: '%s', timeout: '5000', progressbar: true});" .formatted(notifyMessage, notifyStatus))); } @Override protected void onConnect(ConnectedMessage message) { chatService.join(chatParticipant); } @Override protected void onClose(ClosedMessage message) { chatService.leave(chatParticipant); } }); } @Getter @RequiredArgsConstructor private static class ChatMessage implements Serializable { private final String from; private final String message; } @Override public void renderHead(IHeaderResponse response) { super.renderHead(response); response.render(CssHeaderItem.forReference(new PackageResourceReference(ChatPage.class, "ChatPage.css"))); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/websocket/ChatPage.properties ================================================ username=Username message=Message ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/websocket/ChatParticipant.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.websocket; import lombok.Data; import lombok.RequiredArgsConstructor; import java.io.Serializable; /** * The user can login to multiple browsers and tabs. * This class is used to identify a specific user with an open browser tab * * @author Marc Giffing */ @Data @RequiredArgsConstructor public class ChatParticipant implements Serializable { private final String username; /** * The user can login to multiple browsers and tabs * This identifier is used to identify a single browser tab. */ private final String browserTabIdentifier; } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/websocket/ChatService.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.websocket; import com.giffing.wicket.spring.boot.example.web.pages.websocket.events.JoinChatEvent; import com.giffing.wicket.spring.boot.example.web.pages.websocket.events.LeftChatEvent; import com.giffing.wicket.spring.boot.starter.web.servlet.websocket.WebSocketMessageBroadcaster; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @Component public class ChatService { private final WebSocketMessageBroadcaster broadcaster; private List participants = new ArrayList<>(); public ChatService(WebSocketMessageBroadcaster broadcaster) { this.broadcaster = broadcaster; } public Set getParticipants() { return participants .stream() .map(ChatParticipant::getUsername) .collect(Collectors.toSet()); } public void join(ChatParticipant chatParticipant) { var username = chatParticipant.getUsername(); var existingUserSpecificParticipants = participants .stream() .filter(p -> p.getUsername().equalsIgnoreCase(chatParticipant.getUsername())) .toList(); participants.add(chatParticipant); if (existingUserSpecificParticipants.isEmpty()) { broadcaster.sendToAll(new JoinChatEvent(username)); } } public void leave(ChatParticipant chatParticipant) { var username = chatParticipant.getUsername(); var chatParticipateToDelete = participants .stream() .filter(p -> p.getBrowserTabIdentifier().equals(chatParticipant.getBrowserTabIdentifier())) .findAny(); chatParticipateToDelete.ifPresent(participant -> participants.remove(participant)); var remainingUserSpecificParticipants = participants .stream() .filter(p -> p.getUsername().equalsIgnoreCase(chatParticipant.getUsername())) .toList(); if (remainingUserSpecificParticipants.isEmpty()) { broadcaster.sendToAll(new LeftChatEvent(username)); } } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/websocket/events/CustomerMessageEvent.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.websocket.events; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage; /** * Used to send a message to a specific user */ @Getter @RequiredArgsConstructor public class CustomerMessageEvent implements IWebSocketPushMessage { private final String sender; private final String message; } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/websocket/events/JoinChatEvent.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.websocket.events; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage; @Getter @RequiredArgsConstructor public class JoinChatEvent implements IWebSocketPushMessage { private final String username; } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/pages/websocket/events/LeftChatEvent.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.websocket.events; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage; @Getter @RequiredArgsConstructor public class LeftChatEvent implements IWebSocketPushMessage { private final String username; } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/security/SpringSecurityWicketSessionResolver.java ================================================ package com.giffing.wicket.spring.boot.example.web.security; import com.giffing.wicket.spring.boot.starter.web.servlet.websocket.WicketSessionResolver; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.session.FindByIndexNameSessionRepository; import org.springframework.session.Session; import java.util.ArrayList; import java.util.List; import java.util.Map; @RequiredArgsConstructor public class SpringSecurityWicketSessionResolver implements WicketSessionResolver { private final FindByIndexNameSessionRepository sessions; @Override public List resolve(Object identifier) { Map findByPrincipalName = sessions.findByPrincipalName(identifier.toString()); return new ArrayList<>(findByPrincipalName.keySet()); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/security/WicketWebSecurityAdapterConfig.java ================================================ package com.giffing.wicket.spring.boot.example.web.security; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.firewall.HttpFirewall; import org.springframework.security.web.firewall.StrictHttpFirewall; /** * Default Spring Boot Wicket security getting started configuration. Its only * active if there is not other {@link SecurityFilterChain} bean is present. *

* Holds hard coded users which should only be used to get started * * @author Marc Giffing */ @Configuration @EnableWebSecurity public class WicketWebSecurityAdapterConfig { @ConditionalOnMissingBean @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } @ConditionalOnMissingBean @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .securityContext(ctx -> ctx.requireExplicitSave(false)) .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests.requestMatchers("/**").permitAll()) .logout(LogoutConfigurer::permitAll) .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) .build(); } @Bean public static BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean //TODO Add Wicket Issue - problem with semicolon in wicket websocket url. Allow semicolon. public HttpFirewall allowUrlEncodedSlashHttpFirewall() { StrictHttpFirewall fw = new StrictHttpFirewall(); fw.setAllowSemicolon(true); return fw; } @ConditionalOnMissingBean @Bean public UserDetailsService userDetailsService(final PasswordEncoder passwordEncoder) { InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser( User.withUsername("admin") .password(passwordEncoder.encode("admin")) .authorities("USER", "ADMIN") .build()); manager.createUser( User.withUsername("customer") .password(passwordEncoder.encode("customer")) .authorities("USER", "ADMIN") .build()); return manager; } //@Bean //public WicketSessionResolver springSecurityWicketSessionResolver() { // return new SpringSecurityWicketSessionResolver(); //} } ================================================ FILE: wicket-spring-boot-starter-example/src/main/java/com/giffing/wicket/spring/boot/example/web/wicket/dataprovider/DefaultDataProvider.java ================================================ package com.giffing.wicket.spring.boot.example.web.wicket.dataprovider; import java.util.Iterator; import java.util.List; import org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortState; import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider; import org.apache.wicket.extensions.markup.html.repeater.data.table.filter.IFilterStateLocator; import org.apache.wicket.extensions.markup.html.repeater.util.SingleSortState; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; import com.giffing.wicket.spring.boot.example.repository.Domain; import com.giffing.wicket.spring.boot.example.repository.Filter; import com.giffing.wicket.spring.boot.example.repository.FilterService; import com.giffing.wicket.spring.boot.example.repository.Sort; public abstract class DefaultDataProvider, ID, FILTER_MODEL extends Filter, SORT, SERVICE extends FilterService> implements ISortableDataProvider, IFilterStateLocator { public abstract SERVICE getFilterService(); public abstract FILTER_MODEL getFilter(); public abstract void setFilter(FILTER_MODEL filterModel); private SingleSortState singleSortState = new SingleSortState<>(); @Override public Iterator iterator(long first, long count) { if(singleSortState.getSort() != null){ Sort property = (Sort) singleSortState.getSort().getProperty(); boolean ascending = singleSortState.getSort().isAscending(); getFilter().setSort(property, ascending); } //TODO quick and dirty check again long page = first / count; return getFilterService().findAll(page, count, this.getFilter()).iterator(); } @Override public long size() { long count = getFilterService().count(this.getFilter()); return count; } @Override public IModel model(MODEL object) { ID id = object.getId(); return new CompoundPropertyModel<>(new LoadableDetachableModel(object) { @Override protected MODEL load() { return getFilterService().findById(id); } }); } @Override public ISortState getSortState() { return singleSortState; } @Override public FILTER_MODEL getFilterState() { return getFilter(); } @Override public void setFilterState(FILTER_MODEL state) { setFilter(state); } } ================================================ FILE: wicket-spring-boot-starter-example/src/main/resources/META-INF/spring-devtools.properties ================================================ restart.include.wicketx=/wicketstuff-annotation-[\\w-\.]+\.jar restart.include.wicket-spring-boot-starter=/wicket-spring-boot-starter-[\\w-\.]+\.jar ================================================ FILE: wicket-spring-boot-starter-example/src/main/resources/application.yml ================================================ wicket: stuff: datastore: hazelcast: enabled: false core: resourcesettings: packageresourceguard: pattern: - +*.map csrf: enabled: false settings: markup: stripWicketTags: true endpoints: web: exposure: include: "*" server: compression: enabled: true port: 8081 spring: main: allow-bean-definition-overriding: true jpa: hibernate: ddl-auto: none liquibase: change-log: classpath:/db/changelog/db.changelog-master.xml session: store-type: jdbc jdbc: initialize-schema: always schema: classpath:org/springframework/session/jdbc/schema-h2.sql ================================================ FILE: wicket-spring-boot-starter-example/src/main/resources/db/changelog/db.changelog-master.xml ================================================ ================================================ FILE: wicket-spring-boot-starter-example/src/main/resources/db/changelog/init.xml ================================================ ================================================ FILE: wicket-spring-boot-starter-example/src/main/resources/db/changelog/table_customer.xml ================================================ ================================================ FILE: wicket-spring-boot-starter-example/src/main/resources/db/changelog/test-data.xml ================================================ frodo Frodo Beutlin true bilbo Bilbo Beutlin false adalgrim Adalgrim Tuk true bodo Bodo Stolzfuß false tuk Ferdibrand Tuk true ================================================ FILE: wicket-spring-boot-starter-example/src/main/resources/logback.xml ================================================ ================================================ FILE: wicket-spring-boot-starter-example/src/test/java/com/giffing/wicket/spring/boot/example/web/WicketBaseIntTest.java ================================================ package com.giffing.wicket.spring.boot.example.web; import com.giffing.wicket.spring.boot.example.WicketApplication; import com.giffing.wicket.spring.boot.example.web.pages.login.LoginPage; import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.security.SecureWebSession; import com.giffing.wicket.spring.boot.starter.web.servlet.websocket.WebSocketMessageBroadcaster; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.util.tester.FormTester; import org.apache.wicket.util.tester.WicketTester; import org.junit.jupiter.api.BeforeEach; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; /** * Test class for initialize Wicket & Spring Boot only in the web package. All * external spring beans have to be mocked. * * @author Marc Giffing * */ @SpringBootTest(classes = WicketApplication.class) public abstract class WicketBaseIntTest { private static final String USERNAME = "admin"; private static final String PASSWORD = "admin"; private WicketTester tester; @Autowired private WebApplication wicketApplication; @Configuration public static class TestConfig { @Bean @Primary public WebSocketMessageBroadcaster webSocketMessageBroadcaster() { return Mockito.mock(WebSocketMessageBroadcaster.class); } } @BeforeEach public void setUp() { tester = new WicketTester(wicketApplication, new WicketMockServletContext(wicketApplication, null)); login(USERNAME, PASSWORD); } private void login(String username, String password) { SecureWebSession session = (SecureWebSession) tester.getSession(); session.signOut(); tester.startPage(LoginPage.class); FormTester formTester = tester.newFormTester("loginForm"); formTester.setValue(borderPath("username"), username); formTester.setValue(borderPath("password"), password); formTester.submit(); tester.assertNoErrorMessage(); tester.assertRenderedPage(tester.getApplication().getHomePage()); } public WicketTester getTester() { return tester; } public WebApplication getWicketApplication() { return wicketApplication; } protected String borderPath(String componentName){ return componentName + "Border:" + componentName + "Border_body:" + componentName; } } ================================================ FILE: wicket-spring-boot-starter-example/src/test/java/com/giffing/wicket/spring/boot/example/web/WicketBaseTest.java ================================================ package com.giffing.wicket.spring.boot.example.web; import com.giffing.wicket.spring.boot.example.web.pages.customers.CustomerListPage; import com.giffing.wicket.spring.boot.example.web.pages.login.LoginPage; import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.security.SecureWebSession; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.util.tester.FormTester; import org.apache.wicket.util.tester.WicketTester; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.util.ReflectionTestUtils; import test.com.giffing.wicket.spring.boot.example.web.WicketWebApplicationConfig; /** * Test class for initialize Wicket & Spring Boot only in the web package. All * external spring beans have to be mocked. * * @author Marc Giffing * */ @ExtendWith(SpringExtension.class) @SpringBootTest(classes = WicketWebApplicationConfig.class) @Disabled public class WicketBaseTest { private static final String USERNAME = "admin"; private static final String PASSWORD = "admin"; private WicketTester tester; @Autowired private WebApplication wicketApplication; @Autowired private ApplicationContext applicationContextMock; @MockitoBean private CustomAuthenticationManager customAuthenticationManager; @BeforeEach public void setUp() { ReflectionTestUtils.setField(wicketApplication, "applicationContext", applicationContextMock); tester = new WicketTester(wicketApplication, new WicketMockServletContext(wicketApplication, null)); login(USERNAME, PASSWORD); } public static class CustomAuthenticationManager implements AuthenticationManager { @Override public Authentication authenticate(Authentication arg0) throws AuthenticationException { return new TestingAuthenticationToken(USERNAME, PASSWORD, "USER", "ADMIN"); } } private void login(String username, String password) { SecureWebSession session = (SecureWebSession) tester.getSession(); session.signOut(); tester.startPage(LoginPage.class); FormTester formTester = tester.newFormTester("loginForm"); formTester.setValue(borderPath("username"), username); formTester.setValue(borderPath("password"), password); formTester.submit(); tester.assertNoErrorMessage(); tester.assertRenderedPage(CustomerListPage.class); } public WicketTester getTester() { return tester; } public WebApplication getWicketApplication() { return wicketApplication; } protected String borderPath(String componentName){ return componentName + "Border:" + componentName + "Border_body:" + componentName; } } ================================================ FILE: wicket-spring-boot-starter-example/src/test/java/com/giffing/wicket/spring/boot/example/web/WicketMockServletContext.java ================================================ package com.giffing.wicket.spring.boot.example.web; import java.util.Set; import jakarta.servlet.SessionTrackingMode; import org.apache.wicket.Application; import org.apache.wicket.protocol.http.mock.MockServletContext; public class WicketMockServletContext extends MockServletContext { public WicketMockServletContext(Application application, String path) { super( application, path ); } @Override public Set getDefaultSessionTrackingModes() { return Set.of(SessionTrackingMode.COOKIE); } @Override public Set getEffectiveSessionTrackingModes() { return Set.of(SessionTrackingMode.COOKIE); } } ================================================ FILE: wicket-spring-boot-starter-example/src/test/java/com/giffing/wicket/spring/boot/example/web/pages/customers/CustomerListIntTest.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable; import org.apache.wicket.markup.repeater.Item; import org.junit.jupiter.api.Test; import org.springframework.test.annotation.Rollback; import org.springframework.transaction.annotation.Transactional; import com.giffing.wicket.spring.boot.example.model.Customer; import com.giffing.wicket.spring.boot.example.repository.services.customer.filter.CustomerSort; import com.giffing.wicket.spring.boot.example.web.WicketBaseIntTest; import com.giffing.wicket.spring.boot.example.web.html.modal.YesNoModal; @Transactional @Rollback public class CustomerListIntTest extends WicketBaseIntTest { @Test @SuppressWarnings({ "rawtypes", "unchecked" }) public void assert_start_customer_list_page() { getTester().startPage(CustomerListPage.class); getTester().assertRenderedPage(CustomerListPage.class); getTester().assertComponent("filterForm:table", DataTable.class); DataTable dataTable = (DataTable) getTester().getComponentFromLastRenderedPage( "filterForm:table"); assertThat(dataTable.getItemCount(), equalTo(5L)); //id, username, firstname, lastname, active, actions assertThat(dataTable.getColumns().size(), equalTo(6)); //get third row Item item3 = (Item) getTester().getComponentFromLastRenderedPage("filterForm:table:body:rows:3"); assertThat(item3.getModelObject().getId(), equalTo(3L)); assertThat(item3.getModelObject().getUsername(), equalTo("adalgrim")); Item item5 = (Item) getTester().getComponentFromLastRenderedPage("filterForm:table:body:rows:5"); assertThat(item5.getModelObject().getId(), equalTo(5L)); assertThat(item5.getModelObject().getUsername(), equalTo("tuk")); } @Test public void assert_delete_customer_method_called_once() { getTester().startPage(CustomerListPage.class); getTester().assertRenderedPage(CustomerListPage.class); @SuppressWarnings("unchecked") DataTable dataTable = (DataTable) getTester().getComponentFromLastRenderedPage( "filterForm:table"); assertThat(dataTable.getItemCount(), equalTo(5L)); getTester().clickLink(getTableCell(5, 6) + "items:1:item:link"); getTester().assertComponent("defaultModal", YesNoModal.class); getTester().clickLink("defaultModal:content:yes", true); assertThat(dataTable.getItemCount(), equalTo(4L)); } private String getTableCell(int row, int cell) { return "filterForm:table:body:rows:" + row + ":cells:" + cell + ":cell:"; } } ================================================ FILE: wicket-spring-boot-starter-example/src/test/java/com/giffing/wicket/spring/boot/example/web/pages/customers/CustomerListPageTest.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.util.ArrayList; import java.util.List; import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable; import org.apache.wicket.markup.repeater.Item; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.test.context.bean.override.mockito.MockitoBean; import com.giffing.wicket.spring.boot.example.model.Customer; import com.giffing.wicket.spring.boot.example.repository.services.customer.CustomerRepositoryService; import com.giffing.wicket.spring.boot.example.repository.services.customer.filter.CustomerSort; import com.giffing.wicket.spring.boot.example.web.WicketBaseTest; import com.giffing.wicket.spring.boot.example.web.html.modal.YesNoModal; import com.giffing.wicket.spring.boot.example.web.pages.customers.create.CustomerCreatePage; public class CustomerListPageTest extends WicketBaseTest { private static final long CUSTOMERS_COUNT = 5; @MockitoBean private CustomerRepositoryService repository; @Override @BeforeEach public void setUp(){ super.setUp(); Mockito.when(repository.findAll(Mockito.anyLong(), Mockito.anyLong(), Mockito.any())).thenReturn(createCustomers(CUSTOMERS_COUNT)); Mockito.when(repository.count(Mockito.any())).thenReturn(CUSTOMERS_COUNT); for (long i=1; i <= CUSTOMERS_COUNT; i++) { Mockito.when(repository.findById(i)).thenReturn(createCustomer(i)); } } @Test @SuppressWarnings({ "rawtypes", "unchecked" }) public void assert_start_customer_list_page(){ getTester().startPage(CustomerListPage.class); getTester().assertRenderedPage(CustomerListPage.class); getTester().assertComponent("filterForm:table", DataTable.class); DataTable dataTable = (DataTable) getTester().getComponentFromLastRenderedPage("filterForm:table"); assertThat(dataTable.getItemCount(), equalTo(CUSTOMERS_COUNT)); //id, username, firstname, lastname, active, actions assertThat(dataTable.getColumns().size(), equalTo(6)); //get third row Item item3 = (Item) getTester().getComponentFromLastRenderedPage("filterForm:table:body:rows:3"); assertThat(item3.getModelObject().getId(), equalTo(3L)); assertThat(item3.getModelObject().getUsername(), equalTo("username3")); Item item5 = (Item) getTester().getComponentFromLastRenderedPage("filterForm:table:body:rows:5"); assertThat(item5.getModelObject().getId(), equalTo(5L)); assertThat(item5.getModelObject().getUsername(), equalTo("username5")); } @Test public void assert_click_customer_edit_page(){ getTester().startPage(CustomerListPage.class); getTester().assertRenderedPage(CustomerListPage.class); getTester().clickLink(getTableCell(5, 6) + "items:0:item:link"); getTester().assertRenderedPage(CustomerCreatePage.class); } @Test public void assert_click_customer_create_page(){ getTester().startPage(CustomerListPage.class); getTester().assertRenderedPage(CustomerListPage.class); getTester().clickLink("create"); getTester().assertRenderedPage(CustomerCreatePage.class); } @Test public void assert_delete_customer_method_called_once(){ getTester().startPage(CustomerListPage.class); getTester().assertRenderedPage(CustomerListPage.class); getTester().clickLink(getTableCell(5, 6) + "items:1:item:link"); getTester().assertComponent("defaultModal", YesNoModal.class); getTester().clickLink("defaultModal:content:yes", true); verify(repository, times(1)).delete(Mockito.anyLong()); verify(repository, times(1)).delete(5L); } private String getTableCell(int row, int cell){ return "filterForm:table:body:rows:" + row + ":cells:" + cell + ":cell:"; } public static List createCustomers(long count) { List customers = new ArrayList<>(); for(long i = 1; i <= count; i++){ customers.add(createCustomer(i)); } return customers; } public static Customer createCustomer(long i) { Customer customer = new Customer(); customer.setId(i); customer.setUsername("username" + i); customer.setFirstname("firstname" + i); customer.setLastname("lastname" + i); return customer; } } ================================================ FILE: wicket-spring-boot-starter-example/src/test/java/com/giffing/wicket/spring/boot/example/web/pages/customers/create/CustomerCreatePageIntTest.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers.create; import com.giffing.wicket.spring.boot.example.model.Customer; import com.giffing.wicket.spring.boot.example.repository.services.customer.CustomerRepositoryService; import com.giffing.wicket.spring.boot.example.repository.services.customer.filter.CustomerFilter; import com.giffing.wicket.spring.boot.example.web.WicketBaseIntTest; import com.giffing.wicket.spring.boot.example.web.pages.customers.CustomerListPage; import org.apache.wicket.util.tester.FormTester; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.Rollback; import org.springframework.transaction.annotation.Transactional; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; @Transactional @Rollback public class CustomerCreatePageIntTest extends WicketBaseIntTest { @Autowired private CustomerRepositoryService service; @Test public void assert_new_customer_saved() { long initialCustomerCount = service.count(new CustomerFilter()); getTester().startPage(CustomerCreatePage.class); getTester().assertRenderedPage(CustomerCreatePage.class); FormTester formTester = getTester().newFormTester("form"); String usernameOfNewCreatedUser = "newUser"; formTester.setValue(borderPath("username"), usernameOfNewCreatedUser); formTester.setValue(borderPath("firstname"), "newFirstname"); formTester.setValue(borderPath("lastname"), "newLastname"); formTester.submit("submit"); getTester().assertNoErrorMessage(); getTester().assertNoInfoMessage(); getTester().assertRenderedPage(CustomerListPage.class); long currentCustomerCount = service.count(new CustomerFilter()); assertThat(currentCustomerCount, equalTo(initialCustomerCount + 1)); Customer newUser = service.findByUsername(usernameOfNewCreatedUser); assertThat(newUser, notNullValue()); assertThat(newUser.getId(), notNullValue()); assertThat(newUser.getUsername(), equalTo(usernameOfNewCreatedUser)); } @Test public void assert_error_when_create_existing_customer() { getTester().startPage(CustomerCreatePage.class); getTester().assertRenderedPage(CustomerCreatePage.class); getTester().debugComponentTrees(); FormTester formTester = getTester().newFormTester("form"); //user is created via liquibase as initial data String usernameOfExistingUser = "frodo"; formTester.setValue(borderPath("username"), usernameOfExistingUser); formTester.setValue(borderPath("firstname"), "newFirstname"); formTester.setValue(borderPath("lastname"), "newLastname"); formTester.submit("submit"); getTester().assertErrorMessages("Username already exists!"); } protected String borderPath(String componentName) { return componentName + "Border:" + componentName + "Border_body:" + componentName; } } ================================================ FILE: wicket-spring-boot-starter-example/src/test/java/com/giffing/wicket/spring/boot/example/web/pages/customers/create/CustomerCreatePageTest.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers.create; import com.giffing.wicket.spring.boot.example.model.Customer; import com.giffing.wicket.spring.boot.example.repository.services.customer.CustomerRepositoryService; import com.giffing.wicket.spring.boot.example.web.WicketBaseTest; import com.giffing.wicket.spring.boot.example.web.pages.customers.CustomerListPage; import org.apache.wicket.util.tester.FormTester; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.springframework.test.context.bean.override.mockito.MockitoBean; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; import static org.mockito.Mockito.*; public class CustomerCreatePageTest extends WicketBaseTest { @MockitoBean private CustomerRepositoryService repository; @Override @BeforeEach public void setUp(){ super.setUp(); } @Test public void assert_customer_save_called(){ getTester().startPage(CustomerCreatePage.class); getTester().assertRenderedPage(CustomerCreatePage.class); FormTester formTester = getTester().newFormTester("form"); formTester.setValue(borderPath("username"), "myUsername"); formTester.setValue(borderPath("firstname"), "myFirstname"); formTester.setValue(borderPath("lastname"), "myLastname"); formTester.submit("submit"); getTester().assertNoErrorMessage(); getTester().assertNoInfoMessage(); getTester().assertRenderedPage(CustomerListPage.class); ArgumentCaptor customerArgument = ArgumentCaptor.forClass(Customer.class); //it should be checked, that the username does not already exists verify(repository, times(1)).usernameExists("myUsername"); verify(repository, times(1)).save(Mockito.any()); verify(repository, times(1)).save(customerArgument.capture()); Customer value = customerArgument.getValue(); assertThat(value.getId(), nullValue()); assertThat(value.getUsername(), equalTo("myUsername")); assertThat(value.getFirstname(), equalTo("myFirstname")); assertThat(value.getLastname(), equalTo("myLastname")); } @Test public void assert_customer_not_saved_if_user_already_exist() { getTester().startPage(CustomerCreatePage.class); getTester().assertRenderedPage(CustomerCreatePage.class); String newUsername = "newUsername"; when(repository.usernameExists(newUsername)).thenReturn(true); FormTester formTester = getTester().newFormTester("form"); formTester.setValue(borderPath("username"), newUsername); formTester.setValue(borderPath("firstname"), "myFirstname"); formTester.setValue(borderPath("lastname"), "myLastname"); formTester.submit("submit"); getTester().assertErrorMessages("Username already exists!"); getTester().assertRenderedPage(CustomerCreatePage.class); //it should be checked, that the username does already exists verify(repository, times(1)).usernameExists(newUsername); verify(repository, times(0)).save(Mockito.any()); //verifyNoMoreInteractions(repository); } } ================================================ FILE: wicket-spring-boot-starter-example/src/test/java/com/giffing/wicket/spring/boot/example/web/pages/customers/edit/CustomerEditPageTest.java ================================================ package com.giffing.wicket.spring.boot.example.web.pages.customers.edit; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.util.tester.FormTester; import static org.hamcrest.MatcherAssert.assertThat; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.springframework.test.context.bean.override.mockito.MockitoBean; import com.giffing.wicket.spring.boot.example.model.Customer; import com.giffing.wicket.spring.boot.example.repository.services.customer.CustomerRepositoryService; import com.giffing.wicket.spring.boot.example.web.WicketBaseTest; import com.giffing.wicket.spring.boot.example.web.pages.customers.CustomerListPage; import com.giffing.wicket.spring.boot.example.web.pages.customers.CustomerListPageTest; import com.giffing.wicket.spring.boot.example.web.pages.customers.model.UsernameTextField; public class CustomerEditPageTest extends WicketBaseTest { private static final Long CUSTOMERS_COUNT = 5L; @MockitoBean private CustomerRepositoryService repository; @Override @BeforeEach public void setUp(){ super.setUp(); Mockito.when(repository.findAll(Mockito.anyLong(), Mockito.anyLong(), Mockito.any())).thenReturn(CustomerListPageTest.createCustomers(CUSTOMERS_COUNT)); Mockito.when(repository.count(Mockito.any())).thenReturn(CUSTOMERS_COUNT); for (long i=1; i <= CUSTOMERS_COUNT; i++) { Mockito.when(repository.findById(i)).thenReturn(CustomerListPageTest.createCustomer(i)); } } @Test public void assert_customer_not_exists(){ PageParameters params = new PageParameters(); params.add(CustomerEditPage.CUSTOMER_ID_PARAM, "9548"); getTester().startPage(CustomerEditPage.class, params); getTester().assertRenderedPage(CustomerListPage.class); getTester().assertErrorMessages("Customer not found 9548"); //TODO how to get a resource from a page which can't be accessed cause of redirect. // Localizer localizer = getTester().getApplication().getResourceSettings() // .getLocalizer(); // // getTester().assertErrorMessages(MessageFormat.format(localizer.getString("customer.not-found", getTester().getLastRenderedPage()), "9548")); } @Test public void assert_customer_on_load_existing(){ PageParameters params = new PageParameters(); params.add(CustomerEditPage.CUSTOMER_ID_PARAM, "3"); getTester().startPage(CustomerEditPage.class, params); getTester().assertNoErrorMessage(); getTester().assertNoInfoMessage(); getTester().assertRenderedPage(CustomerEditPage.class); FormTester formTester = getTester().newFormTester("form"); String username = formTester.getTextComponentValue(borderPath("username")); String firstname = formTester.getTextComponentValue(borderPath("firstname")); String lastname = formTester.getTextComponentValue(borderPath("lastname")); assertThat(username, equalTo("username3")); assertThat(firstname, equalTo("firstname3")); assertThat(lastname, equalTo("lastname3")); getTester().debugComponentTrees(); String usernameFieldPath = "form:" + borderPath("username"); getTester().assertComponent(usernameFieldPath, UsernameTextField.class); getTester().isDisabled(usernameFieldPath); } @Test public void assert_customer_saved(){ PageParameters params = new PageParameters(); params.add(CustomerEditPage.CUSTOMER_ID_PARAM, "3"); getTester().startPage(CustomerEditPage.class, params); FormTester formTester = getTester().newFormTester("form"); formTester.setValue(borderPath("firstname"), "the-new-firstname"); formTester.submit("submit"); ArgumentCaptor customerArgument = ArgumentCaptor.forClass(Customer.class); //it should be checked, that the username does not already exists verify(repository, times(0)).usernameExists(Mockito.any()); verify(repository, times(1)).save(Mockito.any()); verify(repository, times(1)).save(customerArgument.capture()); Customer value = customerArgument.getValue(); assertThat(value.getId(), equalTo(3L)); assertThat(value.getUsername(), equalTo("username3")); assertThat(value.getFirstname(), equalTo("the-new-firstname")); assertThat(value.getLastname(), equalTo("lastname3")); } } ================================================ FILE: wicket-spring-boot-starter-example/src/test/java/com/giffing/wicket/spring/boot/example/web/security/WicketWebSecurityAdapterTestConfig.java ================================================ package com.giffing.wicket.spring.boot.example.web.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @Import(WicketWebSecurityAdapterConfig.class) public class WicketWebSecurityAdapterTestConfig { @Autowired private PasswordEncoder passwordEncoder; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("admin") .password(passwordEncoder.encode("admin")) .authorities("USER", "ADMIN"); } } ================================================ FILE: wicket-spring-boot-starter-example/src/test/java/test/com/giffing/wicket/spring/boot/example/web/WicketWebApplicationConfig.java ================================================ package test.com.giffing.wicket.spring.boot.example.web; import org.apache.wicket.Page; import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.resource.loader.ClassStringResourceLoader; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.transaction.annotation.EnableTransactionManagement; import com.giffing.wicket.spring.boot.example.WicketApplication; import com.giffing.wicket.spring.boot.example.web.SpringBootWebPackageIdentifier; import com.giffing.wicket.spring.boot.example.web.pages.customers.CustomerListPage; import com.giffing.wicket.spring.boot.example.web.pages.login.LoginPage; import com.giffing.wicket.spring.boot.starter.app.WicketBootSecuredWebApplication; import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.security.SecureWebSession; //TODO move to test @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, JpaRepositoriesAutoConfiguration.class, HibernateJpaAutoConfiguration.class, }) @ComponentScan(basePackageClasses = SpringBootWebPackageIdentifier.class) @EnableTransactionManagement public class WicketWebApplicationConfig extends WicketBootSecuredWebApplication { @Override protected void init() { super.init(); // Add WicketApplication.properties resource file getResourceSettings().getStringResourceLoaders().add(new ClassStringResourceLoader(WicketApplication.class)); } @Override protected Class getSignInPageClass() { return LoginPage.class; } @Override public Class getHomePage() { return CustomerListPage.class; } @Override protected Class getWebSessionClass() { return SecureWebSession.class; } } ================================================ FILE: wicket-spring-boot-starter-example/src/test/resources/application.yml ================================================ spring: main: allow-bean-definition-overriding: true jpa: hibernate: ddl-auto: none liquibase: change-log: classpath:db/changelog/db.changelog-master-test.xml wicket: stuff: serializer: fast2: enabled: false core: csrf: enabled: false ================================================ FILE: wicket-spring-boot-starter-example/src/test/resources/db/changelog/db.changelog-master-test.xml ================================================ frodo Frodo Beutlin true bilbo Bilbo Beutlin false adalgrim Adalgrim Tuk true bodo Bodo Stolzfuß false tuk Ferdibrand Tuk true