Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete Automation of Endpoint Initialization #6

Closed
jonashackt opened this issue Nov 7, 2016 · 17 comments
Closed

Complete Automation of Endpoint Initialization #6

jonashackt opened this issue Nov 7, 2016 · 17 comments

Comments

@jonashackt
Copy link
Member

jonashackt commented Nov 7, 2016

Autodetect everything needed to initialize javax.xml.ws.Endpoint completely based upon generated Class files.

Background: The cxf-spring-boot-starter-maven-plugin generates two Class files among others:
One representing your Service Endpoint Interface (SEI) - annotated with javax.jws.WebService - and another WebServiceClient class - which implements javax.xml.ws.Service. Both are needed to initialize the javax.xml.ws.Endpoint for publishing it with Apache CXF. In traditional way with Spring Boot, but without the cxf-spring-boot-starter, this is done like that:

@Bean
public WeatherService weatherService() {
    return new WeatherServiceEndpoint();
}

@Bean
public Endpoint endpoint() {
    EndpointImpl endpoint = new EndpointImpl(springBus, weatherService());        
    // CXF JAX-WS implementation relies on the correct ServiceName as QName-Object with
    // the name-Attribute´s text <wsdl:service name="Weather"> and the targetNamespace
    // "http://www.codecentric.de/namespace/weatherservice/"
    // Also the WSDLLocation must be set
    endpoint.setServiceName(weather().getServiceName());
    endpoint.setWsdlLocation(weather().getWSDLDocumentLocation().toString());
    endpoint.publish(PUBLISH_URL_ENDING);
    return endpoint;
}

@Bean
public Weather weather() {
    // Needed for correct ServiceName & WSDLLocation to publish contract first incl. original WSDL
    return new Weather();
}

Now the idea is, that this is all boilerplate - if you like to run your SOAP web services in a contract first manner. Because then, everything is quite clear from your starting point - the WSDL and XSD files. There you have already the Name of your Service and all it´s methods. It should be possible to initialize the CXF endpoint from that information. Prerequisites remain the generation of the Java classes via the cxf-spring-boot-starter-maven-plugin and a manual implementation of the Service Endpoint Interface (SEI), because that´s the place where you´re service coding starts - we shouldn´t generate that one for you.

With this feature, you dont´t have to do a lot any more - the hole Endpoint initialization (including the definition of a WebService URL) is done for you.

@jonashackt
Copy link
Member Author

jonashackt commented Nov 7, 2016

Also relating commits:

8908f41 9a04947
97c8cac
21c147f
3b3135e
cfcb60d
3efb0c5

@jonashackt
Copy link
Member Author

Idea: The feature is now implemented in a first version - but the reaction if you don´t have an implementation of your SEI is an Exception. It would be better a Failure Analysis shown as startup failure.

jonashackt added a commit that referenced this issue Nov 8, 2016
…tFoundException integrated in WebServiceAutoDetector to handle the Situation, where the user didn´t implement the SEI - because automatic Endpoint Detection is based upon this class.
jonashackt added a commit that referenced this issue Nov 8, 2016
jonashackt added a commit that referenced this issue Nov 8, 2016
jonashackt added a commit that referenced this issue Nov 10, 2016
…dpoint Interface (SEI) is annotated with javax.jws.WebService, but also another class (e.g. the SEI implementing Endpoint class).
@jonashackt
Copy link
Member Author

Also handle situations, where not only the SEI is annotated with javax.jws.WebService, but also the SEI implementing Endpoint class.

jonashackt added a commit that referenced this issue Nov 10, 2016
…uct instead of Pre!) of WebServiceAutoDetector
jonashackt added a commit that referenced this issue Nov 10, 2016
@jonashackt
Copy link
Member Author

The Failure Analyzer for the missing SEI implementation is cool, but we should also analyze, if the SEI itself couldn´t be found - along with the WebServiceClient class. So two more Failure Analyzers should be implemented.

jonashackt added a commit that referenced this issue Nov 16, 2016
jonashackt added a commit that referenced this issue Nov 16, 2016
…int Interface (SEI) could´nt be found. Also refactored the WebServiceAutoDetector module architecture that now separates the Scanning and the working with the specific WebService-Classes. That also results in the possiblility to use Mocks instead of preimplemented WebServiceAutoDetector Testable class, which never felt right.
jonashackt added a commit that referenced this issue Nov 16, 2016
…terface_and_the_other_not() --> class.getName instead of class.getSimpleName is required for Class-resolution.
jonashackt added a commit that referenced this issue Nov 22, 2016
…AutoConfiguration - so the Autodetection is only done once and if it fails, it will fail fast. Also the Logging is enhanced, so you can see the Endpoint initializing better. Also some getters renamed and used inside CxfAutoConfiguration.
jonashackt added a commit that referenced this issue Nov 22, 2016
…er were missing in spring.factories - and not used :( We need a integration test for that!
@jonashackt
Copy link
Member Author

Everything is working fine, but not when starting via java -jar. Then currently you get a

016-11-28 11:47:26.487  INFO 92673 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2016-11-28 11:47:27.429 ERROR 92673 --- [ost-startStop-1] o.s.b.c.embedded.tomcat.TomcatStarter    : Error starting Tomcat context. Exception: org.springframework.beans.factory.BeanCreationException. Message: Error creating bean with name 'de.codecentric.cxf.configuration.CxfAutoConfiguration': Invocation of init method failed; nested exception is de.codecentric.cxf.diagnostics.SeiNotFoundException: The Service Endpoint Interface (SEI) could´nt be found - an interface that´s annotated with javax.jws.WebService
2016-11-28 11:47:27.452  WARN 92673 --- [           main] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.boot.context.embedded.EmbeddedServletContainerException: Unable to start embedded Tomcat
2016-11-28 11:47:27.456 ERROR 92673 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

The Service Endpoint Interface (SEI) could´nt be found - an interface that´s annotated with javax.jws.WebService

Action:

Use the cxf-spring-boot-starter-maven-plugin (https://github.com/codecentric/cxf-spring-boot-starter-maven-plugin) to generate the needed class files from your WSDL & XSDs

@jonashackt
Copy link
Member Author

jonashackt commented Nov 29, 2016

The problem seems to be related to the underlying use of fast-classpath-scanner issue classgraph/classgraph#46 - allthough the scanning in spring boot jars should have been resolved...

After debugging-session, this is not true. All needed class files are listed in the scan results of the FastClassPathScanner. Problem seems to be, how the annotation javax.jws.WebService ist searched for...

Ok, found it :P Works as designed... ~

https://github.com/lukehutch/fast-classpath-scanner/wiki/2.-Constructor#un-blacklisting-system-classes--scanning-system-jars :

By default, system packages (java., javax. and sun.*) are blacklisted.

@jonashackt
Copy link
Member Author

jonashackt commented Dec 23, 2016

One more step is achieved, the fast-classpath-scanner is definitively working correct - now we get an error inside Apache CXF: NoSuchMethodException inorg.apache.cxf.jaxws.JAXWSMethodDispatcher...

Caused by: java.lang.NoSuchMethodException: de.codecentric.soap.endpoint.WeatherServiceEndpoint.getCityWeatherByZIP(de.codecentric.namespace.weatherservice.general.ForecastRequest)
	at java.lang.Class.getMethod(Class.java:1786)
	at org.apache.cxf.jaxws.JAXWSMethodDispatcher.getImplementationMethod(JAXWSMethodDispatcher.java:89)
	at org.apache.cxf.jaxws.JAXWSMethodDispatcher.bind(JAXWSMethodDispatcher.java:56)

Maybe this has something to do with different classloaders in different threads, where different cxf Bus instances are holded: http://stackoverflow.com/questions/14452201/nosuchmethodexception-for-an-available-method

https://docs.jboss.org/author/display/JBWS/Apache+CXF+integration#ApacheCXFintegration-Threadbusstrategy%28THREADBUS%29

@marcopaga
Copy link
Collaborator

marcopaga commented Jan 5, 2017

  • The WeatherService seems to be ignored by the Scanner because of the single jar layout of spring boot.

  • When running the FastClasspathScanner verbosely with java -jar in the sample project I can see that the Service is ignored.

2017-01-05T22:55:05.893+0100 FastClasspathScanner Parsing classfile jar:/Users/marco/Projects/v/spring-samples/cxf-boot-simple/target/cxf-boot-simple-0.0.1-SNAPSHOT.jar!BOOT-INF/classes/de/codecentric/namespace/weatherservice/WeatherService.class (took 0,000046 sec) 2017-01-05T22:55:05.893+0100 FastClasspathScanner -- Class de.codecentric.namespace.weatherservice.WeatherService is at incorrect relative path BOOT-INF/classes/de/codecentric/namespace/weatherservice/WeatherService.class -- ignoring

2017-01-05T22:55:05.869+0100 FastClasspathScanner Ignoring duplicate path BOOT-INF/classes/de/codecentric/namespace/weatherservice/WeatherService.class in classpath element /Users/marco/Projects/v/spring-samples/cxf-boot-simple/target/cxf-boot-simple-0.0.1-SNAPSHOT.jar -- it is masked by the same relative path occurring in an earlier classpath entry

  • Instead when running the tests of the starter in verbose mode the class file is considered:

2017-01-05T23:06:58.024+0100 FastClasspathScanner Parsing classfile /Users/marco/Projects/v/cxf-spring-boot-starter/target/test-classes/de/codecentric/namespace/weatherservice/WeatherService.class (took 0,000105 sec) 2017-01-05T23:06:58.024+0100 FastClasspathScanner -- Found interface class de.codecentric.namespace.weatherservice.WeatherService 2017-01-05T23:06:58.024+0100 FastClasspathScanner ---- Superinterface: java.lang.Object 2017-01-05T23:06:58.024+0100 FastClasspathScanner ---- Annotations: javax.jws.WebService, javax.xml.bind.annotation.XmlSeeAlso

@jonashackt
Copy link
Member Author

jonashackt commented Jan 6, 2017

You´re right, with java -jar fast-classpath-scanner doesn´t seem to work. And with mvn spring-boot:run you get the mentioned problems in CXF.

Let´s switch to the Spring variant and ClassPathScanningCandidateComponentProvider, which is after all capable of scanning for Interfaces with specific Annotations (which I (incl. stackoverflow and google) thought it is not and what was the reason started to go with the fast-classpath-scanner).

jonashackt added a commit that referenced this issue Jan 7, 2017
…ng variant (https://github.com/spring-projects/spring-framework/blob/master/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java). New tests for the WebServiceScanner.

This was only possible, because Spring is after all able to scan for Interfaces which have specific Annotations (http://stackoverflow.com/a/41504372/4964553). Therefore Spring can be used and all problems with java -jar and mvn spring-boot:run not finding classes or CXF gone wild are completely gone now!
jonashackt added a commit that referenced this issue Jan 7, 2017
…for an Class that implements a given Interface it also returns the Interface itself so we have to throw that out of the results. Not cxf-boot-simple is also running again.
@jonashackt
Copy link
Member Author

jonashackt commented Jan 9, 2017

The only thing that´s missing is the right package-basename for Spring to scan for the SEI, SEI-Implementation and WebServiceClient classes.

To get this 100% right, we need to use the same mechanism as the cxf-spring-boot-starter-maven-plugin / jaxws-maven-plugin, which itself uses WSimportTool of the JAXWS-RI implementation, to obtain the package-Name from the WSDL file, where the classes are generated to.

I knew from former experiments, that the WSDL´s targetNamespace is used to generate the package name. If you have targetNamespace="http://www.codecentric.de/namespace/weatherservice/" for example, your package will be de.codecentric.namespace.weatherservice. I just had to find the code which is used to generate this. And I found it in WSDLModeler at line 2312:

        String wsdlUri = document.getDefinitions().getTargetNamespaceURI();
        return XJC.getDefaultPackageName(wsdlUri);

And the best thing is, this algorithm is specified in the JAXB spec. So it should be ok to rely onto it.

All we have to do now is to read the WSDL (maybe use the code from cxf-spring-boot-starter-maven-plugin), obtain the targetNamespace, insert into Spring Scanner and scan :)

jonashackt added a commit that referenced this issue Jan 11, 2017
jonashackt added a commit that referenced this issue Jan 11, 2017
jonashackt added a commit that referenced this issue Jan 12, 2017
jonashackt added a commit that referenced this issue Jan 14, 2017
…om MANIFEST to the cxf-spring-boot-maven.properties generated by the cxf-spring-boot-maven-plugin.
jonashackt added a commit that referenced this issue Jan 14, 2017
…SEI and WebServiceClient´s packageName with the help of JAXB XJC (like it is done in the JAXWS RI) is now part of the cxf-spring-boot-maven-plugin. The packageName is put into the cxf-spring-boot-maven.properties file - already known from the injection of the project´s packageName to later obtain the SEI Implementation class. We only need a PackageName Reader in the cxf-spring-boot-starter now, so the WsdlScanner was deleted.
@jonashackt
Copy link
Member Author

jonashackt commented Jan 16, 2017

Now this feature is completely working. For the time beeing (without releases), you need the current cxf-spring-boot-starter-maven-plugin installed via Maven with the package extraction features.

But the Automation of Endpoint initialization should also consider situations, where it should´nt try to autoinitialize the Endpoints. E.g. if you have an integration test / JAXWS client scenario, you don´t need it. But in the current implementation you @autowire the CxfAutoConfiguration to obtain the baseAndServiceEndingUrl().

Therefore it should be possible to deactive it - at least via a property. Further deactivation features are not part of this (already long) issue.

jonashackt added a commit that referenced this issue Jan 16, 2017
…t only sceanarios (it also helps #8). Renamed the SystemTest from Integration test which it is not.
jonashackt added a commit that referenced this issue Jan 24, 2017
@jonashackt jonashackt reopened this Jan 25, 2017
@jonashackt
Copy link
Member Author

mvn spring-boot:run seems to get into trouble with injection an autowired Bean:

2017-01-25 09:07:02.530  INFO [bootstrap,,,] 5844 --- [  restartedMain] d.c.c.a.WebServiceAutoDetector           : Found SEI implementing class: 'de.xxx.yyyy.endpoint.LebenSe
rviceEndpoint'
2017-01-25 09:07:02.540  WARN [bootstrap,,,] 5844 --- [  restartedMain] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling
refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'de.codecentric.cxf.configuration.XmlValidationConfiguration': Unsa
tisfied dependency expressed through field 'endpoint'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'endpoint' defined
 in class path resource [de/codecentric/cxf/configuration/CxfAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.B
eanInstantiationException: Failed to instantiate [javax.xml.ws.Endpoint]: Factory method 'endpoint' threw exception; nested exception is org.springframework.beans.factory.Unsatisfi
edDependencyException: Error creating bean with name 'seiImplementation': Unsatisfied dependency expressed through field 'lebenServiceController'; nested exception is org.springfra
mework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'de.vorsorge.bipro.controller.LebenServiceController' available: expected at least 1 bean which quali
fies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
2017-01-25 09:07:02.552  INFO [bootstrap,,,] 5844 --- [  restartedMain] o.apache.catalina.core.StandardService   : Stopping service Tomcat
2017-01-25 09:07:02.702 ERROR [bootstrap,,,] 5844 --- [  restartedMain] o.s.b.d.LoggingFailureAnalysisReporter   :

***************************
APPLICATION FAILED TO START
***************************

Description:

Field lebenServiceController in de.vorsorge.bipro.endpoint.LebenServiceEndpoint required a bean of type 'de.xxx.yyy.controller.LebenServiceController' that could not be foun
d.


Action:

Consider defining a bean of type 'de.vorsorge.bipro.controller.LebenServiceController' in your configuration.

and Method matching inside the CXF core:

Caused by: java.lang.NoSuchMethodException: de.codecentric.soap.endpoint.WeatherServiceEndpoint.getCityWeatherByZIP(de.codecentric.namespace.weatherservice.general.ForecastRequest)
	at java.lang.Class.getMethod(Class.java:1786)
	at org.apache.cxf.jaxws.JAXWSMethodDispatcher.getImplementationMethod(JAXWSMethodDispatcher.java:89)
	at org.apache.cxf.jaxws.JAXWSMethodDispatcher.bind(JAXWSMethodDispatcher.java:56)

All other situations are working correctly - java -jar service.jar , a "Run as" inside the IDE or inside mvn test scenarios... Very Strange!

marcopaga pushed a commit that referenced this issue Jan 25, 2017
@marcopaga
Copy link
Collaborator

marcopaga commented Jan 25, 2017

We were using the Spring Boot Devtools to help us speeding up the development. But it seems to be the case that the two class loaders used to start the application get in our way. If both classloaders load the same class independently, the classes are not equal and Spring won't use these for autowiring components.

@jonashackt
Copy link
Member Author

Ah 1000 thanks to you @marcopaga for reporting and sorting that out! I close it again :)

@sreenu116
Copy link

hi jonashakt de.codecentric.namespace.weatherservice this package is not available so its't loaded project

@jonashackt
Copy link
Member Author

Sorry, I don´t really understand... This package isn´t available after first checkout of this project. But if you run a mvn generate-sources and have a WSDL file in place like https://github.com/codecentric/cxf-spring-boot-starter/blob/master/src/test/resources/wsdl/Weather1.0.wsdl, there will be a generated package with that exact name under de.codecentric.namespace.weatherservice, which is generated from the targetNamespace="http://www.codecentric.de/namespace/weatherservice/.

marcopaga added a commit that referenced this issue Jan 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants