I want to add Paging functionality in Spring Boot + Spring Data JPA App, I have Repository :
#Repository
public interface PostRepository extends JpaRepository<Post, Long> {
List<Post> findAllByOrderByPostIdDesc(Pageable pageable);
}
and in Service class I have :
#Service
public class PostService {
#Autowired
private PostRepository postRepository;
public List<Post> findAll(Pageable pageable) {
return postRepository.findAllByOrderByPostIdDesc(pageable);
}
}
in the end Controller is
#Controller
public class HomeController {
#Autowired
private PostService postService;
#GetMapping(value = "/")
public String getHomePage(Model model, Pageable pageable){
model.addAttribute("postList", postService.findAll(pageable));
return "home";
}
}
the code above on http://localhost:8080 mapping I get all the records as response, but if I pass page and size params as http://localhost:8080/?page=0&size=1 I get paging working.
Now I want to make http://localhost:8080/?page=0&size=1 by default landing page address, and in HTML code how can I dynamically update values of params page and size which is :
<ul class="pagination justify-content-center mb-4">
<li class="page-item">
<a class="page-link" th:href="#{/(page=0,size=1)}">← Older</a>
</li>
<li class="page-item disabled">
<a class="page-link" th:href="#{/(page=0,size=1)}">Newer →</a>
</li>
</ul>
I have tried a many tricks but none worked for me!!
Update
I tried and added this class as suggested but #Nyle Hassan,
#Configuration
public class DefaultView extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers( ViewControllerRegistry registry ) {
registry.addViewController( "/" ).setViewName( "forward:/?page=0&size=10" );
registry.setOrder( Ordered.HIGHEST_PRECEDENCE );
super.addViewControllers( registry );
}
}
it resulted in :
2019-01-01 17:26:43.484 ERROR 3824 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception
java.lang.StackOverflowError: null
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:597) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:597) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:597) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:597) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:231) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:597) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
the code above on http://localhost:8080 mapping I get all the records as response
Are you sure about this? Which version of Spring Boot are you using? I checked latest version (2.1.1) and calling http://localhost:8080 without any query parameters will instantiate a Pageable object with page = 0, size = 20, no sorting. Just try to set a debug point to org.springframework.data.web.PageableHandlerMethodArgumentResolver#resolveArgument to learn, how Spring performs this binding.
This argument resolver also comes with an annotation, which allows you to change defaults, which are used, when query parameters are not present. You can use it like this:
#GetMapping(value = "/")
public String getHomePage(Model model, #PageableDefault(page=0, size = 1) Pageable pageable) {
...
}
Page 0 is annotation default, so this has the same effect:
#GetMapping(value = "/")
public String getHomePage(Model model, #PageableDefault(size = 1) Pageable pageable) {
...
}
in HTML code how can I dynamically update values of params page and size
Next time, please post two separate questions, but here it goes:
First, change List<Post> to Page<Post> in your repository and service. In your controller, bind the result to the model:
Page<Post> page = postService.findAll(pageable);
model.addAttribute("page", page); // page.getContent() contains the List<Page> now
And in the view, use the page to render paging links, e.g. like this:
<li class="page-item">
<a class="page-link" th:if="${page.hasPrevious()}" th:href="#{/(page=${page.previousPageable().getPageNumber()},size=${page.getSize()})}">← Older</a>
</li>
<li class="page-item disabled">
<a class="page-link" th:if="${page.hasNext()}" th:href="#{/(page=${page.nextPageable().getPageNumber()},size=${page.getSize()})}">Newer →</a>
</li>
Related
I am not sure how to test thymeleaf controller views in a spring boot webflux application.
The following gives 404 BAD REQUEST:
webTestClient.get()
.uri("/customer-view")
.accept(MediaType.TEXT_HTML)a
.exchange().isOk
I believe that webTestClient doesn't give any server-side views testing support, only mockMvc. But I have only the webflux dependency "org.springframework.boot:spring-boot-starter-webflux", and mockMvc is not present in webflux as it's a blocking client of course.
I am wondering if there is a way to test a thymeleaf view in webflux. For example a method like the following:
#Controller
class CustomerController {
#GetMapping("/customer-view")
suspend fun getCustomerView(model: Model
): String {
val customer = customerService.getCustomer()
model["customer"] = customer
return "customer-view"
}
}
Thanks
My understanding is thymeleaf requires resources to be present, so while you are testing you need to load resources as well.
#Controller
class CustomerController {
#GetMapping("/customer-view")
suspend fun getCustomerView(model:Model):String{
model["something"] = "this is an example"
return "customer-view"
}
}
// make sure that your html template here -> /resource/templates/cusotmer-view.html . Remember to add xmlns attribute to your template, too.
<!DOCTYPE html>
<html xmlns:th="https://www.thymelaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${something}"/>
</body>
</html>
// Now you can test this
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.test.web.reactive.server.WebTestClient
// Again you need resources so add this annotation #WebFluxTest to load them
#WebFluxTest
class CustomerControllerTest{
#Autowired
private lateinit var webTestClient: WebTestClient
#Test
fun testReturns200Ok() {
runBlocking {
webTestClient
.get()
.uri("/customer-view")
.exchange()
.expectStatus().isOk
}
}
}
// I hope this helps
Modifying umit's answer - removing blocking
#RunWith(SpringRunner::class)
#WebFluxTest(CustomerController::class)
class CustomerControllerTest {
#Autowired
private lateinit var webTestClient: WebTestClient
#Test
fun `should return customer view`() {
val customer = Customer("John", "Doe")
`when`(customerService.getCustomer()).thenReturn(customer)
webTestClient.get().uri("/customer-view")
.exchange()
.expectStatus().isOk
.expectBody<String>()
.consumeWith {
//assertions
}
}
}
I'm trying to upload .pdf file with jQuery AJAX to Spring MVC 5 with Spring Security 5 back-end running on Tomcat and faced multiple issues depending on Spring configuration
NOTE:
File upload should be available without authentication
Front-end
Markup:
<div id="upload-modal" class="modal">
<div class="modal-content">
<h4>Upload</h4>
<form action="#" enctype="multipart/form-data">
<div class="file-field input-field">
<div class="btn">
<span>View...</span>
<input type="file" name="file" accept="application/pdf">
</div>
<div class="file-path-wrapper">
<label>
<input class="file-path validate" type="text">
</label>
</div>
</div>
</form>
</div>
<div class="modal-footer">
Cancel
Upload
</div>
</div>
csrf header for all the requests:
$(document).ready(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function (e, xhr, options) {
xhr.setRequestHeader(header, token);
});
});
Uploading with jQuery AJAX:
$("#upload-bttn").click(function () {
var $uploadModal = $("#upload-modal");
const fileName = $uploadModal.find(".file-path").val();
const extension = fileName.substr(fileName.lastIndexOf(".") + 1);
if (extension === "pdf") {
$.ajax({
url: "/upload",
type: "POST",
data: new FormData($uploadModal.find("form").get(0)),
processData: false,
contentType: false,
success: function () {
console.log("success")
},
error: function () {
console.log("error")
}
});
} else {
M.toast({html: 'Selected file is not .pdf'});
}
});
Back-end
General configuration looks like below. It is modified depending on the cases
Security Initialization:
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityContext.class);
}
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
Application initialization:
public class ApplicationInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) {
servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));
servletContext.getSessionCookieConfig().setHttpOnly(true);
servletContext.getSessionCookieConfig().setSecure(true);
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(WebAppContext.class);
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
Case 1 - CommonsMultipartResolver bean definition
CommonsMultipartResolver bean definition:
#Bean
public CommonsMultipartResolver multipartResolver(
#Value("${max.upload.size}") Integer maxNumber,
#Value("${max.size}") Integer maxSize) {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(1024 * maxSize * maxNumber);
resolver.setMaxUploadSizePerFile(maxSize);
resolver.setMaxInMemorySize(maxSize);
resolver.setDefaultEncoding("UTF-8");
try {
resolver.setUploadTempDir(new FileSystemResource(System.getProperty("java.io.tmpdir")));
} catch (IOException e) {
e.printStackTrace();
}
return resolver;
}
I remember there was strange Spring behavior when MultipartResolver bean should be named "multipartResolver" explicitly. I tried both #Bean and #Bean("multipartResolver") with configuration above and had same result (despite bean above is named "multipartResolver" as per method name)
Result:
Error 500 - Unable to process parts as no multi-part configuration has been provided
Case 2 - MultipartConfigElement in Servlet registry
removed CommonsMultipartResolver bean
added StandardServletMultipartResolver bean
added MultipartConfigElement to ApplicationInitializer
StandardServletMultipartResolver bean definition:
#Bean
public StandardServletMultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
Updated ApplicationInitializer:
#Override
public void onStartup(ServletContext servletContext) {
...
servlet.setMultipartConfig(new MultipartConfigElement(
System.getProperty("java.io.tmpdir")
));
}
As per Spring documentation:
Ensure that the MultipartFilter is specified before the Spring Security filter. Specifying the MultipartFilter after the Spring Security filter means that there is no authorization for invoking the MultipartFilter which means anyone can place temporary files on your server. However, only authorized users will be able to submit a File that is processed by your application
As I need to allow not authenticated users to upload the files I tried both before and after in SecurityInitializer as below with the same result
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
or
#Override
protected void afterSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
Result:
Error 403
Questions
What do I miss in the configuration?
Thoughts
CommonsMultipartResolver would be preferable as allows to drive it with Spring properties
Something wrong with Spring Security context setup
There is allowCasualMultipartParsing="true" option (did not test) which I wouldn't like to stick to as its Tomcat specific
Updates
With disabled Spring Security everything works properly
http.authorizeRequests().antMatchers("/**").permitAll(); remains as the only security context configuration so don't think its Security context configuration issue
Set multipart resolver bean name explicitly in MultipartFilter in
beforeSpringSecurityFilterChain(ServletContext servletContext) and still no luck
Adding of _csrf token to the request header did not work for both cases
Realized that I miss additional WebAppContext class in SecurityInitializer constructor. Now error 500 disappeared but 403 appeared for case 1. Logging says that I have invalid csrf token despite I added it to the header like above
Tried to submit the form with csrf token including hidden input <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> yet the result is the same - error 403 with invalid token statement
After two days of struggling:
Constructor should contain both security and application context configuration classes
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityContext.class, WebAppContext.class);
}
}
Application context (WebAppContext) should contain MultipartResolver bean definition
#Bean
public CommonsMultipartResolver multipartResolver(
#Value("${max.upload.size}") Integer maxNumber,
#Value("${max.size}") Integer maxSize) {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(1024 * maxSize * maxNumber);
resolver.setMaxUploadSizePerFile(maxSize);
resolver.setMaxInMemorySize(maxSize);
resolver.setDefaultEncoding("UTF-8");
try {
resolver.setUploadTempDir(new FileSystemResource(System.getProperty("java.io.tmpdir")));
} catch (IOException e) {
e.printStackTrace();
}
return resolver;
}
In my case after application initialization csrf token inside Spring CsrfTokenRepository was empty for some reason so when Spring been comparing token from client request header with null in CsrfFilter Spring was returning error 403. I configured csrf in security context in the following way:
#Override
protected void configure(HttpSecurity http) throws Exception {
...
http.csrf().csrfTokenRepository(new CookieCsrfTokenRepository());
...
}
Now csrf token is passed in cookies with first server response to the browser and the repository generates and caches a token to compare against the one coming from the client so comparison passes successfully
Here CookieCsrfTokenRepository may also be declared as CookieCsrfTokenRepository.withHttpOnlyFalse() if you would like to grab the token from cookie and set it into csrf header, but I have chosen to go with meta tags approach above
In my current project I have a number of Thymeleaf fragments stored under the src tree src/main/resources/templates/fragments. In my test suite i have a number of test templates that will pull in individual fragments so that I may process them and compare the output with some test output files. These are located in src/test/resources/test-templates/input and look like the following:
title-and-subtitle-test-input.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">>
<body>
<table
th:replace="~{fragments/titleAndSubtitle :: titleAndSubtitleTable(titleText = 'Test Title', subtitleText = 'Test Subtitle')}"></table>
</body>
</html>
and the titleAndSubtitle fragment looks like this:
titleAndSubtitle.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<body>
<table class="fragment" th:fragment="titleAndSubtitleTable(titleText, subtitleText)">
<tr>
<td class="title">[[${titleText}]]</td>
</tr>
<tr>
<td class="subtitle">[[${subtitleText}]]</td>
</tr>
</table>
</body>
</html>
and my test thymeleaf config class looks as follows:
ThymeleafTestConfiguration.java
#Configuration
public class ThymeleafTestConfiguration extends WebMvcConfigurerAdapter implements ApplicationContextAware {
private static final String TEST_TEMPLATE_DIRECTORY = "classpath:/test-templates/input/";
private static final String TEMPLATE_SUFFIX = ".html";
#Autowired
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* THYMELEAF: View Resolver - implementation of Spring's ViewResolver
* interface.
*
* #return ViewResolver
*/
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
}
/**
* THYMELEAF: Template Engine (Spring4-specific version).
*
* #return SpringTemplateEngine
*/
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setEnableSpringELCompiler(true);
templateEngine.addTemplateResolver(templateResolver());
return templateEngine;
}
/**
* THYMELEAF: Template Resolver for test templates.
*
* #return TemplateResolver
*/
private ITemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(applicationContext);
templateResolver.setPrefix(TEST_TEMPLATE_DIRECTORY);
templateResolver.setSuffix(TEMPLATE_SUFFIX);
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setOrder(1);
return templateResolver;
}
}
I haven't been able to correctly configure a Thymeleaf template resolver and engine to be able to locate the test input fiel and still resolve the fragment expression, i.e. pull in the fragment from the src tree. Instead I decided to copy the contents of my fragment directory in the src tree to a temporary folder - src/test/resources/test-templates/input/fragments, with the idea being that the fragments are placed in the test resource temporarily so the thymeleaf template resolver could successfully locate and retrieve them, pull them into the test template for processing, and once all tests have ran the temp directory would be deleted. I am able to successfully copy the directory and its contents to the test tree but I'm getting errors like :
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [test-templates/input//fragments/titleAndSubtitle.html]" - line 5, col 9)
//etc
Caused by: java.io.FileNotFoundException: class path resource [test-templates/input/fragments/titleAndSubtitle.html] cannot be opened because it does not exist
This is similar to the errors I was seeing when I was still messing around with template resolvers. The test looks as follows:
FragmentTest.java
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = ThymeleafTestConfiguration.class)
public class ClaimSnapshotFragmentTest {
private static File sourceFragmentDirectory = new File("src/main/resources/templates/fragments");
private static File tempFragmentDirectory = new File("src/test/resources/test-templates/input/fragments");
#BeforeClass
public static void setupClass() throws Exception {
if(tempFragmentDirectory.exists()) {
deleteDirectory(tempFragmentDirectory);
}
copyDirectory(sourceFragmentDirectory, tempFragmentDirectory);
}
#AfterClass
public static void teardownClass() throws Exception {
if(tempFragmentDirectory.exists()) {
deleteDirectory(tempFragmentDirectory);
}
}
#Autowired
private SpringTemplateEngine templateEngine;
#Test
public void test_titleAndSubtitle_fragmentParsedWithData() throws Exception {
final String expected = FileUtils.getFileFromFolderAsString("title-and-subtitle-test-expected-output.html",
FOLDER_TEST_TEMPLATES_OUTPUT);
final String actual = templateEngine.process("title-and-subtitle-test-input", new Context());
final Diff diff = DiffBuilder
.compare(expected)
.withTest(actual)
.checkForSimilar()
.normalizeWhitespace()
.build();
assertThat(diff.hasDifferences()).isFalse();
}
Am I missing something with the Thymeleaf configuration? I have checked via code and manually that the fragments directory is created, contains the fragment template in question, and is successfully removed after the tests have ran but I'm still getting an error about files not existing. Note: I am using Thymeleaf version 3.0.9.RELEASE with Thymeleaf layout dialect version 2.3.0. The application runs Thymeleaf in "offline mode" and is used primarily to create email bodies by combining data received by MQ and processing it in a template. The copyDirectory and deleteDirectory methods are from Apache Commons FileUtils class
need your help here please.
I have a login page. After I enter username/password, I want to see Dashboard page. But I am getting 404 page not found. Can anyone please tell what is going on here.
When I hit http://localhost:8080 -> It goes to http://localhost:8080/login - Which is expected.
After I enter username/password, it goes to http://localhost:8080 - Expected: to go to Dashboard page i.e. http://localhost:8080/dashboard
#Component
public class SimpleAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
...
redirectStrategy.sendRedirect(request, response, "dashboard");
}
DashboardController.java
#Controller
#RequestMapping("/dashboard")
public class DashboardController {
#RequestMapping("/")
String init(){
System.out.println("Dashboard - init()");
return "dashboard_init";
}
}
app.component.html
Hello... {{name}}
Hello... {{title}}
<h1>
Welcome {{title}}!
</h1>
<p>Id: <span>{{greeting.id}}</span></p>
<p>Message: <span>{{greeting.content}}!</span></p>
app-routing.module.ts
import {DashboardComponent} from "./dashboard/dashboard.component";
const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full' },
// { path: 'dashboard', redirectTo: '/dashboard', pathMatch: 'full' },
{
path: 'dashboard_init',
component: DashboardComponent,
data: { title: 'Dashboard' }
}
];
dashboard.component.ts
#Component({
selector: 'dashboard-component',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css'],
})
export class DashboardComponent implements OnInit {
private currentAssociate: Associate;
constructor(private http: Http,
private router: Router) {
}
ngOnInit(): void {
// initialize services and data
this.http
.get('/dashboard')
.toPromise()
.then(response => {
let data = response.json();
if (data.currentAssociate) this.currentAssociate = data.currentAssociate as Associate;
})
.catch(error => {
// this.alertService.error(error);
});
}
}
dashboard.component.html
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="utf-8" />
<title>Dashboard</title>
</head>
<div>
<B>Dashboard...</B>
</div>
</html>
Error: (When the url is http://localhost:8080/dashboard/)
Dashboard - init()
[2m2018-03-26 10:07:20.421[0;39m [31mERROR[0;39m [35m13184[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36morg.thymeleaf.TemplateEngine [0;39m [2m:[0;39m [THYMELEAF][http-nio-8080-exec-2] Exception processing template "dashboard_init": Error resolving template "dashboard_init", template might not exist or might not be accessible by any of the configured Template Resolvers
[2m2018-03-26 10:07:20.422[0;39m [31mERROR[0;39m [35m13184[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.a.c.c.C.[.[.[/].[dispatcherServlet] [0;39m [2m:[0;39m Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "dashboard_init", template might not exist or might not be accessible by any of the configured Template Resolvers] with root cause
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "dashboard_init", template might not exist or might not be accessible by any of the configured Template Resolvers
at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:246)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1104)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011)
something wrong overall..
in Controller replace top /dashboard with /
#Controller
#RequestMapping("/")
public class DashboardController {
#RequestMapping("/")
String init(){
System.out.println("Dashboard - init()");
return "dashboard_init";
}
}
Also as fas as I remember return "dashboard_init" is expecting dashboard_init.html template to be returned
Probably you want redirect or something to /dashboard_init, do like
#RequestMapping(value = "/", method = RequestMethod.GET)
public void index(final HttpServletResponse response) {
response.setStatus(HttpStatus.OK.value());
response.sendRedirect( "/wherever-you-want");
}
This series of tutorials will help you to integrate Spring Security with Angular, using different approaches. Starting with Basic Auth
Spring Security and Angular
I am migrating an application from older version of wicket to wicket 8. Some pages are working ok (simple pages such as login). These pages does not use the extension mechanism.
When I access to a page that has the mechanism it doesn't work. I don't know if this is a bug but I tried everything and couldn't find a solution.
I am using wicket 8.0.0-M5 (i kwnow that is not a stable release). Anybody had the same problem?
Here is my code:
WerklijstPageXXX.html
<html xmlns:wicket>
<body>
<wicket:extend>
<div wicket:id="panelXXX"></div>
</wicket:extend>
</body>
WerklijstPanelXXX.html
<wicket:head xmlns:wicket>
</wicket:head>
<wicket:panel xmlns:wicket>
<div wicket:id="filter">
<div wicket:id="label"></div>
</div>
</wicket:panel>
WerklijstPageXXX.java
public class WerklijstPageXXX extends BasePage {
private WerklijstPanelXXX werklijstPanel;
public WerklijstPageXXX() {
werklijstPanel = new WerklijstPanelXXX("panelXXX"){};
add(werklijstPanel);
}
#Override
public Panel getContextPanel(String id)
{
return werklijstPanel.getWerklijstFilterPanel();
}
WerklijstPanelXXX.java
public abstract class WerklijstPanelXXX extends Panel {
private static final Logger LOG = LoggerFactory
.getLogger(WerklijstPanelXXX.class);
private Panel filterPanel;
public WerklijstPanelXXX(String id) {
super(id);
WebMarkupContainer upperPanel = new WebMarkupContainer("filter");
filterPanel = new Panel("filterPanel") {
};
add(upperPanel);
upperPanel.add(new Label("label"));
}
public Panel getWerklijstFilterPanel() {
return filterPanel;
}
EDIT:
BasePage.html (part of it)
<div id="body" class="body">
<div id="aside" class="aside">
<div wicket:id="contextPanel"></div>
</div>
<div id="content" class="content">
<div class="padding">
<wicket:child></wicket:child>
</div>
</div>
</div>
BasePage.java
#Override
protected void onInitialize() {
super.onInitialize();
add(getContextPanel("contextPanel"));
addTabMenu();
addMenu();
addPatientBar();
}
Error:
Root cause:
org.apache.wicket.WicketRuntimeException: The component(s) below failed to render. Possible reasons could be that:
1) you have added a component in code but forgot to reference it in the markup (thus the component will never be rendered),
2) if your components were added in a parent container then make sure the markup for the child container includes them in .
[WerklijstPanelXXX [Component id = panelXXX, page = nl.philips.raris.web.werklijst.WerklijstPageXXX, path = panelXXX, type = nl.philips.raris.web.werklijst.WerklijstPanelXXX, isVisible = true, isVersioned = true], children = [WebMarkupContainer [Component id = filter]]]
[WebMarkupContainer [Component id = filter, page = nl.philips.raris.web.werklijst.WerklijstPageXXX, path = panelXXX:filter, type = org.apache.wicket.markup.html.WebMarkupContainer, isVisible = true, isVersioned = true], children = [Component id = label]]
[Component id = label, page = nl.philips.raris.web.werklijst.WerklijstPageXXX, path = panelXXX:filter:label, type = org.apache.wicket.markup.html.basic.Label, isVisible = true, isVersioned = true]
[Panel [Component id = filterPanel, page = nl.philips.raris.web.werklijst.WerklijstPageXXX, path = filterPanel, type = org.apache.wicket.markup.html.panel.Panel, isVisible = true, isVersioned = true]]
at org.apache.wicket.Page.checkRendering(Page.java:662)
at org.apache.wicket.Page.onAfterRender(Page.java:805)
at org.apache.wicket.markup.html.WebPage.onAfterRender(WebPage.java:209)
at org.apache.wicket.Component.afterRender(Component.java:919)
at org.apache.wicket.Component.render(Component.java:2335)
at org.apache.wicket.Page.renderPage(Page.java:987)
at org.apache.wicket.request.handler.render.WebPageRenderer.renderPage(WebPageRenderer.java:124)
at org.apache.wicket.request.handler.render.WebPageRenderer.respond(WebPageRenderer.java:236)
at org.apache.wicket.core.request.handler.RenderPageRequestHandler.respond(RenderPageRequestHandler.java:175)
at org.apache.wicket.request.cycle.RequestCycle$HandlerExecutor.respond(RequestCycle.java:912)
at org.apache.wicket.request.RequestHandlerExecutor.execute(RequestHandlerExecutor.java:65)
at org.apache.wicket.request.cycle.RequestCycle.execute(RequestCycle.java:283)
at org.apache.wicket.request.cycle.RequestCycle.processRequest(RequestCycle.java:253)
at org.apache.wicket.request.cycle.RequestCycle.processRequestAndDetach(RequestCycle.java:221)
at org.apache.wicket.protocol.http.WicketFilter.processRequestCycle(WicketFilter.java:262)
at org.apache.wicket.protocol.http.WicketFilter.processRequest(WicketFilter.java:204)
at org.apache.wicket.protocol.http.WicketServlet.doGet(WicketServlet.java:137)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:808)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1669)
at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)
at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365)
at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387)
at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)
at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
at org.springframework.orm.hibernate4.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:151)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
The Java and HTML component trees should match.
Your HTML states: <div wicket:id="*contextPanel*"></div>, but your Java code returns a Panel with id filterPanel instead.
public WerklijstPageXXX() {
werklijstPanel = new WerklijstPanelXXX("panelXXX"){};
add(werklijstPanel);
}
#Override
public Panel getContextPanel(String id) // HERE id is ignored
{
return werklijstPanel.getWerklijstFilterPanel(); // This returns a panel with id `filterPanel`
}