For an assignment i'm practising Java's Spring MVC for creating a web application. I've build this whole project in the IDE Intellij Ultimate 2016.2.5.
I've created a Maven project for this, imported the correct and asked dependencies for this and build it.
The IDE build the following directory structure:
├───src
│ └───bas
│ └───animalkingdom
│ ├───config
│ ├───controllers
├───test
│ └───bas
│ └───animalkingdom
└───web
├───META-INF
├───resources
└───WEB-INF
└───pages
The config package is where my configuration class is, extending from WebMvcConfigurerAdapter:
package bas.animalkingdom.config;
import ...
#Configuration
#ComponentScan("bas.animalkingdom")
#EnableWebMvc
public class Config extends WebMvcConfigurerAdapter {
#Bean
public UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/pages/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
I thought the #ComponentScan had to point to the main source directory where all your source files are.
I was told that I also need a class extending from the WebApplicationInitializer. I got this one from my school
package bas.animalkingdom.config;
import ...
public class WebInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(Config.class);
ctx.setServletContext(servletContext);
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
This one is also in the config package.
The Config class is set as the Spring Application Context in the project structure settings in my IDE.
In the root directory is the web folder. In the folder WEB-INF is a empty web.xml file, which I was told I didn't need because the settings would be loaded via the configuration class. It looks like this:
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
</web-app>
In the root directory of the web folder is a index.jsp file.
In the bas.animalkingdom.controllers package are my controllers. For testing purposing, I only created one:
package bas.animalkingdom.controllers;
import ...
#Controller("AnimalC")
public class AnimalController {
#RequestMapping(value = "/animals", method = RequestMethod.GET)
public String getAnimals(ModelMap modelMap) {
Animal animal = new AfricanElephant(new Male(), "Body Covering", "Ename", " acolor", 123, 321);
modelMap.put("animal", animal);
return "animals";
}
}
With this controller I expected that I can go to the localhost/animals URL, and that it would load up the animals.jsp file located in my web\WEB-INF\pages\ package.
My code has no compile errors in it.
When I run my TomCat server, and open my browser to go to the localhost with the corresponding host, the index.jsp file just loads with no problem. This file is located in the web\ package.
When I go to the localhost:(port)/animals, I just get a 404 page, with the message that the page could not be found.
What does cause this? I've defined the controller which sets that route right?
Also, when looking up other Spring MVC tutorials, they all use a different packaging, does this work as well?
All your java classes and jsps are fine, problem is your module structure. There are basically 2 approaches to your problem, the conventional (recommended) and unconventional way. Let's start with the unconventional way, which is quicker and not recommended:
A. The Unconventional:
Add to your maven-war-plugin, the warSourceDirectory tag as shown below:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<warSourceDirectory>${basedir}/web</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
The disadvantage of using this approach is that other plugins may give you unexpected problems down the road, for example the maven-jetty-plugin will not run out of the box with this approach. It defaults to looking in src/main/webapp, although it is configurable. Maven life is easier if you follow the conventional approach.
B. Conventional Approach:
The solution lies in having a conventional maven structure for your module, make sure to do the following:
Delete Your web.xml file. If maven package failed because of missing web.xml file, add the below plugin in the build section of your pom.xml :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
Create folder structure /src/main/java and put all your java packages into this java directory. If your packages are not formatted properly, you can right click folder java and go to Mark Directory as -> Source Root.
Create folder structure /src/test/java and put all your test packages in the java directory.
Create a folder structure /src/main/webapp and put all the contents of folder web into the webapp directory.
After doing this you can test your application. You can try using jetty plugin to deploy your web app with the below configuration:
<build>
<finalName>spring-mvc</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.2.11.v20150529</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<webApp>
<contextPath>/</contextPath>
</webApp>
<httpConnector>
<port>8080</port>
</httpConnector>
</configuration>
</plugin>
</plugins>
</build>
Related
Having a Spring application with Maven where all the configuration is done in Java (all configuration previously stored in web.xml is now in annotated #Configuration files or in WebAppInitializer that extends AbstractAnnotationConfigDispatcherServletInitializer), how can I set the context root for my application in JBoss Wildfly? The app has no web.xml, nor jboss-web.xml .
When the app used XML configuration the context root was set in jboss-web.xml like this:
<jboss-web>
<context-root>mywebcontextroot</context-root>
</jboss-web>
JBoss wildfly defaults the context root to the name of the war file. Setting the name of the war file to the desired value (web context root) in Maven solves the issue:
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
<warName>mywebcontextroot</warName>
</configuration>
</plugin>
More detailed answer by #Nikhil Bide can be found here.
How would I place the static contents in a WAR Archive.
I have tried placing them inside the classes folder and works fine, but to make the cleaner I am asked to put it outside the WEB-INF, in a custom folder. I was able to do that and make it work by setting the welcome page in web.xml
<welcome-page>'./app/index.html'</welcome-page>
But the issue is that I don't want 'app' to appear in my URL, when the pages were kept inside classes/static the index.html appeared in root(localhost:8080), but putting the pages in custom folder URL changed to - localhost:8080/app
Is there any way to keep the pages in a custom folder and still get the index page and its dependencies from root URL.
Either you place your resources under "src/main/webapp", these will be copied to the root of your WAR. If you need more flexibility, maybe the maven-war-plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<webResources>
<resource>
<directory>${project.basedir}/mycustomdir</directory>
<targetPath>somewhere</targetPath>
</resource>
</webResources>
</configuration>
</plugin>
I want to have the developer, which is defined in the pom.xml with the tag to appear in the application.yml after the build process. Somehow it is working with all attributes but developers.
This is in a Spring Boot project, I want the attributes to be filled during the build process.
This is an excerpt of the pom.xml
<description>my description</description>
<developers>
<developer>
<id>12345</id>
<name>John Doe</name>
<email>john#doe.com</email>
</developer>
</developers>
This is in application.yml
info:
description: "#project.description#"
developer: "#project.developers[0].id#"
It works for description, but not for developer. I tried many variations, e.g. ${..}, "#project.developers.0.id". Nothing seems to be working.
If anybody has an idea, I would be very grateful.
You can read the developer id or email address or any values from the pom.xml with this elegant way:
Generate the build-info.properties with the default data plus your additional data
Read values from this file easily with Spring via the BuildProperties.
pom.xml:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
<configuration>
<additionalProperties>
<developer>${project.developers[0].email}</developer>
</additionalProperties>
</configuration>
</execution>
</executions>
</plugin>
This will produce a build-info.properties file under the META-INF directory with the following content:
build.artifact=<artifactId-from-pom>
build.group=<groupId-from-pom>
build.name=<name-from-pom>
build.time=<build-time>
build.version=<version-from-pom>
build.developer=<email-of-the-first-developer-from-pom>
Then you can read values with Spring:
#Configuration
public class OpenApiConfiguration {
#Autowired
private BuildProperties buildProperties;
#Bean
public OpenAPI customOpenAPI()) {
return new OpenAPI().info(new Info()
.title(...)
.version(buildProperties.getVersion())
.contact(new Contact().email(buildProperties.get("developer"))));
}
}
To read:
Spring Boot Maven Plugin
BuildProperties
I hope that this will help you.
Add it on the property and use on pom and property file, example:
<properties>
<team.name>John Doe</team.name>
</properties>
Use on developer data:
<developers>
<developer>
<name>${team.name}</name>
...
And in the application use the property:
description: "#team.name#"
Typically when you create a webapp you will access your webapp when you hit the url
<your_IP>/<Project_name>/
Example:
127.0.0.1/MyWebapp/
Question: How do you configure your webapp to run from base URL
Example
127.0.0.1/
Moreover, when the browser navigates to your IP (and not your IP + name of your webapp) you can hit your webpage
Question: Is this a configuration file that needs to be edited in your Application web server?
It depends on your container, but generally you will name your file ROOT.war or specify the context explicitly.
For example, since you've tagged this question with jetty, here is the plugin in my pom.xml for embedded testing using mvn jetty:run. Notice the contextPath element.
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.25</version>
<configuration>
<contextPath>/</contextPath>
<scanIntervalSeconds>5</scanIntervalSeconds>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8081</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
</configuration>
</plugin>
When I deploy to Tomcat, I simply name my file ROOT.war and drop it into the webapps folder. Note that you'll need to move or remove the existing ROOT content from webapps first.
Doing this will allow you to access your application at http[s]://<host>[:<port>]/ with no extra context needed.
Assuming you're using maven to build your application, you can avoid manually re-naming your WAR by specifying the finalName in your pom.xml.
<build>
<finalName>ROOT</finalName>
[...]
</build>
How can I tell e.g. Tomcat to use a specific context path when given my WAR-File?
Example:
I have a war file created by maven build and the resulting name of the file is rather long.
So I do not want the tomcat manager application to use the filename of the war as the context.
Supplying a context.xml in META-INF did not produce the desired results
I also found this in the documentation for the path attribute of Context:
The value of this field must not be set except when statically defining a Context in server.xml, as it will be inferred from the filenames used for either the .xml context file or the docBase.
So it does not seem to be the right way to tell the application-server what the path for my WAR should be.
Any more hints?
There are two important points in the the documentation of the Context Container:
In individual files (with a ".xml" extension) in the $CATALINA_BASE/conf/[enginename]/[hostname]/ directory. The name of the file (less the .xml extension) will be used as the context path. Multi-level context paths may be defined using #, e.g. foo#bar.xml for a context path of /foo/bar. The default web application may be defined by using a file called ROOT.xml.
Only if a context file does not exist for the application in the $CATALINA_BASE/conf/[enginename]/[hostname]/, in an individual file at /META-INF/context.xml inside the application files. If the web application is packaged as a WAR then /META-INF/context.xml will be copied to $CATALINA_BASE/conf/[enginename]/[hostname]/ and renamed to match the application's context path. Once this file exists, it will not be replaced if a new WAR with a newer /META-INF/context.xml is placed in the host's appBase.
So, when you bundle a META-INF/context.xml, the file gets renamed to the name of the WAR and this name becomes the context path, regardless of any path defined in the Context element.
I see thus two options here:
Either set the name of the generated war to a shorter name (I suggest using <finalName> over <warName> which is deprecated AFAIK):
<project>
...
<build>
<finalName>mycontext</finalName>
...
</build>
...
</project>
Or use the maven-tomcat-plugin for the deployment and set the context path in the plugin configuration:
<project>
...
<build>
...
<plugins>
...
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<configuration>
<path>/mycontext</path>
</configuration>
</plugin>
...
</plugins>
...
</build>
...
</project>
I found an easy solution to keep war file name and choose the context-path.
You just have to deploy your war outside of the Host's appBase and to create a link inside the appBase directory.
Ex. :
ln -sf ${CATALINA_HOME}/wars/myapp-0.0.8-SNAPSHOT.war ${CATALINA_HOME}/webapps/myapp.war
Ektor
You can set the path attribute of the <Context> element of your META-INF/context.xml.
Alternatively, you can configure maven to create the war artifact with a custom name:
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.0</version>
<configuration>
<warName>yourCustomWarName</warName>
</configuration>
</plugin>
........
</plugins>
</build>
In your project there is a folder META-INF, in that folder there is a context.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/myproject" />