I have written a Log4j2 custom Converter plugin in OSGi environment and have used log4j2 with ops4j-pax-logging. The custom field is tenantId and I have referred to it in the log4j2.xml as %tenantId. When I run the program the logs gets printed as {thread-name}enantId.
Plugin class
package com.test.logging.converters;
#Plugin(name = "TenantIdConverter", category = "Converter")
#ConverterKeys({"tenantId"})
public class TenantIdConverter extends LogEventPatternConverter {
public TenantIdConverter(String name, String style) {
super(name, style);
}
public static TenantIdConverter newInstance(String[] options) {
return new TenantIdConverter("tenantId", "tenantId");
}
#Override
public void format(LogEvent event, StringBuilder toAppendTo) {
toAppendTo.append(getTenantID());
}
public String getTenantID() {
String tenantId = "1234";
if (tenantId == null) {
tenantId = "[]";
}
return tenantId;
}
}
pom.xml
<dependency>
<groupId>org.ops4j.pax.logging</groupId>
<artifactId>pax-logging-api</artifactId>
<version>1.10.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.logging</groupId>
<artifactId>pax-logging-log4j2</artifactId>
<version>1.10.1</version>
</dependency>
<plugin>
<groupId>org.ops4j</groupId>
<artifactId>maven-pax-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<executions>
<execution>
<id>log4j-plugin-processor</id>
<goals>
<goal>compile</goal>
</goals>
<phase>process-classes</phase>
<configuration>
<proc>only</proc>
<annotationProcessors>
<annotationProcessor>org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor</annotationProcessor>
</annotationProcessors>
</configuration>
</execution>
</executions>
</plugin>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO" name="log-test" packages="com.test.logging.converters">
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%-5p %d [%tenantId] %c: %m%n"/>
</Console>
<RollingFile name="RollingFile" fileName="logs/log4j2app.log"
filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout>
<Pattern>%-5p %d [%tenantId] %c: %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="250 MB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
Resolved.
Created an osgi-fragment for org.ops4j.pax.logging.pax-logging-log4j2 and placed the appender in this fragmented bundle.
<Fragment-Host>org.ops4j.pax.logging.pax-logging-log4j2</Fragment-Host>
Related
I am following the guide: Best practice for REST token-based authentication with JAX-RS and Jersey, for implementing an Authentication and Authorization filter for my RestAPI. I am getting this one error:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=User,parent=AuthorizationFilter,qualifiers={#nl.utwente.di.team26.Security.Authentication.User.AuthenticatedUser()},position=-1,optional=false,self=false,unqualified=null,1372104990)
at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:51)
at org.jvnet.hk2.internal.ClazzCreator.resolve(ClazzCreator.java:188)
at org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:211)
at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:334)
at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:463)
at org.jvnet.hk2.internal.SingletonContext$1.compute(SingletonContext.java:59)
at org.jvnet.hk2.internal.SingletonContext$1.compute(SingletonContext.java:47)
at org.glassfish.hk2.utilities.cache.Cache$OriginThreadAwareFuture$1.call(Cache.java:74)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.glassfish.hk2.utilities.cache.Cache$OriginThreadAwareFuture.run(Cache.java:131)
at org.glassfish.hk2.utilities.cache.Cache.compute(Cache.java:176)
at org.jvnet.hk2.internal.SingletonContext.findOrCreate(SingletonContext.java:98)
at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2102)
at org.jvnet.hk2.internal.ServiceHandleImpl.getService(ServiceHandleImpl.java:93)
at org.jvnet.hk2.internal.ServiceHandleImpl.getService(ServiceHandleImpl.java:67)
at org.glassfish.jersey.inject.hk2.AbstractHk2InjectionManager.lambda$getAllServiceHolders$0(AbstractHk2InjectionManager.java:136)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.LinkedList$LLSpliterator.forEachRemaining(LinkedList.java:1239)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at org.glassfish.jersey.inject.hk2.AbstractHk2InjectionManager.getAllServiceHolders(AbstractHk2InjectionManager.java:140)
at org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager.getAllServiceHolders(ImmediateHk2InjectionManager.java:30)
at org.glassfish.jersey.internal.inject.Providers.getServiceHolders(Providers.java:299)
at org.glassfish.jersey.internal.inject.Providers.getAllRankedProviders(Providers.java:182)
at org.glassfish.jersey.server.ProcessingProvidersConfigurator.postInit(ProcessingProvidersConfigurator.java:95)
at org.glassfish.jersey.server.ApplicationHandler.lambda$initialize$2(ApplicationHandler.java:349)
at java.base/java.util.Arrays$ArrayList.forEach(Arrays.java:4411)
at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:349)
at org.glassfish.jersey.server.ApplicationHandler.lambda$initialize$1(ApplicationHandler.java:293)
In essence:
There is an authentication endpoint which checks the login credentials.
It issues a token/cookie as a response, and is all good, (until I implemented the injection part)
So when the makes a request the request is first inspected by this Authentication Filter:
#Secured
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
#Inject
#AuthenticatedUser
Event<String> userAuthenticatedEvent;
#Context
UriInfo uriInfo;
String userId;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
Map<String, Cookie> cookieJar = requestContext.getCookies();
// Validate the Cookie Token
try {
if (!hasCookie(cookieJar)) {
sendToLogin(requestContext);
} else {
// Extract the token from the Cookie
String token = cookieJar.get(CONSTANTS.COOKIENAME).getValue();
try {
// Validate the token
validateToken(token);
userAuthenticatedEvent.fire(userId);
} catch (TokenObsoleteException e) {
e.printStackTrace();
sendToLogin(requestContext);
} catch (AuthenticationDeniedException e) {
e.printStackTrace();
abortWithUnauthorized(requestContext);
}
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
private boolean hasCookie(Map<String, Cookie> cookieJar) {
// Check if the Authorization header is valid
// It must not be null and must be prefixed with "Bearer" plus a whitespace
// The authentication scheme comparison must be case-insensitive
return cookieJar.containsKey(CONSTANTS.COOKIENAME);
}
private void sendToLogin(ContainerRequestContext requestContext) throws URISyntaxException {
requestContext.abortWith(Response.seeOther(new URI("http://localhost:8080/apiName/")).build());
}
private void abortWithUnauthorized(ContainerRequestContext requestContext) {
// Abort the filter chain with a 401 status code response
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED).build());
}
private void validateToken(String token) throws AuthenticationDeniedException, TokenObsoleteException {
// Check if the token was issued by the server and if it's not expired
// Throw an Exception if the token is invalid
}
public static Claims decodeJWT(String jwt) {
}
}
This filter fires an event such that an Authenticated user can be created.
#RequestScoped
public class AuthenticatedUserProducer {
#Produces
#RequestScoped
#AuthenticatedUser
private User authenticatedUser;
UserDao userDao = new UserDao();
#RequestScoped
public void handleAuthenticationEvent(#Observes #AuthenticatedUser String userId) {
int user = Integer.parseInt(userId);
this.authenticatedUser = findUser(user);
}
private User findUser(int userId) {
User user = null;
// Hit the the database or a service to find a user by its username and return it
// Return the User instance
return user;
}
}
This should produce a User instance, under variable name AuthenticatedUser.
But, when the same code is called for checking User Roles, in the Authorization filter, the error above is shown:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=User,parent=AuthorizationFilter,qualifiers={#nl.utwente.di.team26.Security.Authentication.User.AuthenticatedUser()},position=-1,optional=false,self=false,unqualified=null,1372104990)
The Authorization Filter is:
#Secured
#Provider
#Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {
#Context
private ResourceInfo resourceInfo;
#Inject
#AuthenticatedUser
User authenticatedUser; //<--This is where the error comes from.
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Get the resource class which matches with the requested URL
// Extract the roles declared by it
Class<?> resourceClass = resourceInfo.getResourceClass();
List<Role> classRoles = extractRoles(resourceClass);
// Get the resource method which matches with the requested URL
// Extract the roles declared by it
Method resourceMethod = resourceInfo.getResourceMethod();
List<Role> methodRoles = extractRoles(resourceMethod);
try {
// Check if the user is allowed to execute the method
// The method annotations override the class annotations
if (methodRoles.isEmpty()) {
checkPermissions(classRoles);
} else {
checkPermissions(methodRoles);
}
} catch (NotAuthorizedException e) {
requestContext.abortWith(
Response.status(Response.Status.FORBIDDEN).build());
}
}
// Extract the roles from the annotated element
private List<Role> extractRoles(AnnotatedElement annotatedElement) {
if (annotatedElement == null) {
return new ArrayList<>();
} else {
Secured secured = annotatedElement.getAnnotation(Secured.class);
if (secured == null) {
return new ArrayList<>();
} else {
Role[] allowedRoles = secured.value();
return Arrays.asList(allowedRoles);
}
}
}
private void checkPermissions(List<Role> allowedRoles) throws NotAuthorizedException {
// Check if the user contains one of the allowed roles
// Throw an Exception if the user has not permission to execute the method
if (authenticatedUser.getClarificationLevel() < allowedRoles.get(0).ordinal()) {
throw new NotAuthorizedException("You shall not pass!");
}
}
}
Some other code:
#Secured Annotation
#NameBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Secured {
Role[] value() default {};
}
Authenticated User Annotation
#Qualifier
#Retention(RUNTIME)
#Target({ METHOD, FIELD, PARAMETER })
public #interface AuthenticatedUser { }
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<welcome-file-list>
<welcome-file>login.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
POM
<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>nl.utwente.di.app</groupId>
<artifactId>app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.containers/jersey-container-servlet -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.30.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.30.1</version>
</dependency>
<!-- Required only when you are using JAX-RS Client -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.30.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-json-jackson -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.29.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/jakarta.xml.bind/jakarta.xml.bind-api -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple -->
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.12</version>
</dependency>
<!-- For the cookie things-->
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>2.0.SP1</version>
</dependency>
<!-- For the token things -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.1</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.1</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.containers.glassfish/jersey-gf-cdi -->
<dependency>
<groupId>org.glassfish.jersey.containers.glassfish</groupId>
<artifactId>jersey-gf-cdi</artifactId>
<version>2.14</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
I have a beans.xml after searching a bit this was recommended, but I am not sure how it works.
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
Other things I have read:
Creation of bindings (not sure how this would be implemented)
Something to do with a lot of config files.
I am not using SpringBoot or other fancy things. Only simple Jax-rs, Jersey 2.30.1 and some things regarding tokens like jwt.
By reading the error, it seems only that the injected User is not present, so the program has nothing there to inject, but if the class is present why id the DependencyUnsatisfied?
Have I defined the injection properly?
Have I defined the name bindings properly?
Have I used the right maven imports?
Can anyone help? Feel free to ask any further questions.
Thanks.
I am trying to access a welcome page after login using
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService customUserDetailsService;
#Autowired
private DataSource dataSource;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/resources/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.successForwardUrl("/welcome")
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/");
}
}
My UserDetailsService is:
#Service(value = "customUserDetailsService")
public class CustomUserDetailsServiceImpl implements UserDetailsService {
private static Logger logger = LoggerFactory.getLogger(CustomUserDetailsServiceImpl.class);
#Autowired
private UserService userService;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userService.findByEmail(email);
if (user == null) {
logger.error("User with email" + email + " not found.");
throw new UsernameNotFoundException("Oops! User not found with username: " + email);
} else {
logger.info("User {} successfully logged", user.getUsername());
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), getAuthorities(user));
}
}
private Collection<GrantedAuthority> getAuthorities(User user) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (UserProfile userProfile : user.getUserProfileSet()) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + userProfile.getType()));
}
return authorities;
}
}
The relevant bits of the controllers are:
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index(Principal principal) {
if (logger.isDebugEnabled()) {
logger.debug("getWelcome is executed");
System.out.println(principal);
}
return principal == null ? "homeNotSignedIn" : "welcome";
}
#GetMapping("/login")
public String login(Model model, String error, String logout) {
if (error != null)
model.addAttribute("error", "Your username and password is invalid.");
if (logout != null)
System.out.println(">>>>>>>>>>>>>>>>>>> LOGOUT <<<<<<<<<<<<<<<<<<");
model.addAttribute("message", "You have been logged out successfully.");
return "login";
}
#GetMapping("/welcome")
public String welcome(Model model) {
System.out.println(">>>>>>>>>>>>>>>>>>>>>>> WELCOME <<<<<<<<<<<<<<<<<<<<<<<");
return "welcome";
}
My pom.xml is:
<?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>ie.gtludwig.pa</groupId>
<artifactId>pa</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>PA</name>
<modules>
<module>core</module>
<module>engine</module>
</modules>
<properties>
<!-- JAVA -->
<version.java>1.8</version.java>
<java.version>${version.java}</java.version>
<jdk.version>${version.java}</jdk.version>
<maven.compiler.target>${version.java}</maven.compiler.target>
<maven.compiler.source>${version.java}</maven.compiler.source>
<!-- Generic properties -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- External Dependency Versions -->
<spring-boot.version>2.1.3.RELEASE</spring-boot.version>
<jedis.version>2.9.1</jedis.version>
<junit.version>4.12</junit.version>
<logback.version>1.2.3</logback.version>
<thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.3.0</thymeleaf-layout-dialect.version>
<flyway.version>5.2.0</flyway.version>
<mysql.version>8.0.16</mysql.version>
<h2database.version>1.4.199</h2database.version>
<sendgrid-java.version>4.3.0</sendgrid-java.version>
<commons-lang3.version>3.7</commons-lang3.version>
<commons-io.version>2.6</commons-io.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<!--WEB-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>${thymeleaf-layout-dialect.version}</version>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
<!--SECURITY-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--OPERATIONS-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--SERVER-->
<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>
<scope>provided</scope>
</dependency>
<!--DATABASE-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
<!--TEST DATABASE FOR TESTING PROCESSES AND RULES IN-MEMORY-->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2database.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.2.17.Final</version>
<scope>runtime</scope>
</dependency>
<!--EMAIL-->
<!-- https://mvnrepository.com/artifact/com.sendgrid/sendgrid-java -->
<dependency>
<groupId>com.sendgrid</groupId>
<artifactId>sendgrid-java</artifactId>
<version>${sendgrid-java.version}</version>
</dependency>
<!--DEV/TEST-->
<!--DEV TOOLS CONFLICT WITH JREBEL - DISABLE THIS IF JREBEL IS BEING USED -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>5.0.7.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<!-- Resource plugin to enable expanding properties from this file so that they can be exposed by the zone (E.g. #project.version#) -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<nonFilteredFileExtensions>
<!--font/binary files must be excluded from filtering or they will be corrupted-->
<nonFilteredFileExtension>woff2</nonFilteredFileExtension>
<nonFilteredFileExtension>woff</nonFilteredFileExtension>
<nonFilteredFileExtension>ttf</nonFilteredFileExtension>
<nonFilteredFileExtension>jks</nonFilteredFileExtension>
</nonFilteredFileExtensions>
<delimiters>
<delimiter>#</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<!-- Add our project version to the manifest file -->
<version>${project.version}</version>
<description>${project.description}</description>
</manifestEntries>
</archive>
<failOnMissingWebXml>false</failOnMissingWebXml>
<attachClasses>false</attachClasses>
</configuration>
<version>3.1.0</version>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>external</id>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>${spring-boot.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</profile>
</profiles>
What happens is:
When I access localhost:8080/core I get the correct homeNotSignedIn page, but there is no CSS or JS loaded from /resources/**.
I click a login redirection link to the login page (again without CSS or JS) and CAN login.
The logged username is displayed to the console, so the customUserDetailsService seems to be working alright, but I don't get redirected to /welcome.
At this point, I can only logout by typing /logout to the URL and it works and sends me to homeNotSignedIn.
I have been going back and forth at this and I can't seem to get it done.
What am I missing?
I have been going back and forth at this and I can't seem to get it done.
What am I missing?
EDIT
I managed to load CSS and JS by changing:
.antMatchers("/resources/**").permitAll()
to:
.antMatchers("/css/**", "/js/**").permitAll()
When you are using successForwardUrl you will be forwarded to the given URL, but not redirected.
That means that the URL in your browser will not change, however it will display the HTML in your welcome.html.
If you would like to redirect, you can use defaultSuccessUrl as mentioned on the spring-security documentation.
Then your security configuration would look like this.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/resources/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/welcome", true)
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/");
}
I have a tomcat server with different applications and each one has it's own log4j2 appender which logs to it's own file, each java class logs it's exceptions properly, and for the JSF exceptions such as unclosed tags, wrong class/method/property name I made a defaultexceptionhandler which logs the FacesExceptions, but for some reason I can't find it doesn't log at all.
Here are the specs of the enviroment
Ubuntu 18.04 and 16.04 (tried it on both just in case)
tomcat 8.0.47
oracle jdk 1.8.0_181
myfaces 2.3.1
log4j2 2.11.0
So the thing is that even while debugging the handle() while forcing exceptions by leaving tags open and so on, it does reach the breakpoints on the logger.error() call, but it just doesn't append to the file.
This is the declaration of the handler in the faces-config.xml of the project (I spared all the navigation cases and other junk, all of which works so far, I'll edit it all in if needed)
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<factory>
<exception-handler-factory>
gescoweb.tools.DefaultExceptionHandlerFactory
</exception-handler-factory>
</factory>
</faces-config>
This is the DefaultExceptionHandler.java
package gescoweb.tools;
import java.util.Iterator;
import javax.faces.FacesException;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerWrapper;
import javax.faces.context.FacesContext;
import javax.faces.event.ExceptionQueuedEvent;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
/**
*
*/
public class DefaultExceptionHandler extends ExceptionHandlerWrapper {
private ExceptionHandler wrapped;
public static final Logger LOGGER = LogManager.getLogger("gescoweb");
public DefaultExceptionHandler(ExceptionHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getWrapped() {
return this.wrapped;
}
/** Recibe la lista de excepciones y las procesa segĂșn su tipo.
* #throws FacesException
*/
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
Throwable t = i.next().getContext().getException();
if (t != null) {
LOGGER.error("Error inesperado.", new Exception(t));
} else {
LOGGER.error("", new Exception(t));
}
}
getWrapped().handle();
}
/**
* Tratamiento especial de otras excepciones.
* #param facesContext
* #param t
* #return
*/
protected String handleUnexpected(FacesContext facesContext, final Throwable t) {
if (t instanceof IllegalStateException) {
return "key.exception.IllegalStateException";
} else {
super.handle();
return "";
}
}
}
And this is the handler factory
package gescoweb.tools;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerFactory;
/**
*
*/
public class DefaultExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory parent;
public DefaultExceptionHandlerFactory(ExceptionHandlerFactory parent) {
this.parent = parent;
}
/**
* Crea los handlers modificados.
*
* #return
*/
#Override
public ExceptionHandler getExceptionHandler() {
ExceptionHandler eh = parent.getExceptionHandler();
eh = new DefaultExceptionHandler(eh);
return eh;
}
}
And finally this is the log4j2.xml config file
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Properties>
<Property name="log-path">${sys:catalina.base}/logs</Property>
<Property name="layout"> [%-5level] %d{yyyy-MM-dd HH:mm:ss} %c{1} - %msg%ex%n </Property>
</Properties>
<Appenders>
<!-- console appender -->
<Console name="console-log" target="SYSTEM_OUT">
<PatternLayout pattern="${layout}"/>
</Console>
<!-- logger gescoweb -->
<RollingFile name="gescoweb-log" fileName="${log-path}/gescoweb.log"
filePattern="${log-path}/gescoweb/$${date:yyyy-MM}/gescoweb-%d{dd}.log.gz">
<!-- log pattern -->
<PatternLayout pattern="${layout}"/>
<!-- set file size policy -->
<CronTriggeringPolicy schedule="0 0 12 */7 * ?"/>
</RollingFile>
<!-- logger catalina -->
<RollingFile name="catalina-log"
fileName="${log-path}/catalina.out"
filePattern="${log-path}/catalina/$${date:yyyy-MM}/catalina-%d{dd}.log.gz">
<PatternLayout pattern="${layout}"/>
<CronTriggeringPolicy schedule="0 0 12 */7 * ?"/>
</RollingFile>
<!-- logger localhost -->
<RollingFile name="localhost-log"
fileName="${log-path}/localhost.log"
filePattern="${log-path}/localhost/$${date:yyyy-MM}/localhost-%d{dd}.log.gz">
<PatternLayout pattern="${layout}"/>
<CronTriggeringPolicy schedule="0 0 12 */7 * ?"/>
</RollingFile>
<!-- logger manager -->
<RollingFile name="manager-log"
fileName="${log-path}/manager.log"
filePattern="${log-path}/manager/$${date:yyyy-MM}/manager-%d{dd}.log.gz">
<PatternLayout pattern="${layout}"/>
<CronTriggeringPolicy schedule="0 0 12 */7 * ?"/>
</RollingFile>
<!-- logger host-manager -->
<RollingFile name="host-manager-log"
fileName="${log-path}/host-manager.log"
filePattern="${log-path}/host-manager/$${date:yyyy-MM}/host-manager-%d{dd}.log.gz">
<PatternLayout pattern="${layout}"/>
<CronTriggeringPolicy schedule="0 0 12 */7 * ?"/>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="gescoweb" level="debug">
<AppenderRef ref="gescoweb-log"/>
</Logger>
<Logger name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost]"
level="info"
additivity="false">
<AppenderRef ref="localhost-log"/>
</Logger>
<Logger name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager]"
level="info"
additivity="false">
<AppenderRef ref="manager-log"/>
</Logger>
<Logger name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager]"
level="info"
additivity="false">
<AppenderRef ref="host-manager-log"/>
</Logger>
<Root level="info">
<AppenderRef ref="console-log"/>
<AppenderRef ref="catalina-log"/>
</Root>
</Loggers>
</Configuration>
Nevermind it, I imported the wrong log4j libraries in the DefaultExceptionHandler, changed the imports to
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
And everything works now...
I'm having a hard time getting Swagger UI to work with my dropwizard application. The annotated dropwizard app seems to generate the correct json documents but swagger UI is unabel to navigate it properly.
Main API page, generated at http://localhost:8080/v1/TimeService/api-docs:
{"apiVersion":"0.0","swaggerVersion":"1.2","apis":[{"path":"/times"}]}
Swagger displays that page correctly.
Detail information for the time resource generated at http://localhost:8080/v1/TimeService/api-docs/times:
{"apiVersion":"0.0","swaggerVersion":"1.2","basePath":"/v1/TimeService/*","resourcePath":"/times","produces":["application/json"],"apis":[{"path":"/times/","operations":[{"method":"GET","summary":"Retrieves time for given","notes":"timezone parameter is optional, defaults to GMT","type":"array","items":{"$ref":"Time"},"nickname":"getTime","parameters":[{"name":"timezone","description":"timezone","required":false,"items":{"type":"string"},"paramType":"query","allowMultiple":false}]}]}],"models":{"Time":{"id":"Time","properties":{"time":{"type":"string"}}}}}
Which looks good to me. But whenever, in the swagger UI, I click on any "times" hyperlink to display the details, nothing happens. It looks like swagger ui cannot resolve the link correctly.
My annotated resource, TimeResource.java:
package com.cgi.saas.bluprint;
import com.google.common.base.Optional;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
import com.wordnik.swagger.annotations.ApiParam;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
#Path("/times")
#Produces(MediaType.APPLICATION_JSON)
#Api("/times")
public class TimeResource {
private final String defaultTimezone;
public TimeResource(String defaultTimezone) {
this.defaultTimezone = defaultTimezone;
}
#GET
#ApiOperation(value = "Retrieves time for given",
notes = "timezone parameter is optional, defaults to GMT",
response = Time.class,
responseContainer = "List")
#Path("/")
public Time getTime(#ApiParam(value="timezone") #QueryParam("timezone") Optional<String> timezone) {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TimeZone timeZone = TimeZone.getTimeZone(timezone.or(defaultTimezone));
formatter.setTimeZone(timeZone);
String formatted = formatter.format(new Date());
return new Time(formatted);
}
}
The main service class, TimeService.java:
package com.cgi.saas.bluprint;
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration.Dynamic;
import org.eclipse.jetty.servlets.CrossOriginFilter;
import com.wordnik.swagger.config.ConfigFactory;
import com.wordnik.swagger.config.ScannerFactory;
import com.wordnik.swagger.config.SwaggerConfig;
import com.wordnik.swagger.jaxrs.config.DefaultJaxrsScanner;
import com.wordnik.swagger.jaxrs.listing.ApiDeclarationProvider;
import com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON;
import com.wordnik.swagger.jaxrs.listing.ResourceListingProvider;
import com.wordnik.swagger.jaxrs.reader.DefaultJaxrsApiReader;
import com.wordnik.swagger.reader.ClassReaders;
import io.dropwizard.Application;
import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.server.DefaultServerFactory;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
public class TimeService extends Application<TimezoneConfiguration> {
private void configureCors(Environment environment) {
final Dynamic filter = environment.servlets().addFilter("CORS",
CrossOriginFilter.class);
filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
filter.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,PUT,POST,DELETE,OPTIONS");
filter.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
filter.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
filter.setInitParameter("allowedHeaders",
"Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin");
filter.setInitParameter("allowCredentials", "true");
}
public static void main(String[] args) throws Exception {
new TimeService().run(args);
}
public void initialize(Bootstrap<TimezoneConfiguration> timezoneConfigurationBootstrap) {
timezoneConfigurationBootstrap.addBundle(new AssetsBundle("/assets/", "/", "index.html"));
timezoneConfigurationBootstrap.addBundle(new AssetsBundle("/assets/swagger", "/swagger", "index.html", "swagger"));
}
#Override
public void run(TimezoneConfiguration appConfig, Environment environment) throws Exception {
DefaultServerFactory sf = (DefaultServerFactory) appConfig.getServerFactory();
String rootPath = "/v1/" + appConfig.getDeployName() + "/*";
sf.setJerseyRootPath(rootPath);
configureCors(environment);
String defaultTimezone = appConfig.getDefaultTimezone();
TimeResource timeResource = new TimeResource(defaultTimezone);
environment.jersey().register(timeResource);
// Swagger Resource
environment.jersey().register(new ApiListingResourceJSON());
// Swagger providers
environment.jersey().register(new ApiDeclarationProvider());
environment.jersey().register(new ResourceListingProvider());
// Swagger Scanner, which finds all the resources for #Api Annotations
ScannerFactory.setScanner(new DefaultJaxrsScanner());
// Add the reader, which scans the resources and extracts the resource information
ClassReaders.setReader(new DefaultJaxrsApiReader());
// Set the swaggeonfigurationBootstrap
final SwaggerConfig swConfig = ConfigFactory.config();
swConfig.setBasePath(rootPath);
}
}
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>com.cgi.saas.bluprint</groupId>
<artifactId>Application2</artifactId>
<version>1.0</version>
<repositories>
<repository>
<id>sonatype-nexus-snapshots</id>
<name>Sonatype Nexus Snapshots</name>
<url>http://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<properties>
<dropwizard.version>0.9.0-SNAPSHOT</dropwizard.version>
</properties>
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-assets</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-jersey</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>com.wordnik</groupId>
<artifactId>swagger-jaxrs_2.10</artifactId>
<version>1.3.10</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-scala_2.10</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.wordnik</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.3.10</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.2</version>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.cgi.saas.bluprint.TimeService</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Rewrote it as follows:
Cloned the latest and greatest version of swagger-ui at https://github.com/swagger-api/swagger-ui.git
Copied dist under src/main/resources/swagger/assets
Updated pom.xml:
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.federecio</groupId>
<artifactId>dropwizard-swagger</artifactId>
<version>0.6</version>
</dependency>
Updated TimeService.java:
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration.Dynamic;
import org.eclipse.jetty.servlets.CrossOriginFilter;
import io.dropwizard.Application;
import io.dropwizard.server.DefaultServerFactory;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import io.federecio.dropwizard.swagger.SwaggerDropwizard;
public class TimeService extends Application<TimezoneConfiguration> {
private final SwaggerDropwizard swaggerDropwizard = new SwaggerDropwizard();
private void configureCors(Environment environment) {
final Dynamic filter = environment.servlets().addFilter("CORS",
CrossOriginFilter.class);
filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
filter.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,PUT,POST,DELETE,OPTIONS");
filter.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
filter.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
filter.setInitParameter("allowedHeaders",
"Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin");
filter.setInitParameter("allowCredentials", "true");
}
public static void main(String[] args) throws Exception {
new TimeService().run(args);
}
public void initialize(Bootstrap<TimezoneConfiguration> timezoneConfigurationBootstrap) {
swaggerDropwizard.onInitialize(timezoneConfigurationBootstrap);
}
#Override
public void run(TimezoneConfiguration appConfig, Environment environment) throws Exception {
DefaultServerFactory sf = (DefaultServerFactory) appConfig.getServerFactory();
String rootPath = "/v1/" + appConfig.getDeployName() + "/*";
sf.setJerseyRootPath(rootPath);
configureCors(environment);
String defaultTimezone = appConfig.getDefaultTimezone();
TimeResource timeResource = new TimeResource(defaultTimezone);
environment.jersey().register(timeResource);
swaggerDropwizard.onRun(appConfig, environment, "localhost");
}
}
The swagger ui is available at http://localhost:8080/v1/TimeService/swagger
I have tried this example: Richfaces example, the valueChange event only fires if I add a submit button and click it.
I assum one of my configuration is false, as the ajax request not working.
SelectBean class:
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
#ManagedBean(name = "selectsBean")
#RequestScoped
public class SelectsBean
{
private static final String[] FRUITS = { "", "Banana", "Cranberry", "Blueberry", "Orange" };
private static final String[] VEGETABLES = { "", "Potatoes", "Broccoli", "Garlic", "Carrot" };
private String currentItem = "";
private String currentType = "";
private List<SelectItem> firstList = new ArrayList<SelectItem>();
private List<SelectItem> secondList = new ArrayList<SelectItem>();
public SelectsBean()
{
SelectItem item = new SelectItem("", "");
firstList.add(item);
item = new SelectItem("fruits", "Fruits");
firstList.add(item);
item = new SelectItem("vegetables", "Vegetables");
firstList.add(item);
for (int i = 0; i < FRUITS.length; i++)
{
item = new SelectItem(FRUITS[i]);
}
}
public List<SelectItem> getFirstList()
{
return firstList;
}
public List<SelectItem> getSecondList()
{
return secondList;
}
public static String[] getFRUITS()
{
return FRUITS;
}
public static String[] getVEGETABLES()
{
return VEGETABLES;
}
public void valueChanged(ValueChangeEvent event)
{
secondList.clear();
if (null != event.getNewValue())
{
String[] currentItems;
if (((String) event.getNewValue()).equals("fruits"))
{
currentItems = FRUITS;
}
else
{
currentItems = VEGETABLES;
}
for (int i = 0; i < currentItems.length; i++)
{
SelectItem item = new SelectItem(currentItems[i]);
secondList.add(item);
}
}
}
public String getCurrentType()
{
return currentType;
}
public void setCurrentType(String currentType)
{
this.currentType = currentType;
}
public String getCurrentItem()
{
return currentItem;
}
public void setCurrentItem(String currentItem)
{
this.currentItem = currentItem;
}
/**
* #param args
*/
public static void main(String[] args)
{
String webappDirLocation = "WebContent/";
// The port that we should run on can be set into an environment variable
// Look for that variable and default to 8080 if it isn't there.
String webPort = System.getenv("PORT");
if (webPort == null || webPort.isEmpty())
{
webPort = "8080";
}
Server server = new Server(Integer.valueOf(webPort));
WebAppContext root = new WebAppContext();
root.setContextPath("/");
root.setDescriptor(webappDirLocation + "/WEB-INF/web.xml");
root.setResourceBase(webappDirLocation);
// Parent loader priority is a class loader setting that Jetty accepts.
// By default Jetty will behave like most web containers in that it will
// allow your application to replace non-server libraries that are part of the
// container. Setting parent loader priority to true changes this behavior.
// Read more here: http://wiki.eclipse.org/Jetty/Reference/Jetty_Classloading
root.setParentLoaderPriority(true);
server.setHandler(root);
try
{
server.start();
server.join();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
index.xhtml:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
<h:form>
<h:selectOneMenu value="#{selectsBean.currentType}" valueChangeListener="#{selectsBean.valueChanged}">
<f:selectItems value="#{selectsBean.firstList}" />
<a4j:ajax event="valueChange" render="second" execute="#this" />
</h:selectOneMenu>
<a4j:outputPanel id="second" layout="block">
<h:selectOneMenu value="#{selectsBean.currentType}" rendered="#{not empty selectsBean.currentType}">
<f:selectItems value="#{selectsBean.secondList}" />
</h:selectOneMenu>
</a4j:outputPanel>
</h:form>
</ui:composition>
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>JSFExamples</groupId>
<artifactId>JSFExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Jetty -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>7.6.0.v20120127</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>7.6.0.v20120127</version>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-2.1-glassfish</artifactId>
<version>2.1.v20100127</version>
</dependency>
<!-- Servlet Faces API -->
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.2.7</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.2.7</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.primefaces.extensions</groupId>
<artifactId>all-themes</artifactId>
<version>1.0.8</version>
</dependency>
<!-- RichFaces libraries -->
<dependency>
<groupId>org.richfaces.ui</groupId>
<artifactId>richfaces-components-ui</artifactId>
<version>4.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.richfaces.core</groupId>
<artifactId>richfaces-core-impl</artifactId>
<version>4.1.0.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<webXml>WebContent\WEB-INF\web.xml</webXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>JSFExamples</display-name>
<welcome-file-list>
<welcome-file>index.xhtml</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<context-param>
<description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
<param-value>resources.application</param-value>
</context-param>
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
</web-app>
faces-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
version="2.2">
<managed-bean>
<managed-bean-name>selectsBean</managed-bean-name>
<managed-bean-class>SelectsBean</managed-bean-class>
<managed-bean-scope>none</managed-bean-scope>
</managed-bean>
</faces-config>
Your XHTML produces broken HTML output.
You forgot the <html><h:head><h:body> in XHTML.
Particularly the <h:head> part is mandatory for JSF in order to auto-include JavaScript and CSS files associated with some components, such as <a4j:ajax>.
Fix it accordingly. The index.xhtml should look like this:
<!DOCTYPE html>
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
>
<h:head>
<title>Put your title here.</title>
</h:head>
<h:body>
<h:form>
Put your original form here.
</h:form>
</h:body>
</html>
See also:
How to include another XHTML in XHTML using JSF 2.0 Facelets?
commandButton/commandLink/ajax action/listener method not invoked or input value not updated
Unrelated to the concrete problem, that JSF 1.x way of of <managed-bean> declaration in faces-config.xml is totally unnecessary given that you already have declared it via JSF 2.x flavored #ManagedBean annotation. Make sure that you focus on JSF 2.x targeted resources while learning and seeking for answers, not on JSF 1.x targeted ones, else it will only confuse you.