I am new to Thymeleaf.I am creating a spring-boot app using thymeleaf in view part.
Please find below my pom.xml file[only the dependencies part]:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
I have created an application.properties file in following location:src/main/resources
server.port = 8086
spring.mvc.static-path-pattern=/resources/**
spring.thymeleaf.prefix=/WEB-INF/views/
spring.thymeleaf.suffix=.html
Following is my Boot Application Main class:
#SpringBootApplication
#ComponentScan(basePackages="<common package structure>")
public class AdvMain extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(AdvMain.class);
}
public static void main(String[] args) {
SpringApplication.run(AdvMain.class, args);
}
}
I have a controller class as follows:
#Controller
public class HomeController {
#RequestMapping(path = "/info", method = { RequestMethod.GET, RequestMethod.POST })
public String info(Model model) {
model.addAttribute("message", "Thymeleaf");
return "info";
}
#RequestMapping(path = "/home", method = RequestMethod.GET)
public String index() {
return "index";
}
}
When I invoke the following url:
http://localhost:8086/AddViewer/home
I am getting page not found error.
Please find below my tomcat console log:
Mapped "{[/home],methods=[GET]}" onto public java.lang.String <package_Structure>.HomeController.index()
Mapped "{[/info],methods=[GET || POST]}" onto public java.lang.String <package_Structure>.HomeController.info(org.springframework.ui.Model)
Mapped URL path [/resources/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
Tomcat started on port(s): 8086 (http)
FrameworkServlet 'dispatcherServlet': initialization completed in 29 ms
No mapping found for HTTP request with URI [/AddViewer/home] in DispatcherServlet with name 'dispatcherServlet'
Can anyone provide any suitable solution to this???
This is not a problem of thymeleaf.
In your console message, you have two mappings, /home and /info.
Mapped "{[/home],methods=[GET]}" onto public java.lang.String <package_Structure>.HomeController.index()
Mapped "{[/info],methods=[GET || POST]}" onto public java.lang.String <package_Structure>.HomeController.info(org.springframework.ui.Model)
But you made a request /AddViewer/home. So, it does not matches with request mappings.
Request like this.
http://localhost:8086/home
if you using thymeleaf put your view files in src/main/resources/template and no need to add these two line -
spring.thymeleaf.prefix=/WEB-INF/views/
spring.thymeleaf.suffix=.html
Related
I am creating a simple Spring WebMVC app with thymeleaf and now I want to add a service which provides a connection to a couchbase server.
I tried to create the couchbase service on basis of the following tutorial Couchbase with Spring-Boot and Spring Data
I have the following project structure:
src/main/java
com.project
config
MvcWebApplicationInitializer
MvcWebConfig
controller
IndexController
model
Area
Building
BuildingRepository
BuildingService
BuildingServiceImpl
When trying to autowire the service I get the exception that there is no bean named couchbaseRepositoryOperationsMapping which happens because my repository class extends CouchbasePagingAndSortingRepository which throws the exception.
I get the following exception:
[main] INFO org.springframework.web.servlet.DispatcherServlet -
Initializing Servlet 'dispatcher' [main] INFO
org.springframework.data.repository.config.RepositoryConfigurationDelegate
- Multiple Spring Data modules found, entering strict repository configuration mode! [main] INFO
org.springframework.data.repository.config.RepositoryConfigurationDelegate
- Bootstrapping Spring Data repositories in DEFAULT mode. [main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate
- Finished Spring Data repository scanning in 64ms. Found 1 repository interfaces. [main] WARN
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
- Exception encountered during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'buildingServiceImpl': Unsatisfied
dependency expressed through field 'buildingRepository'; nested
exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'buildingRepository':
'buildingRepository' depends on missing bean
'couchbaseRepositoryOperationsMapping'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'couchbaseRepositoryOperationsMapping' available [main]
ERROR org.springframework.web.servlet.DispatcherServlet - Context
initialization failed
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'buildingServiceImpl': Unsatisfied
dependency expressed through field 'buildingRepository'; nested
exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'buildingRepository':
'buildingRepository' depends on missing bean
'couchbaseRepositoryOperationsMapping'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'couchbaseRepositoryOperationsMapping' available
As far as I understand it is not working because there is no bean
named couchbaseRepositoryOperationsMapping.
My MvcWebConfig:
#Configuration
#EnableWebMvc
#ComponentScan("com.xplorg.controller")
#EnableCouchbaseRepositories(basePackages = {"com.xplorg.model"})
public class MvcWebConfig implements WebMvcConfigurer {
#Autowired
private ApplicationContext applicationContext;
#Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("/WEB-INF/views/");
templateResolver.setSuffix(".html");
return templateResolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.setEnableSpringELCompiler(true);
return templateEngine;
}
public void configureViewResolvers(ViewResolverRegistry registry) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
registry.viewResolver(viewResolver);
}
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
My pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.project</groupId>
<artifactId>xplorg</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>Project</name>
<properties>
<failOnMissingWebXml>false</failOnMissingWebXml>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7-1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-couchbase</artifactId>
<version>3.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.4.18.v20190429</version>
</plugin>
</plugins>
</build>
</project>
****Update****
Adding a CouchbaseConfig class fixed the problem with the couchbaseRepositoryOperationsMapping but I get now another error, that there is now named bean buildingRepository. All classes are based on the tutorial Couchbase with Spring-Boot and Spring Data. Do I miss an annotations and therefore spring can not autowire the class?
My repository class:
#N1qlPrimaryIndexed
#ViewIndexed(designDoc = "building")
public interface BuildingRepository extends CouchbasePagingAndSortingRepository<Building, String> {
List<Building> findByCompanyId(String companyId);
Page<Building> findByCompanyIdAndNameLikeOrderByName(String companyId, String name, Pageable pageable);
#Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and companyId = $1 and $2 within #{#n1ql.bucket}")
Building findByCompanyAndAreaId(String companyId, String areaId);
#Query("#{#n1ql.selectEntity} where #{#n1ql.filter} AND ANY phone IN phoneNumbers SATISFIES phone = $1 END")
List<Building> findByPhoneNumber(String telephoneNumber);
#Query("SELECT COUNT(*) AS count FROM #{#n1ql.bucket} WHERE #{#n1ql.filter} and companyId = $1")
Long countBuildings(String companyId);
}
My service class:
#Service
public class BuildingServiceImpl implements BuildingService {
#Autowired
private BuildingRepository buildingRepository;
#Override
public List<Building> findByCompanyId(String companyId) {
return buildingRepository.findByCompanyId(companyId);
}
public List<Building> findByCompanyIdAndNameLike(String companyId, String name, int page) {
return buildingRepository.findByCompanyIdAndNameLikeOrderByName(companyId, name, new PageRequest(page, 20))
.getContent();
}
#Override
public Building findByCompanyAndAreaId(String companyId, String areaId) {
return buildingRepository.findByCompanyAndAreaId(companyId, areaId);
}
#Override
public List<Building> findByPhoneNumber(String telephoneNumber) {
return buildingRepository.findByPhoneNumber(telephoneNumber);
}
#Override
public Building findById(String buildingId) {
return buildingRepository.findById(buildingId).get();
}
#Override
public Building save(Building building) {
return buildingRepository.save(building);
}
#Override
public Long countBuildings(String companyId) {
return buildingRepository.countBuildings(companyId);
}
}
You missed the config class couchebase server
Create a dedicate config class extending the AbstractCouchbaseConfiguration for your couhcebase as below ( server # , user , pwd )
#Configuration
#EnableCouchbaseRepositories(basePackages = {"com.xplorg.model"})
public class CouchbaseConfig extends AbstractCouchbaseConfiguration {
#Override
protected List<String> getBootstrapHosts() {
return Arrays.asList("your server ip (like localhost in local)");
}
#Override
protected String getBucketName() {
return "username";
}
#Override
protected String getBucketPassword() {
return "passsword";
}
}
also change #ComponentScan("com.xplorg.controller") to #ComponentScan("com.xplorg") in your project config so your repositories interface and service should be scanned
This is a follow up to the solution which was provided to me on this previous post:
How to Properly Close Raw RestClient When Using Elastic Search 5.5.0 for Optimal Performance?
This same exact error message came back!
2017-09-29 18:50:22.497 ERROR 11099 --- [8080-Acceptor-0] org.apache.tomcat.util.net.NioEndpoint : Socket accept failed
java.io.IOException: Too many open files
at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method) ~[na:1.8.0_141]
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422) ~[na:1.8.0_141]
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250) ~[na:1.8.0_141]
at org.apache.tomcat.util.net.NioEndpoint$Acceptor.run(NioEndpoint.java:453) ~[tomcat-embed-core-8.5.15.jar!/:8.5.15]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_141]
2017-09-29 18:50:23.885 INFO 11099 --- [Thread-3] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#5387f9e0: startup date [Wed Sep 27 03:14:35 UTC 2017]; root of context hierarchy
2017-09-29 18:50:23.890 INFO 11099 --- [Thread-3] o.s.c.support.DefaultLifecycleProcessor : Stopping beans in phase 2147483647
2017-09-29 18:50:23.891 WARN 11099 --- [Thread-3] o.s.c.support.DefaultLifecycleProcessor : Failed to stop bean 'documentationPluginsBootstrapper'
... 7 common frames omitted
2017-09-29 18:50:53.891 WARN 11099 --- [Thread-3] o.s.c.support.DefaultLifecycleProcessor : Failed to shut down 1 bean with phase value 2147483647 within timeout of 30000: [documentationPluginsBootstrapper]
2017-09-29 18:50:53.891 INFO 11099 --- [Thread-3] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
2017-09-29 18:50:53.894 INFO 11099 --- [Thread-3] com.app.controller.SearchController : Closing the ES REST client
I tried using the solution from the previous post.
ElasticsearchConfig:
#Configuration
public class ElasticsearchConfig {
#Value("${elasticsearch.host}")
private String host;
#Value("${elasticsearch.port}")
private int port;
#Bean
public RestClient restClient() {
return RestClient.builder(new HttpHost(host, port))
.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
#Override
public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
return requestConfigBuilder.setConnectTimeout(5000).setSocketTimeout(60000);
}
}).setMaxRetryTimeoutMillis(60000).build();
}
SearchController:
#RestController
#RequestMapping("/api/v1")
public class SearchController {
#Autowired
private RestClient restClient;
#RequestMapping(value = "/search", method = RequestMethod.GET, produces="application/json" )
public ResponseEntity<Object> getSearchQueryResults(#RequestParam(value = "criteria") String criteria) throws IOException {
// Setup HTTP Headers
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
// Setup query and send and return ResponseEntity...
Response response = this.restClient.performRequest(...);
}
#PreDestroy
public void cleanup() {
try {
logger.info("Closing the ES REST client");
this.restClient.close();
}
catch (IOException ioe) {
logger.error("Problem occurred when closing the ES REST client", ioe);
}
}
}
pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
</parent>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Elasticsearch -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>5.5.0</version>
</dependency>
<!-- Apache Commons -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<!-- Log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
This makes me think that the RestClient was never explicitly closing the connection, in the first place...
And this is surprising since my Elasticsearch Spring Boot based Microservice is load balanced on two different AWS EC-2 Servers.
That exception occurreded like 2000 times reported by the log file and only in the end did the preDestroy() close the client. See the INFO from the #PreDestroy() cleanup method being logged at the end of the StackTrace.
Do I need to explicitly put a finally clause inside the SearchController and close the RestClient connection explicitly?
It's really critical that this IOException doesn't happen again because this Search Microservice is dependent on a lot of different mobile clients (iOS & Android).
Need this to be fault tolerant and scalable... Or, at the very least, not to break.
The only reason this is in the bottom of the log file:
2017-09-29 18:50:53.894 INFO 11099 --- [Thread-3] com.app.controller.SearchController : Closing the ES REST client
Is because I did this:
kill -3 jvm_pid
Should I keep the #PreDestory cleanup() method but change the contents of my SearchController.getSearchResults() method to reflect something like this:
#RequestMapping(value = "/search", method = RequestMethod.GET, produces="application/json" )
public ResponseEntity<Object> getSearchQueryResults(#RequestParam(value = "criteria") String criteria) throws IOException {
// Setup HTTP Headers
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
// Setup query and send and return ResponseEntity...
Response response = null;
try {
// Submit Query and Obtain Response
response = this.restClient.performRequest("POST", endPoint, Collections.singletonMap("pretty", "true"), entity);
}
catch(IOException ioe) {
logger.error("Exception when performing POST request " + ioe);
}
finally {
this.restClient.close();
}
// return response as EsResponse();
}
This way the RestClient connection is always closing...
Would appreciate if someone can help me with this.
From my point of view, there are few thing that you are doing wrong but I will go directly to the solution.
I'm not going to write the full solution (in fact, I didn't execute or test anything), but the important is to understand it. Also, it is better if you move all related with data access to another layer. Anyway, this is only an example so the design is not perfect.
Step 1: Import the right library.
Practically the same as your example.
I updated the example to use the last client library recommended in version 5.6.2
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.acervera</groupId>
<artifactId>elastic-example</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>elastic-example</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<es.version>5.6.2</es.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
</parent>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Elasticsearch -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${es.version}</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${es.version}</version>
</dependency>
<!-- Log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
Step 2: Create and shutdown the client in the bean factory.
In the bean factory, create and destroy it. You can reuse the same client.
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
#Configuration
public class ElasticsearchConfig {
// Here all init stuff with #Value(....)
RestClient lowLevelRestClient;
RestHighLevelClient client;
#PostConstruct
public void init() {
lowLevelRestClient = RestClient.builder(new HttpHost("host", 9200, "http")).build();
client = new RestHighLevelClient(lowLevelRestClient);
}
#PreDestroy
public void destroy() throws IOException {
lowLevelRestClient.close();
}
#Bean
public RestHighLevelClient getClient() {
return client;
}
}
Step 3: Execute the query using the Java Transport Client.
Use the Java Transport Client to execute the query.
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
#RestController
#RequestMapping("/api/v1")
public class SearchController {
#Autowired
private RestHighLevelClient client;
#RequestMapping(value = "/search", method = RequestMethod.GET, produces="application/json" )
public ResponseEntity<Tweet> getSearchQueryResults(#RequestParam(value = "criteria") String criteria) throws IOException {
// This is only one example. Of course, this logic make non sense and you are going to put it in a DAO
// layer with more logical stuff
SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
SearchResponse searchResponse = client.search(searchRequest);
if(searchResponse.getHits().totalHits > 0) {
SearchHit searchHit = searchResponse.getHits().iterator().next();
// Deserialize to Java. The best option is to use response.getSource() and Jackson
// This is other option.
Tweet tweet = new Tweet();
tweet.setId(searchHit.getField("id").getValue().toString());
tweet.setTittle(searchHit.getField("tittle").getValue().toString());
return ResponseEntity.ok(tweet);
} else {
return ResponseEntity.notFound().build();
}
}
}
Also, use a bean to build the response.
public class Tweet {
private String id;
private String tittle;
public String getTittle() {
return tittle;
}
public void setTittle(String tittle) {
this.tittle = tittle;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// Here rest of bean stuff (equal, hash, etc) or Lombok
}
Step: 4
Enjoy Elasticsearch!
Notes: Java REST Client [5.6] » Java High Level REST Client
PS. It is necessary to refactor the example. It is only to understand the way.
Are you sure that you don't initiate a new (thread pool) connection to the elasticsearch server on every HTTP request? I.e., in line
Response response = this.restClient.performRequest(...);
Double-check the logs on the elasticsearch server after a single HTTP request. You should try implementing a Singleton pattern without the #Autowired annotation and see if the problem persists.
From your stacktrace, it appears that embedded tomcat(your application container) is not longer able to accept new connection due to too many open files error. From your code, elasticsearch rest client does not seems problematic.
Since you are re-using the single instance of RestClient while servicing your search request, there may not be more more than 30 (org.elasticsearch.client.RestClientBuilder.DEFAULT_MAX_CONN_TOTAL) open connections with ES cluster. So it is unlikely that RestClient it is causing the issue.
Other potential root cause may be your service's consumer are keeping connection open for longer time with your (tomcat) server or they are not closing connection properly.
Do I need to explicitly put a finally clause inside the
SearchController and close the RestClient connection explicitly?
No. You shouldn't. Rest client should be closed while bringing down your service(in a #PreDestroy method as you are already doing correctly).
I am able to return a JSONP from a custom java object without problems (following this: http://www.concretepage.com/spring-4/spring-4-mvc-jsonp-example-with-rest-responsebody-responseentity), but when i try to return a String withing the JSONP the wrapping function disappears
What i am doing:
#RequestMapping(value ="/book", produces = {MediaType.APPLICATION_JSON_VALUE, "application/javascript"})
public #ResponseBody ResponseEntity<String> bookInfo() {
JSONObject test = new JSONObject();
test.put("uno", "uno");
return new ResponseEntity<String>(test.toString(), HttpStatus.OK);
}
Call to the service:
http://<server>:port//book?callback=test
Returns:
{"uno":"uno"}
Expected result:
test({"uno":"uno"})
Also tried to return directly the JSONObject ResponseEntity.accepted().body(test); but i got a 406 Error. Any ideas?
The error looks like the class JsonpAdvice from this example, isn't available for the request mapping.
#ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
I used HashMap, since it has a similar use here and HashMap is more straightforward to use in this example:
#RequestMapping(value="/book", produces=MediaType.APPLICATION_JSON)
public ResponseEntity<Map> bookInfo() {
Map test = new HashMap();
test.put("uno", "uno");
return ResponseEntity.accepted().body(test);
}
This provided me with the result:
// http://localhost:8080/book?callback=test
/**/test({
"uno": "uno"
});
I was using Spring boot:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
I recreate a spring boot simple project "greetings", and when i run it On server, it work and i get JSON data in the browser like this:
localhost:9090/api/greetings
and it show:
[{"id":1,"text":"Hello world1"},{"id":2,"text":"Hello world2"}]
and i generate successfully the war file via console :
...\workspace-sts-3.8.0.RELEASE\demo>mvn clean install
and I have already set up tomcat7 on my ubuntu server 14.04 tls,
and throw the web manager I select the war file to deploy on the server:
but when tap the url to show all greetings for example, it doesn't show anything:
error : the requested resource type is not valid
so i check the error in the tomcat 7 catalina.out log:
INFO: Deploying web application archive /var/lib/tomcat7/webapps/demo-0.0.1-SNAPSHOT.war
Jul 28, 2016 7:38:55 PM org.apache.catalina.loader.WebappClassLoader validateJarFile
INFO: validateJarFile(/var/lib/tomcat7/webapps/demo-0.0.1-SNAPSHOT/WEB-INF/lib/javax.el-3.0.0.jar) - jar not loaded. See Servlet Spec 3.0, section 10.7.2. Offending class: javax/el/Expression.class
Jul 28, 2016 7:38:55 PM org.apache.catalina.loader.WebappClassLoader validateJarFile
INFO: validateJarFile(/var/lib/tomcat7/webapps/demo-0.0.1-SNAPSHOT/WEB-INF/lib/javax.servlet-api-3.1.0.jar) - jar not loaded. See Servlet Spec 3.0, section 10.7.2. Offending class: javax/servlet/Servlet.class
Jul 28, 2016 7:38:55 PM org.apache.catalina.loader.WebappClassLoader validateJarFile
INFO: validateJarFile(/var/lib/tomcat7/webapps/demo-0.0.1-SNAPSHOT/WEB-INF/lib/tomcat-embed-el-8.0.36.jar) - jar not loaded. See Servlet Spec 3.0, section 10.7.2. Offending class: javax/el/Expression.class
But i did't understand what's the issue exactly,
this is my project structure :
project structure
and this is my java classes:
1/class DemoApplication
#SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(DemoApplication.class);
}
}
2/class Greeting
public class Greeting {
private BigInteger id;
private String text;
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
3/class GreetingController
#RestController
public class GreetingController {
private static BigInteger nextId;
private static Map<BigInteger, Greeting> greetingMap;
private static Greeting save(Greeting greeting){
if(greetingMap == null){
greetingMap = new HashMap<BigInteger, Greeting>();
nextId = BigInteger.ONE;
}
//if update....
if(greeting.getId() != null){
Greeting oldGreeting = greetingMap.get(greeting.getId());
if(oldGreeting == null){
return null;
}
greetingMap.remove(greeting.getId());
greetingMap.put(greeting.getId(), greeting);
return greeting;
}
// if create....
greeting.setId(nextId);
nextId = nextId.add(BigInteger.ONE);
greetingMap.put(greeting.getId(), greeting);
return greeting;
}
static {
Greeting g1 = new Greeting();
g1.setText("Hello word");
save(g1);
Greeting g2 = new Greeting();
g2.setText("Hello samsa");
save(g2);
}
#RequestMapping(
value="/api/greetings",
method=RequestMethod.GET,
produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Collection<Greeting>> getGreetings(){
Collection<Greeting> greetings = greetingMap.values();
return new ResponseEntity<Collection<Greeting>>(greetings, HttpStatus.OK);
}
#RequestMapping(
value="/api/greetings/{id}",
method=RequestMethod.GET,
produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Greeting> getGreeting(#PathVariable("id") BigInteger id){
Greeting greeting = greetingMap.get(id);
if(greeting == null){
return new ResponseEntity<Greeting>(greeting, HttpStatus.NOT_FOUND);
}
return new ResponseEntity<Greeting>(greeting, HttpStatus.OK);
}
#RequestMapping(
value="/api/greetings",
method=RequestMethod.POST,
consumes=MediaType.APPLICATION_JSON_VALUE,
produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Greeting> createGreeting(#RequestBody Greeting greeting){
Greeting savedGreeting = save(greeting);
return new ResponseEntity<Greeting>(savedGreeting, HttpStatus.CREATED);
}
#RequestMapping(
value="/api/greetings/{id}",
method=RequestMethod.PUT,
consumes=MediaType.APPLICATION_JSON_VALUE,
produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Greeting> updateGreeting(#RequestBody Greeting greeting){
Greeting updatedGreeting = save(greeting);
if(updatedGreeting == null){
return new ResponseEntity<Greeting>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<Greeting>(updatedGreeting, HttpStatus.OK);
}
private static boolean delete(BigInteger id) {
Greeting deletedGreeding = greetingMap.remove(id);
if(deletedGreeding == null){
return false;
}
return true;
}
#RequestMapping(
value="/api/greetings/{id}",
method=RequestMethod.DELETE,
consumes=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Greeting> deletedGreeting(#PathVariable("id") BigInteger id, #RequestBody Greeting greeting){
boolean deleted = delete(id);
if(!deleted){
return new ResponseEntity<Greeting>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<Greeting>(HttpStatus.NO_CONTENT);
}
}
and this is the pom file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
It is possible that you did not set your context path on application.properties and therefore Tomcat is using the default path of the application name.
Add server.contextPath=/ to the properties file to set the context path for the embedded tomcat and for the external tomcat add the context.xml with the below to your project to set the context path.
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/"/>
I'm making a web application using Spring Boot and I have the functionality I want by using a in memory database(H2) but I can't connect it to the postgresql server I set up on my computer. I've been at this for some time and tried a bunch of stuff that didn't work so I set everything back to the way it was just to get it working again.
Here's my UploadController.java, it handles the upload from the server and puts it into my in memory database:
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import project.service.MediaFile;
import project.service.MediaFileRepository;
#Controller
public class UploadController {
#Autowired
private MediaFileRepository repository;
#RequestMapping(value = "/uploadmedia", method = RequestMethod.GET)
public String uploadForm() {
return "upload";
}
#RequestMapping(value = "/uploadmedia", method = RequestMethod.POST)
public String uploadSubmit(#RequestParam(value="files[]") MultipartFile[] files,
#RequestParam("tags") String tags, #RequestParam("type") String type)
{
String[] tagsArray = tags.split("\\s+");
MultipartFile file;
String name;
String tag;
String path;
for (int i = 0; i < files.length; i++) {
file = files[i];
name = file.getOriginalFilename();
path = "/Users/johannesthorkell/Developer/spring_prufa/images/" + name;
System.out.println(name);
if (!file.isEmpty()) {
try {
byte[] bytes = file.getBytes();
BufferedOutputStream stream =
new BufferedOutputStream(new FileOutputStream(new File(path)));
stream.write(bytes);
stream.close();
for (int j = 0; j < tagsArray.length; j++) {
tag = tagsArray[j].toLowerCase();
repository.save(new MediaFile(name, tag, path, type));
}
System.out.println("Success!");
} catch (Exception e) {
System.out.println("Failure... " + e.getMessage());
}
} else {
System.out.println("No file");
}
}
return "upload";
}
}
...and here's my MediaFile class, the #Entity object:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class MediaFile {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String name;
private String tag;
private String resource;
private String type;
protected MediaFile() {}
public MediaFile(String name, String tag, String resource, String type) {
this.name = name;
this.tag = tag;
this.resource = resource;
this.type = type;
}
public String getTag() {
return tag;
}
#Override
public String toString() {
return String.format(
"MediaFile[id=%d, name='%s', tag='%s', resource='%s', type='%s']",
id, name, tag, resource, type);
}
}
...here's my pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>HBV501G</groupId>
<artifactId>Spring_Web_MVC</artifactId>
<version>0.1</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
...and finally, here's the application.properties:
spring.view.prefix=/WEB-INF/jsp/
spring.view.suffix=.jsp
multipart.maxFileSize=-1
With this setup everything works. I tried putting the following in my application.properties:
spring.datasource.url=jdbc:postgresql://localhost/test
spring.datasource.username=myusername
spring.datasource.password=mypassword
spring.datasource.driver-class-name=org.postgresql.jdbc.Driver
...Along with adding the following dependency to the pom.xml:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1200-jdbc41</version>
</dependency>
...And then I tried mixing and matching these things and reading tutorials for a few hours to no avail.
Edit:
I removed one line and added another (at the advice of Stéphane Nicoll) so now my application.properties look like this:
spring.view.prefix=/WEB-INF/jsp/
spring.view.suffix=.jsp
multipart.maxFileSize=-1
debug=true
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=username
spring.datasource.password=password
...And my pom.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>HBV501G</groupId>
<artifactId>Spring_Web_MVC</artifactId>
<version>0.1</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1200-jdbc41</version>
</dependency>
<!--<dependency>-->
<!--<groupId>com.h2database</groupId>-->
<!--<artifactId>h2</artifactId>-->
<!--</dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
With the changes to my application.properties my app now runs! But I get the following error when I submit to my POST form to UploadController.java:
2015-10-06 11:32:14.878 INFO 22287 --- [ main] project.Application : Started Application in 11.897 seconds (JVM running for 12.971)
2015-10-06 11:32:40.263 INFO 22287 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2015-10-06 11:32:40.264 INFO 22287 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2015-10-06 11:32:40.295 INFO 22287 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 31 ms
hopaskipting.pdf
2015-10-06 11:32:49.752 DEBUG 22287 --- [nio-8080-exec-3] org.hibernate.SQL : select nextval ('hibernate_sequence')
2015-10-06 11:32:49.760 WARN 22287 --- [nio-8080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 42P01
2015-10-06 11:32:49.760 ERROR 22287 --- [nio-8080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: relation "hibernate_sequence" does not exist
Position: 17
Failure... could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
I changed the GenerationType to IDENTITY and get the following error:
2015-10-06 12:56:32.496 DEBUG 22746 --- [nio-8080-exec-7] org.hibernate.SQL : insert into media_file (name, resource, tag, type) values (?, ?, ?, ?)
2015-10-06 12:56:32.505 DEBUG 22746 --- [nio-8080-exec-7] org.hibernate.SQL : insert into media_file (name, resource, tag, type) values (?, ?, ?, ?)
Failure... A different object with the same identifier value was already associated with the session : [project.service.MediaFile#0]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [project.service.MediaFile#0]
The key part of the error is:
2015-10-06 11:32:49.760 ERROR 22287 --- [nio-8080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: relation "hibernate_sequence" does not exist
Hibernate's looking for a table named hibernate_sequence to support #GeneratedValue on MediaFile. You've configured it with AUTO at the moment. The actual behaviour then varies depending on the database you're using.
I think you have a couple of options:
Create a sequence in Postgres (CREATE SEQUENCE), named hibernate_sequence
Change to using a different generation type, e.g. GenerationType.IDENTITY
If I were you, I'd create my own dataSource bean, like:
#Configuration
public class MyConfig{
#Autowired
Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("myapp.dataSource.driver"));
dataSource.setUrl(env.getProperty("myapp.dataSource.url"));
dataSource.setUsername(env.getProperty("myapp.dataSource.username"));
dataSource.setPassword(env.getProperty("myapp.dataSource.password"));
return dataSource;
}
}
application.properties:
logging.level. = INFO
myapp.dataSource.driver =
myapp.dataSource.url =
myapp.dataSource.username =
myapp.dataSource.password =
IF you don't want to try this, you could try changing your driver org.postgresql.jdbc.Driver to org.postgresql.Driver.
It's hard to help without any logs.