I have implemented web service:
#WebServiceClient(//parameters//)
#HandlerChain(file = "handlers.xml")
public class MyWebServiceImpl {...}
Also I have implemented ObjectFactory with list of classes for creating my requests and responses. For Example class Test.
I need to get xml of response.
I try to use JAX-WS SOAP handler, so I add this #HandlerChain(file = "handlers.xml") anotation.
My handlers.xml looks like:
<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
<handler-chain>
<handler>
<handler-class>java.com.webservice.service.LoggingHandler</handler-class>
</handler>
</handler-chain>
</handler-chains>
My LoggingHandler class is:
import java.io.PrintWriter;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class LoggingHandler implements javax.xml.ws.handler.soap.SOAPHandler<SOAPMessageContext> {
public void close(MessageContext messagecontext) {
}
public Set<QName> getHeaders() {
return null;
}
public boolean handleFault(SOAPMessageContext messagecontext) {
return true;
}
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean) smc.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
System.out.println("\nOutbound message:");
} else {
System.out.println("\nInbound message:");
}
SOAPMessage message = smc.getMessage();
try {
PrintWriter writer = new PrintWriter("soap_responce" + System.currentTimeMillis(), "UTF-8");
writer.println(message);
writer.close();
message.writeTo(System.out);
System.out.println(""); // just to add a newline
} catch (Exception e) {
System.out.println("Exception in handler: " + e);
}
return outboundProperty;
}
}
I have test class which creates request, here are part of code:
MyWebServiceImpl impl = new MyWebServiceImpl(url, qName);
ws = impl.getMyWebServicePort();
Test req = new Test();
I suppose to get xml response in file "soap_responce" + System.currentTimeMillis(). But such file isn't even created. Please suggest how to get xml response, I'm new to web services and may do something wrong. Thanks
Using SOAP handlers is IMHO perfectly fine for such a task. I would approach it the same way.
I was able to use your configuration with minor modifications to get the example running. As a result I am able to see generated files. If you can't see them please check whether you check correct path, e.g. using:
File file = new File("soap_responce" + System.currentTimeMillis());
System.out.println(file.getAbsolutePath());
What I changed is:
package from java.com.webservice.service.LoggingHandler to com.webservice.service.LoggingHandler as packages starting with java are forbidden
Complete project can be found here:
https://github.com/destin/SO-answers/tree/master/SO-how-get-xml-responce-using-jax-ws-soap-handler
org.dpytel.jaxws.jaxws_java_first_jboss.client.Main class shows how I get and execute the web service.
BTW. you don't need to implement client stub and object factory etc when you have WSDL file. You can use wsimport tool. You can check how to use it in mentioned project.
Related
I'm using Citrus 2.7.8 with Cucumber 2.4.0. I'm making a soap call and want to get the response and do some advanced parsing on it to validate a graphql response has matching values. (I understand how to do validations when it's something that just has one element, but I need something able to handle when there could be one or many elements returned (for example, 1 vehicle or 4 vehicles)). To make my validation very dynamic and able to handle many different 'quotes', I want to store the response to a Citrus variable and then make it available to java to read in the file and do the advanced parsing and validation.
The TestContext injection doesn't appear to currently work with cucumber (see https://github.com/citrusframework/citrus/issues/657) so I'm using the workaround here:
How to inject TestContext using TestRunner and cucumber to manually create the context. Without this I get a nullpointerexception on anything with the context.
I am able to use Citrus's message function to grab the soap response which is awesome. My echo statements in the console show that it successfully put the right value into the citrus variable. But I'm having problems making that available to java so that I can then open it up and parse through it.
I've scaled down my step definition file to just the pertinent code. My couple attempts are listed below along with the problems I encountered in their results.
Does anyone have any ideas on how I can successfully workaround the context issues and make my response available to java?
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import com.consol.citrus.Citrus;
import com.consol.citrus.annotations.CitrusFramework;
import com.consol.citrus.annotations.CitrusResource;
import com.consol.citrus.config.CitrusSpringConfig;
import com.consol.citrus.context.TestContext;
import com.consol.citrus.dsl.junit.JUnit4CitrusTestRunner;
import com.consol.citrus.dsl.runner.TestRunner;
import com.consol.citrus.ws.client.WebServiceClient;
import cucumber.api.java.en.When;
#ContextConfiguration(classes = CitrusSpringConfig.class)
public class CitrusSteps extends JUnit4CitrusTestRunner {
#CitrusFramework
private Citrus citrus;
#CitrusResource
private TestRunner runner;
#CitrusResource
private TestContext context;
#Autowired
private WebServiceClient getQuote;
#When("^I call getQuote with id \"([^\"]*)\"$")
public void i_call_getquote_with_id(String quoteId) throws Throwable {
context = citrus.createTestContext();
String soappayload = "my payload (taken out for privacy purposes)";
runner.soap(action -> action.client(getQuote)
.send()
.soapAction("getQuote")
.payload(soappayload));
runner.soap(action -> action.client(getQuote)
.receive()
.name("getQuoteResponseStoredMessage"));
//this bombs out on the context line with this: "com.consol.citrus.exceptions.CitrusRuntimeException: Unknown variable 'messageStoreGetQuoteResponse1'"
runner.variable("messageStoreGetQuoteResponse1", "citrus:message(getQuoteResponseStoredMessage.payload())");
runner.echo("First try: ${messageStoreGetQuoteResponse1}");
String firstTry = context.getVariable("messageStoreGetQuoteResponse1");
log.info("First Try java variable: " + firstTry);
//this bombs out on the context line with this: "com.consol.citrus.exceptions.CitrusRuntimeException: Unknown variable 'messageStoreGetQuoteResponse2'"
runner.createVariable("messageStoreGetQuoteResponse2", "citrus:message(getQuoteResponseStoredMessage.payload())");
runner.echo("Second try: ${messageStoreGetQuoteResponse2}");
String secondTry = context.getVariable("messageStoreGetQuoteResponse2");
log.info("Second Try java variable: " + secondTry);
//This stores the literal as the value - it doesn't store the message so it appears I can't use citrus functions within the context
context.setVariable("messageStoreGetQuoteResponse3", "citrus:message(getQuoteResponseStoredMessage.payload())");
String thirdTry = context.getVariable("messageStoreGetQuoteResponse3");
log.info("Third Try java variable: " + thirdTry);
}
}
A smart co-worker figured out a workaround for the injection not working w/ cucumber.
I replaced these two lines:
#CitrusResource
private TestContext context;
with these lines instead:
TestContext testContext;
public TestContext getTestContext() {
if (testContext == null) {
runner.run(new AbstractTestAction() {
#Override
public void doExecute(TestContext context) {
testContext = context;
}
});
}
return testContext;
}
Then within my step where I want the context, I can use the above method. In my case I wanted my message response, so I was able to use this and confirm that the response is now in my java variable:
String responseXML = getTestContext().getMessageStore().getMessage("getQuoteResponseStoredMessage").getPayload(String.class);
log.info("Show response XML: " + responseXML);
I'm using spring-integration bundled with spring-batch and got stuck trying to write integration tests to test the whole flow, not just single config.
I've created Embedded Sftp Server for this tests and trying to send message to sftpInboundChannel - the message is sent, but nothing happens, but when i send this message to the next channel (after sftpInboundChannel) it goes ok. Also i'm not able to load test source properties, even though i'm using #TestPropertySource annotation.
This are my class annotations
#TestPropertySource(properties = {
//here goes all the properties
})
#EnableConfigurationProperties
#RunWith(SpringRunner.class)
#Import({TestConfig.class, SessionConfig.class})
#ActiveProfiles("it")
#SpringIntegrationTest
#EnableIntegration
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
This is my class body
#Autowired
private PollableChannel sftpInboundChannel;
#Autowired
private SessionFactory<ChannelSftp.LsEntry> defaultSftpSessionFactory;
#Autowired
private EmbeddedSftpServer server;
#Test
public void shouldDoSmth() {
RemoteFileTemplate<ChannelSftp.LsEntry> template;
try {
template = new RemoteFileTemplate<>(defaultSftpSessionFactory);
SftpTestUtils.moveToRemoteFolder(template);
final List<ChannelSftp.LsEntry> movedFiles = SftpTestUtils.listFilesFromDirectory("folder/subfolder", template);
log.info("Moved file {}", movedFiles.size());
final MessageBuilder<String> messageBuilder = MessageBuilder.withPayload("Sample.txt") // path to file
.setHeader("file_Path", "Sample.txt")
boolean wasSent = this.sftpInboundChannel.send(messageBuilder.build());
log.info("Was sent to sftpInboundChannel channel {}", wasSent);
log.info("message {}", messageBuilder.build());
} finally {
SftpTestUtils.cleanUp();
}
}
To the case of not read the property file one solution is add in your Test class something like this:
#BeforeClass
public static void beforeClass() {
System.setProperty("propertyfile", "nameOfFile.properties");
}
A second way is to create a xml (or class) config where you add the tag:
<context:property-placeholder
location="nameOfFile.properties"
ignore-resource-not-found="true" system-properties-mode="OVERRIDE" />
and your file will be localized.
The property file should be inside of resources folder.
I have implementated a Rest web service (the function is not relevant) using JAX-RS. Now I want to generate its documentation using Swagger. I have followed these steps:
1) In build.gradle I get all the dependencies I need:
compile 'org.glassfish.jersey.media:jersey-media-moxy:2.13'
2) I documentate my code with Swagger annotations
3) I hook up Swagger in my Application subclass:
public class ApplicationConfig extends ResourceConfig {
/**
* Main constructor
* #param addressBook a provided address book
*/
public ApplicationConfig(final AddressBook addressBook) {
register(AddressBookService.class);
register(MOXyJsonProvider.class);
register(new AbstractBinder() {
#Override
protected void configure() {
bind(addressBook).to(AddressBook.class);
}
});
register(io.swagger.jaxrs.listing.ApiListingResource.class);
register(io.swagger.jaxrs.listing.SwaggerSerializers.class);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.2");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8282");
beanConfig.setBasePath("/");
beanConfig.setResourcePackage("rest.addressbook");
beanConfig.setScan(true);
}
}
However, when going to my service in http://localhost:8282/swagger.json, I get this output.
You can check my public repo here.
It's times like this (when there is no real explanation for the problem) that I throw in an ExceptionMapper<Throwable>. Often with server related exceptions, there are no mappers to handle the exception, so it bubbles up to the container and we get a useless 500 status code and maybe some useless message from the server (as you are seeing from Grizzly).
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
public class DebugMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable exception) {
exception.printStackTrace();
if (exception instanceof WebApplicationException) {
return ((WebApplicationException)exception).getResponse();
}
return Response.serverError().entity(exception.getMessage()).build();
}
}
Then just register with the application
public ApplicationConfig(final AddressBook addressBook) {
...
register(DebugMapper.class);
}
When you run the application again and try to hit the endpoint, you will now see a stacktrace with the cause of the exception
java.lang.NullPointerException
at io.swagger.jaxrs.listing.ApiListingResource.getListingJson(ApiListingResource.java:90)
If you look at the source code for ApiListingResource.java:90, you will see
Swagger swagger = (Swagger) context.getAttribute("swagger");
The only thing here that could cause the NPE is the context, which scrolling up will show you it's the ServletContext. Now here's the reason it's null. In order for there to even be a ServletContext, the app needs to be run in a Servlet environment. But look at your set up:
HttpServer server = GrizzlyHttpServerFactory
.createHttpServer(uri, new ApplicationConfig(ab));
This does not create a Servlet container. It only creates an HTTP server. You have the dependency required to create the Servlet container (jersey-container-grizzly2-servlet), but you just need to make use of it. So instead of the previous configuration, you should do
ServletContainer sc = new ServletContainer(new ApplicationConfig(ab));
HttpServer server = GrizzlyWebContainerFactory.create(uri, sc, null, null);
// you will need to catch IOException or add a throws clause
See the API for GrizzlyWebContainerFactory for other configuration options.
Now if you run it and hit the endpoint again, you will see the Swagger JSON. Do note that the response from the endpoint is only the JSON, it is not the documentation interface. For that you need to use the Swagger UI that can interpret the JSON.
Thanks for the MCVE project BTW.
Swagger fixed this issue in 1.5.7. It was Issue 1103, but the fix was rolled in last February. peeskillet's answer will still work, but so will OP's now.
I have a strange issue and didn't find any information about it at all.
Having a simple POJO like (simplified..)
#XmlRootElement
public class Bill {
List<Position> positions
.. getter/setter
}
#XmlRootElement
public class Position {
.. some simple properties with getters/setters
}
I am unable to call a RESTful Service using instances of these classes. I'm getting real weird errors I don't really understand.
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.List out of START_OBJECT token
The funny thing is, when I just test serialization/deserialization using Jackson Object mapper directly, it works as expected!
ObjectMapper mapper = new ...
mapper.writeValue(stringWriter, bill);
mapper.readValue(stringWriter.toString(), Bill.class);
This works perfectly. So I guess the POJO itself is OK and Jackson is able to handle the JSON-String.
Calling the RESTful service using the same Bill instance fails with the error mentioned above. I see it is using Jackson as well, here is part of stack trace:
at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:160)
at org.codehaus.jackson.map.deser.StdDeserializationContext.mappingException(StdDeserializationContext.java:198)
at org.codehaus.jackson.map.deser.CollectionDeserializer.deserialize(CollectionDeserializer.java:103)
at org.codehaus.jackson.map.deser.CollectionDeserializer.deserialize(CollectionDeserializer.java:93)
at org.codehaus.jackson.map.deser.CollectionDeserializer.deserialize(CollectionDeserializer.java:25)
at org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:230)
And here is how the RESTful Application is configured:
#javax.ws.rs.ApplicationPath("rest")
public class ApplicationConfig extends Application {
public Set<Class<?>> getClasses() {
return getRestResourceClasses();
}
/**
* Do not modify this method. It is automatically generated by NetBeans REST support.
*/
private Set<Class<?>> getRestResourceClasses() {
Set<Class<?>> resources = new java.util.HashSet<Class<?>>();
resources.add(rest.RestAPI.class);
// following code can be used to customize Jersey 1.x JSON provider:
try {
Class jacksonProvider = Class.forName("org.codehaus.jackson.jaxrs.JacksonJsonProvider");
resources.add(jacksonProvider);
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(getClass().getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
return resources;
}
}
Do you have any idea what I'm missing?
I generated the service and the client with NetBeans. Oh and it works when I use XML instead of JSON.
Any help would be very much appreciated.
I'm sorry but after hours of testing and debugging I finally found the cause of the problem.
Still I would be very interested why this is?
Commenting out the following code did the trick:
// following code can be used to customize Jersey 1.x JSON provider:
try {
Class jacksonProvider = Class.forName("org.codehaus.jackson.jaxrs.JacksonJsonProvider");
resources.add(jacksonProvider);
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(getClass().getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
I'm using GlassFish Tools Bundle for Eclipse.
I need to create a bean and a client that tests it. The bean (and its interface) are the following.
package mykPK;
import java.math.BigDecimal;
import javax.ejb.*;
#Stateless
public class ConverterBean implements Converter {
private BigDecimal yenRate = new BigDecimal("115.3100");
private BigDecimal euroRate = new BigDecimal("0.0071");
public BigDecimal dollarToYen(BigDecimal dollars) {
BigDecimal result = dollars.multiply(yenRate);
return result.setScale(2, BigDecimal.ROUND_UP);
}
public BigDecimal yenToEuro(BigDecimal yen) {
BigDecimal result = yen.multiply(euroRate);
return result.setScale(2, BigDecimal.ROUND_UP);
}
}
The interface:
package mykPK;
import java.math.BigDecimal;
import javax.ejb.Remote;
#Remote
public interface Converter {
public BigDecimal dollarToYen(BigDecimal dollars);
public BigDecimal yenToEuro(BigDecimal yen);
}
I create them correctly in an EJB project and run them "as a server". All seems to start correctly.
Now I want to create a client.
I tried to put the client inside the same project, creating a different project ("Application Client Project") or even creating a more general "E application project" with two subproject. The result is the same.
Now, the client code is the following
import java.math.BigDecimal;
import javax.ejb.EJB;
import mykPK.Converter; /*of course to to that, i reference in the client project the
EJB project*/
public class ConverterClient {
#EJB private static Converter converter;
public ConverterClient(String[] args) {
}
public static void main(String[] args) {
ConverterClient client = new ConverterClient(args);
client.doConversion();
}
public void doConversion() {
try {
BigDecimal param = new BigDecimal("100.00");
BigDecimal yenAmount = converter.dollarToYen(param);
System.out.println("$" + param + " is " + yenAmount
+ " Yen.");
BigDecimal euroAmount = converter.yenToEuro(yenAmount);
System.out.println(yenAmount + " Yen is " + euroAmount
+ " Euro.");
System.exit(0);
} catch (Exception ex) {
System.err.println("Caught an unexpected exception!");
ex.printStackTrace();
}
}
}
When I run this file, i always get the same:
Caught an unexpected exception!
java.lang.NullPointerException
at ConverterClient.doConversion(ConverterClient.java:17)
at ConverterClient.main(ConverterClient.java:12)
I suppose this is beacause my client is not in the same container of the bean, and it is not "deployed" (I simply run the file). But when I tried the more general "Enterprise Application Project" the results were the same)
So, where to put the client and give him the access (with #EJB) to the Bean??
You're trying to inject into a non-managed object. You need to grab the initial context and look it up.
Pretty much the same thing as here:
cannot find my bean using the InitialContext.lookup() method
The stack trace suggests that you've directly launched the main method. In order to use injection in the main class, you must use the application client container.
A good example of this working can be found here Packaging your client for use with glassfish's application client container (via the "appclient" command) is shown, as is packaging it as a standalone Java app.