├── .gitignore
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── org
│ │ └── acme
│ │ └── sample
│ │ ├── SpringBootFacesApplication.java
│ │ ├── jsf
│ │ └── FacesViewScope.java
│ │ └── web
│ │ ├── FormBean.java
│ │ └── FormController.java
├── resources
│ └── application.yml
└── webapp
│ ├── WEB-INF
│ ├── faces-config.xml
│ └── web.xml
│ └── index.xhtml
└── test
└── java
└── org
└── acme
└── sample
└── SpringBootFacesApplicationTests.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled
2 | *.class
3 | *.jar
4 | *.war
5 |
6 | # Maven
7 | /target
8 |
9 | # Eclipse
10 | /.classpath
11 | /.project
12 | /.settings
13 | /.metadata
14 |
15 | # Git
16 | !.gitignore
17 | !.gitkeep
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ATTENTION: I do not recommend this example anymore! Better use [JoinFaces](http://joinfaces.org/)
2 |
3 | Spring Boot With JSF Sample
4 | =================================================================
5 |
6 | * Shows how to configure JSF 2.2.x with Spring Boot 1.2.x on Servlet API 3.x
7 |
8 | ### Running
9 |
10 | This setup can run in all possible ways:
11 |
12 | * Either deploy on Tomcat inside Eclipse
13 | * Or execute `SpringBootFacesApplication` as Java application
14 | * Or run `mvn spring-boot:run`
15 | * Or run `mvn package` and then:
16 | * either deploy `target\projectname.war`
17 | * or run `java -jar target\projectname.war`
18 |
19 | ### More things shown
20 |
21 | * How to configure Faces View Scope with Spring
22 | * How to include PrimeFaces
23 | * How to include a custom theme for PrimeFaces (in this case primefaces-bootstrap-theme)
24 | * How to include Twitter Bootstrap CSS for responsive layout
25 | * Spring Boot Actuator is added under /sysinfo path (try for example /sysinfo/metrics)
26 | * How to write simple http tests
27 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 1.2.3.RELEASE
9 |
10 |
11 |
12 | de.igormukhin.samples
13 | spring-boot-org.acme.sample-jsf
14 | 0.0.1-SNAPSHOT
15 |
16 | war
17 |
18 |
19 |
20 | 7
21 |
22 |
23 | 7.0.62
24 |
25 | 2.2.11
26 | 5.2
27 |
28 |
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-web
33 |
34 |
35 |
39 |
40 | org.springframework.boot
41 | spring-boot-starter-tomcat
42 | provided
43 |
44 |
45 |
46 | org.apache.tomcat.embed
47 | tomcat-embed-jasper
48 | provided
49 |
50 |
51 |
52 | org.springframework.boot
53 | spring-boot-starter-actuator
54 |
55 |
56 |
57 | org.primefaces
58 | primefaces
59 | ${primefaces.version}
60 |
61 |
62 |
63 |
64 |
65 | org.primefaces.themes
66 | bootstrap
67 | 1.0.10
68 |
69 |
70 |
71 | org.glassfish
72 | javax.faces
73 | ${jsf-mojarra.version}
74 |
75 |
76 |
77 | org.springframework.boot
78 | spring-boot-starter-test
79 | test
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | org.springframework.boot
88 | spring-boot-maven-plugin
89 |
90 |
93 | false
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/src/main/java/org/acme/sample/SpringBootFacesApplication.java:
--------------------------------------------------------------------------------
1 | package org.acme.sample;
2 |
3 | import java.util.Collections;
4 | import java.util.HashSet;
5 | import java.util.Set;
6 |
7 | import javax.faces.application.ProjectStage;
8 | import javax.servlet.ServletContainerInitializer;
9 | import javax.servlet.ServletContext;
10 | import javax.servlet.ServletException;
11 | import javax.servlet.annotation.HandlesTypes;
12 |
13 | import org.acme.sample.jsf.FacesViewScope;
14 | import org.apache.catalina.Context;
15 | import org.primefaces.util.Constants;
16 | import org.springframework.beans.factory.config.CustomScopeConfigurer;
17 | import org.springframework.boot.SpringApplication;
18 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
20 | import org.springframework.boot.builder.SpringApplicationBuilder;
21 | import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
22 | import org.springframework.boot.context.embedded.ServletContextInitializer;
23 | import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
24 | import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
25 | import org.springframework.boot.context.web.NonEmbeddedServletContainerFactory;
26 | import org.springframework.boot.context.web.SpringBootServletInitializer;
27 | import org.springframework.context.annotation.Bean;
28 | import org.springframework.context.annotation.ComponentScan;
29 | import org.springframework.context.annotation.Configuration;
30 |
31 | import com.sun.faces.config.FacesInitializer;
32 |
33 | @Configuration
34 | @ComponentScan
35 | @EnableAutoConfiguration
36 | public class SpringBootFacesApplication extends SpringBootServletInitializer {
37 |
38 | public static void main(String[] args) throws Exception {
39 | SpringApplication.run(SpringBootFacesApplication.class, args);
40 | }
41 |
42 | @Override
43 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
44 | return application.sources(SpringBootFacesApplication.class);
45 | }
46 |
47 | @Bean
48 | public static CustomScopeConfigurer customScopeConfigurer() {
49 | CustomScopeConfigurer configurer = new CustomScopeConfigurer();
50 | configurer.setScopes(Collections.singletonMap(
51 | FacesViewScope.NAME, new FacesViewScope()));
52 | return configurer;
53 | }
54 |
55 | @Bean
56 | public ServletContextInitializer servletContextCustomizer() {
57 | return new ServletContextInitializer() {
58 | @Override
59 | public void onStartup(ServletContext sc) throws ServletException {
60 | sc.setInitParameter(Constants.ContextParams.THEME, "bootstrap");
61 | sc.setInitParameter(Constants.ContextParams.FONT_AWESOME, "true");
62 | sc.setInitParameter(ProjectStage.PROJECT_STAGE_PARAM_NAME, ProjectStage.Development.name());
63 | }
64 | };
65 | }
66 |
67 | /**
68 | * This bean is only needed when running with embedded Tomcat.
69 | */
70 | @Bean
71 | @ConditionalOnMissingBean(NonEmbeddedServletContainerFactory.class)
72 | public EmbeddedServletContainerFactory embeddedServletContainerFactory() {
73 | TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
74 |
75 | tomcat.addContextCustomizers(new TomcatContextCustomizer() {
76 | @Override
77 | public void customize(Context context) {
78 | // register FacesInitializer
79 | context.addServletContainerInitializer(new FacesInitializer(),
80 | getServletContainerInitializerHandlesTypes(FacesInitializer.class));
81 |
82 | // add configuration from web.xml
83 | context.addWelcomeFile("index.jsf");
84 |
85 | // register additional mime-types that Spring Boot doesn't register
86 | context.addMimeMapping("eot", "application/vnd.ms-fontobject");
87 | context.addMimeMapping("ttf", "application/x-font-ttf");
88 | context.addMimeMapping("woff", "application/x-font-woff");
89 | }
90 | });
91 |
92 | return tomcat;
93 | }
94 |
95 | @SuppressWarnings("rawtypes")
96 | private Set> getServletContainerInitializerHandlesTypes(Class extends ServletContainerInitializer> sciClass) {
97 | HandlesTypes annotation = sciClass.getAnnotation(HandlesTypes.class);
98 | if (annotation == null) {
99 | return Collections.emptySet();
100 | }
101 |
102 | Class[] classesArray = annotation.value();
103 | Set> classesSet = new HashSet>(classesArray.length);
104 | for (Class clazz: classesArray) {
105 | classesSet.add(clazz);
106 | }
107 |
108 | return classesSet;
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/org/acme/sample/jsf/FacesViewScope.java:
--------------------------------------------------------------------------------
1 | package org.acme.sample.jsf;
2 |
3 | import java.util.Map;
4 |
5 | import javax.faces.context.FacesContext;
6 |
7 | import org.springframework.beans.factory.ObjectFactory;
8 | import org.springframework.beans.factory.config.Scope;
9 |
10 | /**
11 | * Ein Bean, dass die View-Scope von JSF bei Spring bekannt macht.
12 | *
13 | * Diese Scope wird standardmäßig in /alf-jsf/src/main/resources/alf-jsf-beans.xml
registiert.
14 | *
15 | * Ohne dieser Datei kann man die Scope auf folgende Weise in Spring registrieren:
16 | *
{@code
17 | *
18 | *
19 | *
24 | *
25 | *
26 | * }
27 | *
28 | *
29 | *
30 | */
31 | public class FacesViewScope implements Scope {
32 |
33 | public static final String NAME = "view";
34 |
35 | @Override
36 | public Object get(String name, ObjectFactory> objectFactory) {
37 | FacesContext facesContext = FacesContext.getCurrentInstance();
38 | if (facesContext == null) {
39 | throw new IllegalStateException("FacesContext.getCurrentInstance() returned null");
40 | }
41 |
42 | Map viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
43 |
44 | if (viewMap.containsKey(name)) {
45 | return viewMap.get(name);
46 | } else {
47 | Object object = objectFactory.getObject();
48 | viewMap.put(name, object);
49 |
50 | return object;
51 | }
52 | }
53 |
54 | @Override
55 | public Object remove(String name) {
56 | return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
57 | }
58 |
59 | @Override
60 | public String getConversationId() {
61 | return null;
62 | }
63 |
64 | @Override
65 | public void registerDestructionCallback(String name, Runnable callback) {
66 | // Not supported by JSF for view scope
67 | }
68 |
69 | @Override
70 | public Object resolveContextualObject(String key) {
71 | return null;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/org/acme/sample/web/FormBean.java:
--------------------------------------------------------------------------------
1 | package org.acme.sample.web;
2 |
3 | import java.io.Serializable;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | import org.acme.sample.jsf.FacesViewScope;
8 | import org.springframework.context.annotation.Scope;
9 | import org.springframework.stereotype.Component;
10 |
11 | @Component("formBean")
12 | @Scope(FacesViewScope.NAME)
13 | public class FormBean implements Serializable {
14 |
15 | private static final long serialVersionUID = 1L;
16 |
17 | private List submittedValues = new ArrayList<>();
18 |
19 | private String field;
20 |
21 | public String getField() {
22 | return field;
23 | }
24 |
25 | public void setField(String field) {
26 | this.field = field;
27 | }
28 |
29 | public List getSubmittedValues() {
30 | return submittedValues;
31 | }
32 |
33 | public void setSubmittedValues(List submittedValues) {
34 | this.submittedValues = submittedValues;
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/org/acme/sample/web/FormController.java:
--------------------------------------------------------------------------------
1 | package org.acme.sample.web;
2 |
3 | import javax.faces.application.FacesMessage;
4 | import javax.faces.context.FacesContext;
5 |
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.context.annotation.Scope;
8 | import org.springframework.stereotype.Component;
9 |
10 | @Component("formController")
11 | @Scope("request")
12 | public class FormController {
13 | @Autowired
14 | private FormBean formBean;
15 |
16 | public void submit() {
17 | formBean.getSubmittedValues().add(formBean.getField());
18 |
19 | FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Value submitted."));
20 | }
21 |
22 | public void reset() {
23 | formBean.getSubmittedValues().clear();
24 | formBean.setField(null);
25 |
26 | FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Form reset."));
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | management:
2 | context-path: /sysinfo
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/faces-config.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | org.springframework.web.jsf.el.SpringBeanFacesELResolver
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | index.jsf
5 |
6 |
--------------------------------------------------------------------------------
/src/main/webapp/index.xhtml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
28 |
29 |
30 |
31 |
40 |
41 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | #{value}
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/src/test/java/org/acme/sample/SpringBootFacesApplicationTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2014 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.acme.sample;
18 |
19 | import static org.junit.Assert.assertEquals;
20 | import static org.junit.Assert.assertTrue;
21 |
22 | import java.util.Map;
23 |
24 | import org.junit.Test;
25 | import org.junit.runner.RunWith;
26 | import org.springframework.beans.factory.annotation.Value;
27 | import org.springframework.boot.test.IntegrationTest;
28 | import org.springframework.boot.test.SpringApplicationConfiguration;
29 | import org.springframework.boot.test.TestRestTemplate;
30 | import org.springframework.http.HttpStatus;
31 | import org.springframework.http.ResponseEntity;
32 | import org.springframework.test.annotation.DirtiesContext;
33 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
34 | import org.springframework.test.context.web.WebAppConfiguration;
35 |
36 | /**
37 | * Basic integration tests.
38 | *
39 | * @author Phillip Webb
40 | */
41 | @RunWith(SpringJUnit4ClassRunner.class)
42 | @SpringApplicationConfiguration(classes = SpringBootFacesApplication.class)
43 | @WebAppConfiguration
44 | @IntegrationTest("server.port:0")
45 | @DirtiesContext
46 | public class SpringBootFacesApplicationTests {
47 |
48 | @Value("${local.server.port}")
49 | private int port;
50 |
51 | @Value("${management.context-path:}")
52 | private String actuatorPath;
53 |
54 | @Test
55 | public void testJsfWelcomePage() throws Exception {
56 | ResponseEntity entity = new TestRestTemplate().getForEntity("http://localhost:" + this.port, String.class);
57 | assertEquals(HttpStatus.OK, entity.getStatusCode());
58 | assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody().contains("javax.faces.ViewState"));
59 | }
60 |
61 | @SuppressWarnings({"rawtypes", "unchecked"})
62 | @Test
63 | public void testActuatorHealthPage() throws Exception {
64 | ResponseEntity