Hi Andriy,
I added the parameter usePathBasedConfig=true to the Swagger2Feature bean
declarations but still it does generate an empty swagger.yaml for interfaces
KmopResources and KmopDienstverlener although I noticed that for these
interfaces the @Path() annotation was commented out (as I included it in the
server declaration). After providing an empty @Path("") declaration on the
API interface classes everything worked.
Thanks for the support.
-----Original Message-----
From: Andriy Redko <[email protected]>
Sent: dinsdag 23 mei 2023 3:42
To: Jean Pierre URKENS <[email protected]>; [email protected]
Subject: Re: How to setup multiple JAXRS server endpoints
Hi Jean,
The main problem to configure Swagger property in your particular case is
that the server address is not "known" or "introspectable" for Swagger.
Intuitively, it has to be set manually using basePath to the, essentially,
the server address
part:
- /op/services/accounts
- /op/services/resources
- /op/services/dienstverlener
You could read more about other Swagger properties you have asked here:
https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties
You definitely need to set usePathBasedConfig to "true" otherwise you will
see the same Swagger specs for all servers. We have a sample here which uses
2 jaxrs:server
instances:
https://github.com/apache/cxf/tree/3.6.x-fixes/distribution/src/main/release/samples/jax_rs/description_swagger2_web
Regarding SwaggerUI, I think the value for each of those should be set to,
respectively:
- /op/services/accounts/swagger.yaml
- /op/services/resources/swagger.yaml
- /op/services/dienstverlener/swagger.yaml
I believe this is matching your settings already, except the
usePathBasedConfig part. The example referred above could be helpful, my
apologies if I missed something, there are quite a lot of questions :-) The
fact that the generated Swagger specification is empty is unexpected - it
should not happen when JAX-RS resources are properly configured.
Thank you.
Best Regards,
Andriy Redko
JPU> RE: How to setup multiple JAXRS server endpoints
JPU> Hi Andriy,
JPU> I am not quite understanding how to correctly configure the
Swagger2Feature.
JPU> Referring to the attached cxf-endpoints configuration I (as a test)
JPU> created
JPU> 3 JAXRS server instances:
JPU> 1. A* KmopApiServer* server for the*
JPU> be.dvtm.aeo.op.sodexo.api.KmopApiService* interface, serving
JPU> requests for URI path:
JPU> * <protocol>**//<host:<port>/op/services/accounts*
JPU> ‘op’ = root path of the web application
JPU> ‘services’ = servlet path of the CXF-servlet
JPU> The address of the server is set to ‘/accounts’ and the @Path(…)
JPU> annotation on the interface class was cleared.
JPU> 2. A* Kmop**Resources**ApiServer* server for the* be.dvtm.aeo.op.*
JPU> *openapi.**api.Kmop**Recources**ApiService* interface, serving
JPU> requests for URI path:
JPU> * <protocol>**//<host:<port>/op/services/**resources*
JPU> The address of the server is set to ‘/resources’ and the @Path(…)
JPU> annotation on the interface class was cleared.
JPU> 3. A* Kmop**Dienstverlener**Server* server for the*
be.dvtm.aeo.op.*
JPU> *openapi**.api.Kmop**Dienstverlener**Service* interface, serving
JPU> requests for URI path:
JPU> * <protocol>**//<host:<port>/op/services/**dienstverlener*
JPU> The address of the server is set to ‘/dienstverlener’ and the
JPU> @Path(…) annotation on the interface class was cleared.
JPU> For each of these server instances I’ve set the Swagger2Feature
JPU> with configuration as indicated in the attached cxf-endpoints.xml.
JPU> With regard to the configurations for the Swagger2Feature I’ve the
JPU> following questions:
JPU> a) Referring to *https://cxf.apache.org/docs/swagger2feature.html*
JPU> <https://cxf.apache.org/docs/swagger2feature.html> could you
JPU> clarify on the following configuration parameters:
JPU> *i. ** basePath* – Is this the path to the CXFServlet context (‘
JPU> /op/services’) or to the JAX-RS server instance (e.g.
JPU> ‘/op/services/accounts’) or still something else? Is it used to
JPU> resolve service classes or is it just for documentation in the swagger
file?
JPU> *ii. ** resourcePackage* – the description mentions ‘package names’
JPU> while the default mentions ‘service classes’? Service 2 and 3 above
JPU> are within the same package (generated from the same yaml
JPU> specification that included both interfaces).
JPU> *iii. ** ig**noreRoutes* – is this taken into account when
JPU> scanAllResources=false?
JPU> *iv. ** swaggerUiConfig* – What is the correct ‘url’ parameter value
JPU> (cf. question ‘a’)?
JPU> b) What would be the correct URL to generate a swagger.yaml file
for
JPU> each of the above interfaces? Initially I called:
JPU> *i. **
<protocol>**//<host:<port>/op/services/accounts**/swagger.yaml*
JPU> *ii. **
<protocol>**//<host:<port>/op/services/**resources/swagger.yaml*
JPU> *iii. ** <protocol>**//<host:<port>/op/services/**dienstver*
JPU> *lener/swagger.yaml*
JPU> All three requests delivered the same yaml specification, namely the
one
JPU> for interface* KmopApiServer*?
JPU> c) I tried to debug the processing of the requests under ‘b)’ and
this
JPU> is done by the class JAXRSInterceptor#processRequest where the
JPU> MessageImpl object for request “ii.” looks like the one attached.
JPU> It finds 3 resource
JPU> classes:
JPU> be.dvtm.aeo.op.openapi.api.impl.KmopResourcesApiServiceImpl
JPU> org.apache.cxf.jaxrs.swagger.Swagger2ApiListingResource
JPU> org.apache.cxf.jaxrs.swagger.ui.SwaggerUiService
JPU> è It matches the request to resource* Swagger2ApiListingResource*
with
JPU> UriInfo={type=[yaml], FINAL_MATCH_GROUP=[/]}} and calling its*
JPU> process(…)* method.
JPU> è Here it seems to go wrong. It generates a SwaggerContextService
JPU> having basePath=/op/services/resources/,swaggerConfig=null,
JPU> usePathBasedConfig=null and then calls
JPU> SwaggerContextService.getSwagger()
JPU> which returns the Swagger definition for interface KmopApiServer?
JPU> It looks like it caches generated swagger definitions based on a
JPU> configIdKey with prefix ’swagger.config.id.xxx’. This key is the
JPU> same for all 3 interfaces as usePathBasedConfig=null
JPU> and maps to ‘swagger.config.id.default’. The usePathBasedConfig is
JPU> derived from the ServletConfig parameter
JPU> ‘swagger.use.path.based.config’.* So should this be set on the
JPU> declaration of the CXFServlet** in web.xml?*
JPU> è Actually the SwaggerConfig, the JaxrsScanner and the generated
Swagger
JPU> are cached using keys like
JPU> ‘swagger.config.id.[default|baseUriPath]’, ‘
JPU> scanner.config.id.[default|baseUriPath]’. Caching with ‘baseUriPath’ is
only done when usePathBasedconfig=true.
JPU> è If I patch this to true then configIdKey=’
JPU> swagger.config.id./op/services/resources/’ and no swagger entry is
JPU> cached for this key so it will generate a new one. Again by
JPU> patching
JPU> SwaggerContextService.isUsePathBasedConfigInitParamDefined(sc)=true
JPU> it will call: “swagger = scan(app, servletContext, sc, uriInfo);”
JPU> è Again Scanners are cached and if usePathBasedConfig=null it
will use
JPU> the one cached under ‘swagger.scanner.id.default’ and this again
JPU> returns the swagger definition for the KmopApiService interface.
JPU> è So patching usePathBasedConfig=true will return a new one
JPU> (DefaultJaxrsScanner). The classes to scan for in this new scanner
JPU> are ‘ be.dvtm.aeo.op.openapi.api.impl.KmopResourcesApiServiceImpl‘
JPU> which is correct. It will generate a new (but empty) Swagger object.
JPU> è Next Swagger2ApiListingResource will call the
JPU> customizer.customize(s), which still isn’t putting anything new in
JPU> the Swagger object. Should it or should the next step do this?
JPU> è Next BaseApiListingResource#getListing(…) is called which on
its
JPU> turn calls getListingYamlResponse(..)
JPU> è The final result is a swagger.yaml document with following
content:
JPU> swagger: "2.0"
JPU> info:
JPU> license:
JPU> name: "Apache 2.0 License"
JPU> url: http://www.apache.org/licenses/LICENSE-2.0.html
JPU> basePath: "/op/services/resources"
JPU> So basically an empty swagger file.
JPU> d) The usePathBasedConfig is derived from the ServletConfig
parameter ‘
JPU> swagger.use.path.based.config’. Without this parameter set to true
JPU> there will be only one Swaggerconfig, JaxrsScanner and Swagger
JPU> object.* So should this be set on the declaration of the
JPU> CXFServlet** in web.xml?*
JPU> The majority in this processing happens in the library
JPU> swagger-jaxrs-v1.6.10 which is included as a dependency on
cxf-rt-rs-service-description-swagger.
JPU> Even if I patch usePathBasedConfig=true about everywhere where I
JPU> met this it still doesn’t generate a correct swagger.yaml. Am I
JPU> still missing some configuration parameter?
JPU> Any suggestions on how to resolve this would be welcome.
JPU> Regards,
JPU> J.P. Urkens
JPU> <<...>> <<...>>
JPU> -----Original Message-----
JPU> From: Andriy Redko <[email protected]>
JPU> Sent: maandag 8 mei 2023 23:15
JPU> To: Jean Pierre URKENS <[email protected]>; CXF Dev
JPU> List <
[email protected]>>
JPU> Subject: Re: How to setup multiple JAXRS server endpoints
JPU> Hi Jean,
JPU> Indeed the way you would like to do that is somewhat tricky.
>> So I tried to keep the @Path declaration on the interface classes but
JPU> changed them to @Path(“”). That does seems to work except the
JPU> swagger stuff no longer correctly works.
JPU> This is one of the possible options but OpenAPI/Swagger gets
JPU> confused for a
JPU> reason: the path is now implicit (not in the spec).
JPU> So how about this option:
JPU> - use only one JAX-RS server (address "/")
JPU> - host both resources but use @Path("accounts") and
JPU> @Path("resources") on them respectively
JPU> I see that for @Path("accounts") you need to apply the
JPU> "kmopApiAuthorizationFilter", that could be done using
JPU> DynamicFeature [1], [2]. If this is not the option and you would
JPU> prefer to use 2 separate JAX-RS servers, you may need to provide
JPU> your own instance of Swagger2Customizer [3], [4] which allows to
JPU> transform the OpenAPI/Swagger on the fly. Please let me know if that
would it work for you, thank you.
JPU> [1]
JPU> https://docs.oracle.com/javaee/7/api/javax/ws/rs/container/DynamicF
JPU> eature.html
JPU> [2]
JPU> https://aredko.blogspot.com/2016/02/your-jax-rs-apis-were-not-born-
JPU> equal.html
JPU> [3]
JPU> https://cxf.apache.org/javadoc/latest/org/apache/cxf/jaxrs/swagger/
JPU> Swagger2Customizer.html
JPU> [4] https://cxf.apache.org/docs/swagger2feature.html (has
JPU> customizer
JPU> property)
JPU> Best Regards,
JPU> Andriy Redko
>> Hi Andriy,
>> I am again getting into trouble with server endpoint declarations.
>> Now
JPU> because I am adding additional JAX-RS endpoints.
>> The issue is with:
>> 1. The 'address' attribute on the <jaxrs:server> declaration in
JPU> combination with
>> 2. The 'url-pattern' for the CXFServlet declaration in the web.xml
JPU> in combination with
>> 3. The @Path declaration in the interface class in combination with
>> 4. The @Path declaration on the interface method in combination with
>> So what I had is that my web application deployed under baseUlr 'op'
>> had
JPU> one JAXRS server endpoint with declarations like:
>> 1. <jaxrs:server id="restServer"
JPU> basePackages="be.dvtm.aeo.op.sodexo" address="/">
>> 2. <url-pattern>/services/*</url-pattern>
>> 3. @Path("accounts") on the public interface class
>> 4. @Path("/{authorisationId}/customerFund") on the customerFund
JPU> interface method
>> A valid API call would thus be e.g.:
>> https://<hostname>:<port>/op/services/accounts/{authorizationId}/cust
>> o
>> merFund
>> And this works correctly.
>> We're now introducing additional JAX-RS service endpoints and now I
>> am
JPU> running into problems. This second endpoint was declared with:
>> 1. <jaxrs:server id="resourceServer"
JPU> basePackages="be.dvtm.aeo.op.resources" address="/">
>> 2. <url-pattern>/services/*</url-pattern>
>> 3. @Path("resources") on the public interface class
>> 4. @Path("/NACE") on the NACE interface method
>> So here a valid API call woud be:
JPU> https://<hostname>:<port>/op/services/resources/NACE.
>> The problem is that I can not declare two <jaxrs:server> entries with
>> the
JPU> same ‘address’ as it throws the exception:
>> Caused by: org.apache.cxf.service.factory.ServiceConstructionException:
JPU> There is an endpoint already running on /.
>> So I tried changing the addresses to:
>> · address=”accounts” for the restServer
>> · address=”resources” for the resourceServer
>> But to keep the API-call URLs the same I removed the @Path
>> declaration on
JPU> the interface classes. By doing so the <jaxrs:server> bean
JPU> declarations no longer loads successfully.
>> So I tried to keep the @Path declaration on the interface classes but
JPU> changed them to @Path(“”). That does seems to work except the
JPU> swagger stuff no longer correctly works.
>> So what is the decent way to setup multiple JAX-RS server endpoints
>> where
JPU> each server has its own configuration regarding supported features:
>> · own validation
>> · own object and exception mappings
>> · own swagger file generation
>> · own logging (in separate file if possible)
>> I am using Apache CXF-3.5.2 which uses swagger-core v1.6.6 in
>> cooperation
JPU> with swager-ui v4.5.0.
>> Below the declarations of my endpoints <<...>> Thanks for any advice.
>> Regards,
>> J.P. Urkens
>> -----Original Message-----
>> From: Andriy Redko <[email protected]>
>> Sent: zaterdag 18 juni 2022 1:12
>> To: Jean Pierre URKENS <[email protected]>;
>> [email protected]; [email protected]
>> Subject: Re: JAXRS server endpoint not gracefully shutdown Hi Jean,
>>> 1. a jaxrs server on url: '/<basePath>/services/service1'
>> Correct, so in the relative form like address="/<something>", the
>> JAX-RS
JPU> endpoint path would be:
>> <baseUrl>/<servlet path
>> mapping>/<address>/[@ApplicationPath]/[@Path]
>> The @ApplicationPath is optional in this case.
>>> 2. a jaxws service endpoint on '/<basePath>/services/service2'
>> The JAX-WS is very different from JAX-RS, essentially the action
>> comes
JPU> inside the SOAP message behind <baseUrl>/<servlet path mapping>/
JPU> (@Path / @ApplicationPath are not relevant there).
>>> Question: Because now address="/" is set for the jaxrs:server will
>>> it
>>> also inspect requests targeted for the jaxws service as those
>>> requests have start with the same path '/<basePath>/services/...
>> This is a good question, I have not done it myself but I think it
>> should
JPU> work:
>> the servlet dispatches according to registered services, in this
>> regard
JPU> JAX-RS and JAX-WS should not conflict. Does it work in your case? Thank
you.
>> Best Regards,
>> Andriy Redko
>>> Hi Andriy,
>>> Using address="/" seems to work but still I don't understand how the
>>> following work together:
>>> - path specification in servlet mapping for the CXF servlet
>>> (org.apache.cxf.transport.servlet.CXFServlet)
>>> - the 'address' attribute on the jaxrs:server bean declaration
>>> - the javax.ws.rs.Path or javax.jws.WebService annotation on the
>>> service API description Say I've two services with (relateive to the
>>> host) url's:
>>> 1. a jaxrs server on url: '/<basePath>/services/service1'
>>> 2. a jaxws service endpoint on '/<basePath>/services/service2'
>>> How do I configure above 3 aspects? Currently I have (working):
>>> 1.for the jaxrs:server endpoint:
>>> - servlet path mapping: '/services/*'
>>> - jaxrs-server address attribute: address="/"
>>> - @Path annotation: @Path("service1") 2.For the jaxws
>>> service endpoint:
>>> - servlet path mapping: '/services/*' (JAXWS and JAXRS
>>> requests are handleb by the same CXF servle)
>>> - jaxws:endpoint server address attribute:
>>> address="/service2"
>>> - @WebService(name="service2") A correct request for
>>> '1' would be '/basePath>/services/service1/<ID>'.
>>> A correct request for '2' would be '/basePath>/services/service2'.
>>> The jaxrs/jaxws configuration behavior seem to differ with respect to:
>>> - the server address attribute
>>> - The API annotation (@Path or @Webservice) The JAXWS server
>>> address attribute doesn't seem to interfere with the @Webservice
>>> annotation. While the jaxrs server address attribute does seem to
>>> interfere with the @Path annotation. I would have expected the jaxrs
>>> server aspects to be configured as:
>>> - servlet path mapping: '/services/*'
>>> - jaxrs-server address attribute: address="/service1"
>>> - @Path annotation: @Path("service1") but then a
>>> valid
>>> request would be
>>>> /services/service1/service1/<ID>'.
>>> For both the 'address' attribute is relative to the servlet path.
>>> The @Path Javadoc mentions that this path is relative to the
>>> ApplicationPath which thus seems to be relative to the jaxrs-server
>>> address attribute. As for @Webservice it doesnn't seem to be
>>> url-path
JPU> related.
>>> Question: Because now address="/" is set for the jaxrs:server will
>>> it
>>> also inspect requests targeted for the jaxws service as those
>>> requests have start with the same path '/<basePath>/services/...'.
>>> Albeit somewhat confusing.
>>> J.P.
>>> -----Original Message-----
>>> From: Andriy Redko <[email protected]>
>>> Sent: dinsdag 14 juni 2022 1:08
>>> To: Jean Pierre URKENS <[email protected]>;
>>> [email protected]; [email protected]
>>> Subject: Re: JAXRS server endpoint not gracefully shutdown Hi Jean,
>>> Indeed, the jaxrs:server does not expect address to be omitted, you
>>> could use the "/" (and I believe an empty string would also make it):
>>> <jaxrs:server id="restServer" basePackages="xxx" address="/"> ...
>>> </jaxrs:server>
>>> Thank you.
>>> Hope it helps.
>>> Best Regards,
>>> Andriy Redko
>>>> I create a JAXRS server endpoint (CXF 3.5.2) using spring bean
>>>> declarations
>>>> like:
>>>> <jaxrs:server id="restServer" basePackages="xxx">
>>>> <jaxrs:serviceBeans>
>>>> <ref bean="TestApi" />
>>>> </jaxrs:serviceBeans>
>>>> <jaxrs:providers>
>>>> <…/>
>>>> </jaxrs:providers>
>>>> <jaxrs:features>
>>>> <… />
>>>> </jaxrs:features>
>>>> <jaxrs:inInterceptors>
>>>> <… />
>>>> </jaxrs:inInterceptors>
>>>> <jaxrs:outInterceptors>*
>>>> <**…**/>*
>>>> </jaxrs:outInterceptors>*
>>>> </jaxrs:server>
>>>> Here my “TestApi” bean interface is declared like:
>>>> @Path("accounts")
>>>> @Consumes(MediaType.*APPLICATION_JSON*)
>>>> @Produces(MediaType.*APPLICATION_JSON*)
>>>> public interface TestApi {
>>>> …
>>>> }
>>>> And CXF is triggered via a servlet configuration like:
>>>> <servlet>
>>>> <display-name>CXF Servlet</display-name>
>>>> <servlet-name>CXFServlet</servlet-name>
>>>> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servle
>>>> t
-class>>>>>
>>>> </servlet>
>>>> <servlet-mapping>
>>>> <servlet-name>CXFServlet</servlet-name>
>>>> <url-pattern>/services/*</url-pattern>
>>>> </servlet-mapping>
>>>> Because I’ve got the @Path declaration on the interface type I’ve
>>>> omitted
>>>> the address=”accounts” attribute on the jaxrs:server declaration
>>>> since otherwise
>>>> I noticed that the server would be listening to
>>>> /basepath/services/ accounts/accounts/…).
>>>> Now this configuration works perfectly, only when shutting down
>>>> the application server cxf calls
>>>> ServerImpl#destroy()
>>>> which delegates (via Obeservable) to
>>>> AbstractHTTPDestination#deactivate()
>>>> which calls
>>>> registry.removeDestination(path).
>>>> This path is null (no ‘address’ specified on jaxrs:server
>>>> declaration) and results in a NPE on the registry Map.
>>>> This causes an unclean shutdown of my server.
>>>> Is this an error in cxf or is my jaxrs:server configured incorrectly?
>>>> How does the ‘address’ attribute on the jaxrs:server declaration
>>>> correctly interact with the @Path parameter on the API interface?
JPU> <<...>>