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.
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 followed a simple java server setup guide.
So i have something like this:
MyApp.java
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
#ApplicationPath("/")
public class MyApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> h = new HashSet<>();
h.add( HelloWorld.class );
return h;
}
}
and HelloWord.java
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
#Path("/helloworld")
public class HelloWorld {
#Context
private HttpServletRequest httpRequest;
#GET
#Produces("text/plain")
public String getClichedMessage() {
return "Hello, World!";
}
#POST
#Consumes()
#Produces("text/plain")
public String doThis(String x) {
System.out.println(x);
//....
}
}
The incoming request is of type
content-type = multipart/form-data; boundary=somerandstuff
and looks like :
--somerandstuff
Content-Disposition: form-data; name="somedata1"
data text 1here
--somerandstuff
Content-Disposition: form-data; name="somedata2"
more data text here
--somerandstuff
Content-Disposition: form-data; name="numberoffiles"
3
--somerandstuff
Content-Disposition: form-data; name="file1"; filename="firstfile"
Content-Type: application/octet-stream
{unreadable symbols thing here}
.
.
. 2 more files like above
How do I go about actually reading the data? I've tried many things and can't get it to work. I tried using httpRequest.getParameterMap() and httpRequest.getParameterNames() and printing everything but nothing prints. I can't seem to even access the post body data. The only things I can access are the headers using httpRequest.getHeaderNames().
What I want to do is store each file in a File object (or something similar) and then download it. How would i go about doing that? I've looked at a few posts on this topic but when I try implementing the solutions, they don't seem to work.
Edit: here is 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.serverproject</groupId>
<artifactId>serverProject</artifactId>
<version>1.0-SNAPSHOT</version>
<name>serverProject</name>
<packaging>war</packaging>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<junit.version>5.6.2</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>8.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.31</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.31</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.31</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.31</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.0</version>
</plugin>
</plugins>
</build>
</project>
Ok so what you have is multipart data, which is most commonly used to send multiple files and metadata (or other data) along with those files. There is no standard JAX-RS support for multipart, but you are using Jersey (a JAX-RS implementation) and Jersey does have support for multipart. The first thing you need to do is add a new dependency to your pom.xml file
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>2.31</version>
</dependency>
The next thing you need to do is register this feature with your application. Since you are using an Application subclass, you would add the MultiPartFeature class to the classes in getClasses()
#ApplicationPath("/")
public class MyApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> h = new HashSet<>();
h.add(HelloWorld.class);
h.add(MultiPartFeature.class);
return h;
}
}
Now we can use this feature. In your resource method, what we will do is declare each part using Jersey's #FormDataParam annotation. As you can see in the multipart entity, each part has a name
Content-Disposition: form-data; name="somedata1"
Here the name of this part is somedata1. So we will add a parameter for this part. Also notice I will add #Consumes(MediaType.MULTIPART_FORM_DATA) as that is the content-type our method will be consuming.
#POST
#Path("upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response fileUpload(#FormDataParam("somedata1") String someData1) {
...
}
Based on what type of data is expected, that's how you will determine the parameter type. The type of somedata1 is just plain text, that's why I used a String. But the numeroffiles part is a number, so I can use an int for that part
public Response fileUpload(#FormDataParam("somedata1") String someData1,
#FormDataParam("numberoffiles") int numberOfFiles) {
...
}
The firtfile part is going to be a binary file. So what we can use an InputStream, File, or byte[] parameter. We can also add an additional parameter of type FormDataContentDisposition which will give us some info about this file. For this example, I will use an InputStream. Just google "how to save an InputStream to a file in Java"
public Response fileUpload(#FormDataParam("somedata1") String someData1,
#FormDataParam("numberoffiles") int numberOfFiles,
#FormDataParam("file1") InputStream firstFile,
#FormDataParam("file1") FormDataContentDisposition fdcd) {
...
String fileName = fdcd.getFileName();
}
UPDATE
If the number of files is unknown, instead of decalring all these separate parts, what you can do is just declare one parameter of type FormDataMultiPart and programmatically extract each part
public Response fileUpload(FormDataMultiPart multipart) {
Map<String,List<FormDataBodyPart>> bodyParts = multipart.getFields();
FormDataBodyPart someDataPart = multipart.getField("somedata1");
String someData = someDataPart.getValueAs(String.class);
}
You will need to traverse all the FormDataBodyParts and you use the bodyPart.getValueAs(Class) method to extract the data of the body part. For the files, you would use getValueAs(InputStream.class).
And that's pretty much it. That's how you work with multipart in Jersey. For more info, you can read the complete docs
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.
While trying to detectIntent from text I have received a below exception (project-id was replaced). Cannot find anything in google related to DesignTimeAgent issue. Any help is appreciated.
Exception :
There was an unexpected error (type=Internal Server Error, status=500).
io.grpc.StatusRuntimeException: NOT_FOUND: com.google.apps.framework.request.NotFoundException: No DesignTimeAgent found for project 'MY_PROJECT_ID'.
com.google.api.gax.rpc.NotFoundException: io.grpc.StatusRuntimeException: NOT_FOUND: com.google.apps.framework.request.NotFoundException: No DesignTimeAgent found for project 'MY_PROJECT_ID'.
at com.google.api.gax.rpc.ApiExceptionFactory.createException(ApiExceptionFactory.java:45)
at com.google.api.gax.grpc.GrpcApiExceptionFactory.create(GrpcApiExceptionFactory.java:72)
at com.google.api.gax.grpc.GrpcApiExceptionFactory.create(GrpcApiExceptionFactory.java:60)
at com.google.api.gax.grpc.GrpcExceptionCallable$ExceptionTransformingFuture.onFailure(GrpcExceptionCallable.java:95)
at com.google.api.core.ApiFutures$1.onFailure(ApiFutures.java:61)
at com.google.common.util.concurrent.Futures$CallbackListener.run(Futures.java:1015)
at com.google.common.util.concurrent.DirectExecutor.execute(DirectExecutor.java:30)
at com.google.common.util.concurrent.AbstractFuture.executeListener(AbstractFuture.java:1137)
at com.google.common.util.concurrent.AbstractFuture.complete(AbstractFuture.java:957)
at com.google.common.util.concurrent.AbstractFuture.setException(AbstractFuture.java:748)
at io.grpc.stub.ClientCalls$GrpcFuture.setException(ClientCalls.java:493)
at io.grpc.stub.ClientCalls$UnaryStreamToFuture.onClose(ClientCalls.java:468)
at io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
at io.grpc.internal.CensusStatsModule$StatsClientInterceptor$1$1.onClose(CensusStatsModule.java:684)
...
Suppressed: com.google.api.gax.rpc.AsyncTaskException: Asynchronous task failed
at com.google.api.gax.rpc.ApiExceptions.callAndTranslateApiException(ApiExceptions.java:57)
at com.google.api.gax.rpc.UnaryCallable.call(UnaryCallable.java:112)
at com.google.cloud.dialogflow.v2.SessionsClient.detectIntent(SessionsClient.java:245)
at com.google.cloud.dialogflow.v2.SessionsClient.detectIntent(SessionsClient.java:184)
at com.my.microservices.controllers.DialogFlowRestController.test(DialogFlowRestController.java:35)
...
Caused by: io.grpc.StatusRuntimeException: NOT_FOUND: com.google.apps.framework.request.NotFoundException: No DesignTimeAgent found for project 'MY_PROJECT_ID'.
at io.grpc.Status.asRuntimeException(Status.java:533)
... 23 more
Rest controller to trigger detectIntent test (as https://localhost:8080/api/test):
package com.my.microservices.controllers;
import com.google.cloud.dialogflow.v2.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
#RestController
#RequestMapping("/api")
public class DialogFlowRestController {
private static final Logger logger = LoggerFactory.getLogger(DialogFlowRestController.class);
private String userText = "Weather forecast for today in Berlin";
private final String LANG_CODE = "en-US";
private final String PROJECT_ID = System.getenv().get("GOOGLE_CLOUD_PROJECT");
private String sessionId = UUID.randomUUID().toString();
#GetMapping(value = "/test", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String test() throws Exception {
logger.info("test was called");
try (SessionsClient sessionsClient = SessionsClient.create()) {
SessionName session = SessionName.of(PROJECT_ID, sessionId);
TextInput.Builder textInput = TextInput.newBuilder().setText(userText).setLanguageCode(LANG_CODE);
QueryInput queryInput = QueryInput.newBuilder().setText(textInput).build();
DetectIntentResponse response = sessionsClient.detectIntent(session, queryInput);
return response.toString();
}
}
}
Provided environment variables:
GOOGLE_CLOUD_PROJECT=MY_PROJECT_ID
GOOGLE_APPLICATION_CREDENTIALS=/Users/me/.my/pk.json
Spring-Boot 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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.my.microservices</groupId>
<artifactId>dialog-flow-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>dialog-flow-test</name>
<description>Demo project with Dialog Flow</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Dialogflow API Client Library for Java -->
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-dialogflow</artifactId>
<version>v2-rev81-1.25.0</version>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-dialogflow</artifactId>
<version>0.103.0-alpha</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
OK, I have figured it out.
The above misleading error message was caused by the use of a wrong PROJECT_ID. My mistake was, that first I have created a new Project with a Service Account, gave to it dialogflow API rights and later I have tried to use its name and related PK JSON for my detectIntent calls. BUT it was not linked with Dialogflow Project ID.
So, to fix it I have started with a default Project ID, which was given to my new Dialogflow Agent and following instruction https://dialogflow.com/docs/reference/v2-auth-setup I have created a new Project with a Service Account which fit my Dialogflow Agent.
In my case I needed to move my Agent to Global serving from EU-W1
In my code it is giving an error when trying to use the getPage method. I'm not sure what the problem is, any ideas of perhaps where I should be looking?
I've tried adding the class under "found" to my import but it doesn't seem to exist. This is my first time posting a java problem so please let me know if more information is needed.
The error:
java: incompatible types
required: com.gargoylesoftware.htmlunit.html.HtmlPage
found: com.gargoylesoftware.htmlunit.Page
My code
package com.buth.trabot;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.lang.Object;
import com.gargoylesoftware.htmlunit.*;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.WebClient;
import static org.junit.Assert.*;
/**
* Created with IntelliJ IDEA.
* User: Tyler
* Date: 9/28/13
* Time: 7:28 PM
* To change this template use File | Settings | File Templates.
*/
public class Main {
public static void main (String [] args) {
final WebClient webClient = new WebClient();
final HtmlPage startPage = webClient.getPage("http://htmlunit.sf.net");
assertEquals("HtmlUnit - Welcome to HtmlUnit", startPage.getTitleText());
}
}
My pom.xml file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>MavenFirst</groupId>
<artifactId>MavenFirst</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
</dependencies>
</project>
It seems the return type of getPage is com.gargoylesoftware.htmlunit.Page.
Your variable is of type com.gargoylesoftware.htmlunit.html.HtmlPage.
You need to either change the type of your variable or add a cast.
I guess you should be linking it this way:
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.12</version>
</dependency>