During debugging in IntelliJ, I'm getting the SOAPFaultException after evaluate the expression (or adding Watching in debug console) in the RequestContext object. More specifically, the error says:
javax.xml.ws.soap.SOAPFaultException: Server did not recognize the value of HTTP Header SOAPAction: .
Why this is happening?
This error occurs due to the Map custom implementation of the class RequestContext.java made in the jaxws-rt (version 2.1.4) library.
The custom map implementation has a Fallback mechanism that when the variable "mapView.fallbackMap" is null the header "soapAction" is clear in the fallback for-loop. Check the snipped bellow:
/**
* Fill a {#link Packet} with values of this {#link RequestContext}.
*/
public void fill(Packet packet) {
if(mapView.fallbackMap==null) {
if (endpointAddress != null)
packet.endpointAddress = endpointAddress;
packet.contentNegotiation = contentNegotiation;
if (soapAction != null) {
packet.soapAction = soapAction;
}
if(!others.isEmpty()) {
packet.invocationProperties.putAll(others);
//if it is not standard property it deafults to Scope.HANDLER
packet.getHandlerScopePropertyNames(false).addAll(others.keySet());
}
} else {
Set<String> handlerScopePropertyNames = new HashSet<String>();
// fallback mode, simply copy map in a slow way
for (Entry<String,Object> entry : mapView.fallbackMap.entrySet()) {
String key = entry.getKey();
if(packet.supports(key))
packet.put(key,entry.getValue());
else
packet.invocationProperties.put(key,entry.getValue());
//if it is not standard property it deafults to Scope.HANDLER
if(!super.supports(key)) {
handlerScopePropertyNames.add(key);
}
}
if(!handlerScopePropertyNames.isEmpty())
packet.getHandlerScopePropertyNames(false).addAll(handlerScopePropertyNames);
}
}
Additionaly, the variable "mapView.fallbackMap" is filled every time a method from Map not supported by the custom implementation is made, for example, the "entrySet" or "size" method. Therefore, everytime we call a "Watch" or "Evaluate" operation to debug the RequestContext in IntelliJ IDEA the fallback is filled broken the SOAP call.
So the solution, does not use to debug the RequestContext before SOAP call, or make sure to remove the following property before to execute the request:
((BindingProvider) service).getRequestContext().remove("javax.xml.ws.soap.http.soapaction.uri")
Related
I'm writing a generic proto 2 bson document converter and using the descriptors to map the values. I'm having a time finding the correct method within the java protobuf api to tell me if a oneof is set.
What I have so far is:
} else if (descriptor.getContainingOneof() != null) {
final OneofDescriptor containingOneof = descriptor.getContainingOneof();
for (FieldDescriptor oneOfField : containingOneof.getFields()){
//how to check if the oneof is set and how to find which field in the oneof has the value?
}
I am toying with this but isInitialized returns true even if the oneof isn't set.
if (message.hasField(descriptor)) {
final boolean initialized = containingOneof.getOptions().isInitialized();
for (var oneOfItem : containingOneof.getFields()) {
var field = message.getField(oneOfItem);
if(field != null) {
//the field
}
}
}
I suspect you want message.getOneofFieldDescriptor(containingOneof). From the docs:
Obtains the FieldDescriptor if the given oneof is set. Returns null if no field is set.
Note that your isInitialized() call is just checking whether the OneofOptions message associated with the oneof is initialized - it has nothing to do with message.
I have a Spring Boot project in Kotlin which uses a custom locale interceptor to know from which .properties file it load messages.
This works pretty fine. Also I have a custom annotation which is simple and has a default message value, as follow:
#Target(AnnotationTarget.Field)
#Constraint(validatedBy = [MyCustomValidator::class])
annotation class MyAnnotation(
val message: String = "{javax.validation.constraints.MyAnnotation.message}",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = []
)
class MyCustomValidator : ConstraintValidator<MyAnnotation, String> {
override fun isValid(value: String, context: ConstraintValidatorContext) {
return true //Just to make it easy
}
}
The locale properties files contains the key MyAnnotation.value=This field is required and shows as the exception message.
The problem is when I want to add more validations and so, custom messages according to each condition. I read that I should disable the default constraint validator and add the messages, but it is not working.
For example, if I want to create a key at locale file as MyAnnotation.conditionOne, it still prints the message from MyAnnotation.value.
//The only thing that changes in MyAnnotation is that message: String = ""
//Assuming that ConditionOne and ConditionTwo has a isValid static method
class MyCustomValidator : ConstraintValidator<MyAnnotation, String> {
override fun isValid(value: String, context: ConstraintValidatorContext): Boolean {
context.disableDefaultConstraintViolation()
return if (!ConditionOne.isValid(value)) {
context
.buildConstraintViolationWithTemplate("{javax.validation.constraints.MyAnnotation.conditionOne}")
.addConstraintViolation()
false
}
else if (!ConditonTwo.isValid(value)) {
context
.buildConstraintViolationWithTemplate("{javax.validation.constraints.MyAnnotation.message}")
.addConstraintViolation()
false
}
else
true
}
}
Is this the right way to set the message?
As I can see on the example above, you added two constraint violation message but in the different if cases. For a getting several checks and violation messages from one validator you should have not placed return after each if cases, instead of this you can create a local boolean variable and set its value from each of if statement cases, and after all make return that variable.
Pay attention to a little thing: it is important for your validator to set a temporary boolean variable correctly, because if once your if was set into false that means ultimate return value should be false. Cuz there is a principle anyMatch(false) or allMatch(true)
I have a custom SOAP message handler for incoming messages that will run different code based on which operation is being called. My first try to get the operation name looked something liket this:
public boolean handleMessage(SOAPMessageContext context)
{
String op = context.get(MessageContext.WSDL_OPERATION);
...
This failed because the property MessageContext.WSDL_OPERATION appears to never be set. I then tried using this:
public boolean handleMessage(SOAPMessageContext context)
{
Map<?, ?> headers = (Map<?, ?>)context.get(MessageContext.HTTP_REQUEST_HEADERS);
ArrayList<String> SOAPAction = ((ArrayList<String>) headers.get("SOAPAction"));
String opName = SOAPAction.get(0);
//opName will be formatted like "urn#myOperation", so the prefix must be removed
opName = ((opName.replace("\"","").split("#"))[1]);
This works, but I'm concerned there could be situations where the header property "SOAPAction" isn't set (or doesn't even exist), or does not have the value that I'm expecting it to. I'm also a little concerned because I don't know if this is an "official" way to get the operation name - I figured it out by looking at the contents of context in the debugger.
Is there any better way to get the operation name when handling incoming SOAP messages?
You could call body.getElementName().getLocalName() to retrieve the name of SOAP body element of the message payload. It's a little bit verbose and manual but it works. You could have the following in your handler
if ((boolean) context.get(MessageContext.MESSAGE_INBOUND_PROPERTY){ //for requests only
SOAPEnvelope msg = context.getMessage().getSOAPPart().getEnvelope(); //get the SOAP Message envelope
SOAPBody body = msg.getBody();
String operationName = body.getChildNodes().item(1).getLocalName();
}
The result of the above code is guaranteed to carry the name of the operation as specified in your WSDL
EDIT: This solution is based solely on the condition that the web service is implemented as document/literal-wrapped or RPC/literal
I'm very late to this party but I tried to do this over the past week. The accepted answer doesn't actually work for every JAX-WS implementation (at least not that I tried).
I have been trying to make this work on standalone Metro in my development environment but also using Axis2 bundled with WebSphere 7 in a real environment.
I found the following works on Metro:
String operationName = body.getChildNodes().item(0).getLocalName();
and the following works on Axis2:
String operationName = body.getChildNodes().item(1).getLocalName();
What is happening is that Axis2 inserts a Node of type Text into the Body as the first child but Metro doesn't. This text node returns a null local name. My solution was to do the following:
NodeList nodes = body.getChildNodes();
// -- Loop over the nodes in the body.
for (int i=0; i<nodes.getLength(); i++) {
Node item = nodes.item(i);
// -- The first node of type SOAPBodyElement will be
// -- what we're after.
if (item instanceof SOAPBodyElement) {
return item.getLocalName();
}
}
As described in the comments we're actually looking for the first node of type SOAPBodyElement. Hopefully that will help out anyone else looking at this in the future.
The SOAPMessageContext contains this information and can be retrieved super easily like this:
public boolean handleMessage(SOAPMessageContext msgContext) {
QName svcn = (QName) smc.get(SOAPMessageContext.WSDL_SERVICE);
QName opn = (QName) smc.get(SOAPMessageContext.WSDL_OPERATION);
System.out.prinln("WSDL Service="+ svcn.getLocalPart());
System.out.prinln("WSDL Operation="+ opn.getLocalPart());
return true;
}
in case if someone searches for "elegant" way to get needed properties use
for(Map.Entry e : soapMessageContext.entrySet()){
log.info("entry:"+ e.getKey() + " = " + e.getValue());
}
then decide what info you need and get it!
soapMessageContext.get(YOUR_DESIRED_KEY);
I'm trying to log the contents of the HttpServletRequest attributes collection. I need to do this when the servlet first starts, and again right before the servlet is finished. I'm doing this in an attempt to understand a crufty and ill-maintained servlet. Because I need to have as little impact as possible, servlet filters are not an option.
So here's the problem. When the servlet starts, I'll iterate through the enumeration returned by HttpServletRequest.getAttributeNames(). However, when I want to iterate through it again, getAttributeNames().hasMoreElements() returns "false"! I can't find any way to "reset" the enumeration. What's worse is that, even if I add attributes to the collection using HttpServletRequest.setAttribute(), I still get a result of "false" when I call getAttributeNames().hasMoreElements().
Is this really possible? Is there really no way to iterate through the attribute names more than once?
By request, here's my code. It's pretty straightforward -- don't think I'm doing any funny stuff.
/**
*
* Returns the contents of the Attributes collection, formatted for the InterfaceTracker loglines
*
*/
#SuppressWarnings("unchecked")
public static String getAttributes(HttpServletRequest request) {
try {
StringBuilder toLog = new StringBuilder();
Enumeration attributeNames = request.getAttributeNames();
while(attributeNames.hasMoreElements()) {
String current = (String) attributeNames.nextElement();
toLog.append(current + "=" + request.getAttribute(current));
if(attributeNames.hasMoreElements()) {
toLog.append(", ");
}
}
return "TRACKER_ATTRIBUTES={"+ toLog.toString() + "}";
}
catch (Exception ex) {
return "TRACKER_ATTRIBUTES={" + InterfaceTrackerValues.DATA_UNKNOWN_EXCEPTION_THROWN + "}";
}
}
Perhaps you should post the code where you call HttpServletRequest.setAttribute().
At this point it would seem that your crufty and ill-maintained servlet is removing attributes between your two calls to getAttributeNames(), but without any code samples it's hard to say.
UPDATE
Nothing in your code is jumping out at me as being faulty... so I crafted an extremely simple test case inside handleRequest() and gave it a whirl (using jboss-eap-4.3 as my container). I had to manually set an attribute first, as my understanding of request attributes is they are always set server side (i.e. if I didn't set it then I didn't get any output as the Enumeration returned by getAttributeNames() was empty).
request.setAttribute("muckingwattrs", "Strange");
Enumeration attrs = request.getAttributeNames();
while(attrs.hasMoreElements()) {
System.out.println(attrs.nextElement());
}
System.out.println("----------------------------");
Enumeration attrs2 = request.getAttributeNames();
while(attrs2.hasMoreElements()) {
System.out.println(attrs2.nextElement());
}
output
INFO [STDOUT] muckingwattrs
INFO [STDOUT] ----------------------------
INFO [STDOUT] muckingwattrs
So perhaps your container doesn't implement getAttributeNames() correctly? Maybe try an extremely simple test case like mine directly in handleRequest() or doGet()/doPost().
I am trying to read the authorization header for an HTTP request (because I need to add something to it), but I always get null for the header value. Other headers work fine.
public void testAuth() throws MalformedURLException, IOException{
URLConnection request = new URL("http://google.com").openConnection();
request.setRequestProperty("Authorization", "MyHeader");
request.setRequestProperty("Stackoverflow", "anotherHeader");
// works fine
assertEquals("anotherHeader", request.getRequestProperty("Stackoverflow"));
// Auth header returns null
assertEquals("MyHeader", request.getRequestProperty("Authorization"));
}
Am I doing something wrong? Is this a "security" feature? Is there a way to make this work with URLConnection, or do I need to use another HTTP client library?
Apparently, it's a security "feature". The URLConnection is actually an instance of sun.net.www.protocol.http.HttpURLConnection. It defines getRequestProperty as:
public String getRequestProperty (String key) {
// don't return headers containing security sensitive information
if (key != null) {
for (int i=0; i < EXCLUDE_HEADERS.length; i++) {
if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) {
return null;
}
}
}
return requests.findValue(key);
}
The EXCLUDE_HEADERS array is defined as:
// the following http request headers should NOT have their values
// returned for security reasons.
private static final String[] EXCLUDE_HEADERS = {
"Proxy-Authorization",
"Authorization"
};
I am not happy about the extra dependencies, but following the suggestion to switch to Commons Http solved the immediate problem for me.
I'd still like to know what the problem was with my original code.
As Devon's answer correctly states: it's not a bug, it's a "security" feature 😉
But you don't have to switch to a different library: it is always possible to access the underlying MessageHeader-collection via reflection and extract the "Authorization"-header value.
After some headscratch i've managed to come up with a working snippet here.
Have you tried using URLConnection.addRequestProperty()?
This is how I use to add HTTP Request Headers.