I am learning Spring Java Framework, but have the problem with the favicon. I want to get ordinary favicon in the simplest way. I placed the favicon.ico file like this src/main/resources/static/favicon.ico, also tried in just resources folder.
When I did a deployment to a remote GlassFish 5.1 web server the error happened:
[2020-07-18T02:45:52.695+0200] [glassfish 5.1] [WARNING] [] [org.springframework.web.servlet.PageNotFound] [tid: _ThreadID=696 _ThreadName=http-listener-2(1)] [timeMillis: 1595033152695] [levelValue: 900] [[ No mapping for GET /favicon.ico]]
And no favicon was present on a website.
At the remote the favicon can be found at this location:
./glassfish5/glassfish/domains/domain1/applications/HelloSpring-1.0-SNAPSHOT/WEB-INF/classes/static/favicon.ico
Here you are my files
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>ga.rekano</groupId>
<artifactId>HelloSpring</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Config.java
package app;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
#Configuration
#ComponentScan("app")
public class Config extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{Config.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
Hello.java
package app;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class Hello {
#GetMapping("/sayHello")
public String sayHello() {
return "Hello Spring";
}
}
How to get favicon visible?
edit
My question is related to the configuration without files web.xml and mvc-dispatcher-servlet.xml so the answers for questions where these files exist don't solve my problem. I have the very simple application that works and I believe that it is not necessary to add special xml files just to add the favicon. But of course I am a newbie so can be wrong. But on the other hand it is very strange because there are some tutorials where the problem looks like very simple: https://www.baeldung.com/spring-boot-favicon
So the question could be why this simplest method doesn't work in my case?
Related
I'm trying to build a Backend Service project using the example from the site
Using Spring Cloud Gateway with OAuth 2.0 Patterns
Here is the repository itself
backend
Added dependencies
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>ru.test.gw.oauth.resource</groupId>
<artifactId>backresource</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>backresource</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>14</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
I moved the properties from the quotes-application.properties file to this project
server.port=11002
# Resource server settings
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=http://localhost:8484/auth/realms/demo/protocol/openid-connect/token/introspect
spring.security.oauth2.resourceserver.opaquetoken.client-id=gateway
spring.security.oauth2.resourceserver.opaquetoken.client-secret=dfdslksfkljweewrfsd
Added a class
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
import reactor.core.publisher.Mono;
// Custom ReactiveTokenIntrospector to map realm roles into Spring GrantedAuthorities
public class KeycloakReactiveTokenInstrospector implements ReactiveOpaqueTokenIntrospector {
private final ReactiveOpaqueTokenIntrospector delegate;
public KeycloakReactiveTokenInstrospector(ReactiveOpaqueTokenIntrospector delegate) {
this.delegate = delegate;
}
#Override
public Mono<OAuth2AuthenticatedPrincipal> introspect(String token) {
return delegate.introspect(token)
.map( this::mapPrincipal);
}
protected OAuth2AuthenticatedPrincipal mapPrincipal(OAuth2AuthenticatedPrincipal principal) {
return new DefaultOAuth2AuthenticatedPrincipal(
principal.getName(),
principal.getAttributes(),
extractAuthorities(principal));
}
protected Collection<GrantedAuthority> extractAuthorities(OAuth2AuthenticatedPrincipal principal) {
//
Map<String,List<String>> realm_access = principal.getAttribute("realm_access");
List<String> roles = realm_access.getOrDefault("roles", Collections.emptyList());
List<GrantedAuthority> rolesAuthorities = roles.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
Set<GrantedAuthority> allAuthorities = new HashSet<>();
allAuthorities.addAll(principal.getAuthorities());
allAuthorities.addAll(rolesAuthorities);
return allAuthorities;
}
}
And the main class of the project
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.oauth2.server.resource.introspection.NimbusReactiveOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
import ru.test.gw.oauth.resource.backresource.security.KeycloakReactiveTokenInstrospector;
#SpringBootApplication
//#PropertySource("classpath:quotes-application.properties")
#EnableWebFluxSecurity
public class BackresourceApplication {
public static void main(String[] args) {
SpringApplication.run(BackresourceApplication.class, args);
}
#Bean
public SpringOpaqueTokenIntrospector keycloakIntrospector(OAuth2ResourceServerProperties props) {
NimbusReactiveOpaqueTokenIntrospector delegate = new NimbusReactiveOpaqueTokenIntrospector(
props.getOpaquetoken().getIntrospectionUri(),
props.getOpaquetoken().getClientId(),
props.getOpaquetoken().getClientSecret());
return new KeycloakReactiveTokenInstrospector(delegate);
}
}
And in this class I get an error on SpringOpaqueTokenIntrospector, writes that it is not defined. Although all the imports completely coincide with the training example.
If I add a dependency that the IDE tells me to
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
, then I get an error
Type mismatch: cannot convert from KeycloakReactiveTokenInstrospector to SpringOpaqueTokenIntrospector
What's the problem here? Is there some kind of dependency missing?
I completely repeated the structure of the project from the training material.
So far, I would like to build a project without errors.
Marcus is right in his comment, your keycloakIntrospector #Bean type should be ReactiveOpaqueTokenIntrospector (and not SpringOpaqueTokenIntrospector as declared in your conf)
Few facts:
SpringReactiveOpaqueTokenIntrospector is a ReactiveOpaqueTokenIntrospector but SpringOpaqueTokenIntrospector isn't
your KeycloakReactiveTokenInstrospector is (implements) a ReactiveOpaqueTokenIntrospector too but is neither a SpringReactiveOpaqueTokenIntrospector, SpringOpaqueTokenIntrospector nor OpaqueTokenIntrospector
Side notes
Introspection VS JWT decoding
Keycloak issues JWTs. JWT decoding is far more efficient than introspection: resource-server needs to fetch public-key only once from authorization-server to validate all incoming JWTs when introspection requires to submit access-token to authorization-server for each and every incoming request.
Also, you might not be able to implement multi-tenant scenarios with introspection: how to figure out by which issuer (Keycloak instance or realm) an opaque token was emitted? => you would have to "try" introspection on each issuer until one responds positively :/
Overriding introspector VS providing an authentication converter
If you switch to spring-security 5.8 or higher, customizing introspection is easier: you don't have to override the all introspector but can just provide a ReactiveOpaqueTokenAuthenticationConverter bean instead:
http.oauth2ResourceServer().opaqueToken().authenticationConverter(
(String introspectedToken, OAuth2AuthenticatedPrincipal authenticatedPrincipal) ->
new BearerTokenAuthentication(...));
This bean is called after introspection was successfuly completed (and token attributes retrieved) but before Authentication is instanciated and put in security-context which allows you to just map authorities from any attribute you like or completely switch the authentication implementation.
Simplifying your resource-server configuration
I host a set of libs to ease OAuth2 resource-server testing and configuration. There are various spring-boot starters depending on introspection or JWT decoding is used into servlet or reactive apps.
According to your case (reactive app with introspection), you should have a look at this sample with BearerTokenAuthentication and this other one with a custom authentication.
Configuration can be as simple as:
#EnableReactiveMethodSecurity
#Configuration
public class SecurityConfig {
}
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://localhost:8443/realms/master/protocol/openid-connect/token/introspect
spring.security.oauth2.resourceserver.opaquetoken.client-id=spring-addons-confidential
spring.security.oauth2.resourceserver.opaquetoken.client-secret=change-me
com.c4-soft.springaddons.security.issuers[0].location=https://localhost:8443/realms/master
com.c4-soft.springaddons.security.issuers[0].authorities.claims=realm_access.roles,resource_access.spring-addons-public.roles,resource_access.spring-addons-confidential.roles
# this is probably too permissive, addapt to your needs
com.c4-soft.springaddons.security.cors[0]=/**
<dependency>
<groupId>com.c4-soft.springaddons</groupId>
<artifactId>spring-addons-webflux-introspecting-resource-server</artifactId>
<version>6.0.3</version><!-- warning, this version goes with spring-boot 3.0.0-RC1 -->
</dependency>
I should probably mention that Spring is my first framework that I am learning for Java.
What I am trying to do is to get the JDBC working for me using the Spring Data JDBC dependency I downloaded from spring.io. (2.6.4) I followed a tutorial on YouTube where the guy demonstrated it with something similar to the following code. (Should also mention the code he used was for Spring version 2.2 but I'm not sure where else to find right code for 2.6.4)
There is also an UnsatisfiedDependencyException I keep getting where it says:
Error creating bean with name 'applicationRunner' defined in com.example.demo.DemoApplication: Unsatisfied dependency expressed through method 'applicationRunner' parameter 0;
I do not know why this is happening. But here is the actual code:
package com.example.demo;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
ApplicationRunner applicationRunner(StudentRepo studentRepo) {
return args -> {
var s1 = Student.createStudent("John", "Doe");
var s2 = Student.createStudent("Jane", "Doe");
System.out.println(studentRepo.save(s1));
System.out.println(studentRepo.save(s2));
System.out.println(studentRepo.findByFName("John"));
};
}
}
CRUD Repository interface:
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface StudentRepo extends CrudRepository<Student, String> {
#Query("SELECT * FROM Students WHERE name = :name")
List<Student> findByFName(#Param("name") String fName);
}
schema.sql file:
CREATE TABLE Students (
id VARCHAR(50) IDENTITY PRIMARY KEY,
firstName VARCHAR(50),
lastName VARCHAR(50),
rank VARCHAR(50)
);
I think it's also important for me to include pom.xml file: (Though it should also be mentioned that the pom.xml file didn't originally include the hsqldb dependency. I added that in myself.
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Edit:
Exception after including #Repository in the CRUD repository interface:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'applicationRunner' defined in com.example.demo.DemoApplication: Unsatisfied dependency expressed through method 'applicationRunner' parameter 0
Exception after replacing #Repository with #NoRepositoryBean:
2022-03-23 18:30:06.531 INFO 98567 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-03-23 18:30:06.556 ERROR 98567 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
APPLICATION FAILED TO START
Description:
Parameter 0 of method applicationRunner in com.example.demo.DemoApplication required a bean of type 'com.example.demo.StudentRepo' that could not be found.
Action:
Consider defining a bean of type 'com.example.demo.StudentRepo' in your configuration.
Process finished with exit code 1
try modifying the bean:
#Bean
public CommandLineRunner demo(StudentRepo studentRepo) {
return (args) -> {
studentRepo.save(new Student("Jack", "Bauer"));
studentRepo.save(new Student("Chloe", "O'Brian"));
}
you can consult the guide https://spring.io/guides/gs/accessing-data-jpa/
The problem lies in the schema.sql script. An IDENTITY column in HSQLDB has to be of the type INTEGER or BIGINT (see documentation at http://www.hsqldb.org/doc/1.8/guide/ch09.html, section CREATE TABLE). All other errors are just subsequent errors.
I have been checking all of my code for hours now and i just can't seem to find tthe error in here
it keeps throwing org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'imageRepository': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Not a managed type: class entitiys.Image
my controller looks like this:
package imageSearcher;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class AppController {
public static void main(String[] args) {
SpringApplication.run(AppController.class, args);
}
}
this is what the Image entity looks like:
package entitiys;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity(name = "image")
public class Image {
#Id
#Column(name = "id")
public Integer id;
#Column(name= "imageURL")
public String imageURL;
}
The image mapping looks as following
package imageSearcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import entitiys.Image;
#CrossOrigin(origins = "*", allowedHeaders = "*")
#RestController
public class ImageController {
#Autowired
private ImageRepository imageRep;
#GetMapping(path="/all")
public Iterable<Image> index() {
return imageRep.findAll();
}
#GetMapping(path = "/all/URL")
public Iterable<String> AllURL() {
return imageRep.findAllURL();
}
}
the imahe CrudRepository looks like this:
package imageSearcher;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import entitiys.Image;
#Repository
public interface ImageRepository extends CrudRepository<Image, Integer>{
#Query(value = "select * from Image", nativeQuery = true)
Iterable<Image> findAll();
#Query(value = "select Image.imageURL from Image", nativeQuery = true)
Iterable<String> findAllURL();
}
this is the 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<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>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Use MySQL Connector-J -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
and this is the application.properties:
server.port=91
spring.datasource.url=jdbc:mysql://localhost:3306/gip2021
spring.datasource.username=root
spring.datasource.password=
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.naming_startegy=org.hibernate.cfg.EJB3NamingStrategy
spring.jpa.hibernate.ddl-auto=none
I have no clue of what is wrong here i hope someone can help me because this is for my final project and even my Teacher can't see the issue
also here is the database script for if you need it:
create table Image(
id integer unsigned auto_increment primary key,
imageURL varchar(255)
);
insert into image (imageURL) values
("test"),
("test2");
for anyone wanting the git repo:
https://github.com/michiel2003/GIP2021.git
Just moved everything to the same package and it worked my teacher told me to put the entitiys in a different package and that's not what you are supposed to do
The from spring boot documentation for #SpringBootApplication :
Many Spring Boot developers always have their main class annotated
with #Configuration, #EnableAutoConfiguration and #ComponentScan.
Since these annotations are so frequently used together (especially if
you follow the best practices above), Spring Boot provides a
convenient #SpringBootApplication alternative.
The #SpringBootApplication annotation is equivalent to using
#Configuration, #EnableAutoConfiguration and #ComponentScan with their
default attributes: [...]
and #ComponentScan:
If specific packages are not defined, scanning will occur from the
package of the class that declares this annotation.
So only imageSearcher package is being scanned. You need to rearrange you packages or change #SpringBootApplication annotation to
#SpringBootApplication(scanBasePackages = {"imageSearcher","entitiys"})
EDIT:
If your teacher told you to move it to different package maybe he wanted you to make workaround for just like I proposed
created the pull request: https://github.com/michiel2003/GIP2021/pull/1 that will fix the bug, you can simply merge it :)
agreeing with every points that have already been mentioned above regarding #ComponentScan and adding additional things that caught my eyes:
this bug can be easily reproduced by adding a dummy loadContext test, this test will be generated when you start bootstrapping a spring boot application by using spring initializr, which is always the best way to start spring booting
I know test experience has always been treated as an overhead for junior developers, but it is worth it if you want to go down the road to a senior :)
when looking into your git histories: please do not simply delete getter and setters just because they look like template code, if you feel comfortable you can try adding lombok
I upgrade my Spring boot version from 2.0.5.RELEASE to 2.1.8.RELEASE (so Spring Integration from 5.0 to 5.1) and the automatic type casting inside integration flow doesn't work anymore. I am used to define a set of #IntegrationConverter components and automatic casting with the operation transform(Type.class, p -> p) inside integration flow code but with the new version it seems to be broken.
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.grorg</groupId>
<artifactId>grointegration</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>grointegration</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ip</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
And the Main.java file:
package org.grorg.grointegration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.convert.converter.Converter;
import org.springframework.integration.config.IntegrationConverter;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.Transformers;
import org.springframework.integration.ip.dsl.Tcp;
import org.springframework.stereotype.Component;
class Test {
#Override
public String toString() {
return "test";
}
}
#Component
#IntegrationConverter
class Convert implements Converter<String, Test> {
#Override
public Test convert(String s) {
return new Test();
}
}
#SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(GrointegrationApplication.class, args);
}
#Bean
public IntegrationFlow server() {
return IntegrationFlows
.from(Tcp.inboundGateway(Tcp.netServer(1234)))
.transform(Transformers.objectToString())
.transform(Test.class, id -> id) // in 2.1 I could use .convert(Test.class) but the problem is the same
.log()
.handle((p, h) -> "OK")
.get();
}
}
Use with a shell:
telnet localhost 1234
> test
> OK
[...]
With the previous version (2.0.5.RELEASE) the program work nicely like previously, but with the new version (2.1.8.RELEASE) I get this error (and no "OK" response):
org.springframework.integration.handler.ReplyRequiredException: No reply produced by handler 'server.org.springframework.integration.config.ConsumerEndpointFactoryBean#1', and its 'requiresReply' property is set to true.
[...]
What I have found is that the ConversionService has been remplaced by MessageConverter and now Jackson is used to transform message from one type to another.
Am I wrongly using type casting with integration flow? Do you have a new solution for casting object with the new version? Or is this just a regression?
Thanks in advance!
This is a bug in Spring Integration.
We just don't use a proper ConversionService in the GenericMessageConverter.
Please, raise a GH issue on the matter.
Meanwhile a workaround for you is like this:
#Bean(name = IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME)
public static ConfigurableCompositeMessageConverter configurableCompositeMessageConverter(
#Qualifier(IntegrationUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME) ConversionService conversionService) {
return new ConfigurableCompositeMessageConverter(
Collections.singleton(new GenericMessageConverter(conversionService)));
}
So, we override whatever is configured by the framework and inject a proper IntegrationUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME bean which is supplied with your #IntegrationConverter component.
I have setup swagger on jersey 2 and grizzly (no web.xml). I am able to access the swagger page however my API resources do not appear.
My main file is seen below
`
package com.beraben.jersey.test;
import com.wordnik.swagger.jaxrs.config.BeanConfig;
import java.net.URI;
import org.glassfish.grizzly.http.server.CLStaticHttpHandler;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
/**
*
* #author Evan
*/
public class jerseyTestMain {
/**
* #param args the command line arguments
*/
public static final String BASE_URI = "http://localhost:8080/myapp/";
public static HttpServer startServer() {
// create a resource config that scans for JAX-RS resources and providers
// in com.example.rest package
final ResourceConfig rc = new ResourceConfig().packages("com.beraben.jersey.test", "com.wordnik.swagger.jersey.listing");
BeanConfig config = new BeanConfig();
config.setResourcePackage("com.beraben.jersey.test");
config.setVersion("1.0.0");
config.setScan(true);
// create and start a new instance of grizzly http server
// exposing the Jersey application at BASE_URI
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
}
public static void main(String[] args) throws InterruptedException {
final HttpServer server = startServer();
CLStaticHttpHandler staticHttpHandler = new CLStaticHttpHandler(jerseyTestMain.class.getClassLoader(), "swagger-ui/");
server.getServerConfiguration().addHttpHandler(staticHttpHandler, "/docs");
Object syncObj = new Object();
synchronized (syncObj) {
syncObj.wait();
}
}
}
`
I also have a API setup seen below
package com.beraben.jersey.test;
import com.wordnik.swagger.annotations.Api;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
*
* #author Evan
*/
#Path("myresource")
#Api(value = "/myresource")
public class MyResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getIt(){
return "Got it!";
}
}
I have no problem using the API it returns correctly.
But for some reason I cant get swagger to show details about the API calls, is there something i need to do more to get it to show details about the existing API in my code?
My static files are copied from the sample project
jersey2-grizzly2-swagger-demo
Also for reference, here is my pom file (not slight difference from the demo project is I don't use dependencyManagment to get jersey-bom instead i reference it directly).
<?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.beraben</groupId>
<artifactId>jersey-test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.archetypes</groupId>
<artifactId>jersey-quickstart-grizzly2</artifactId>
<version>2.22.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>2.22.2</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
<version>2.22.2</version>
</dependency>
<dependency>
<groupId>com.wordnik</groupId>
<artifactId>swagger-jersey-jaxrs_2.10</artifactId>
<version>1.3.13</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>
After looking at the google forms, it turns out that standalone version of swagger does not actually work.
I created a separate maven web app and added my jersey project as dependencies. This worked after fiddling around with the swagger version that matched the jersey version I was using.