How to check if MTOM attachment is empty - web-services

I am developing webservice based on CXF. One of the requests is that client should be able to upload the optional PDF file as a part of message. This was pretty trivial:
I have added this with getter and setter to my transfer object:
#XmlMimeType("application/octet-stream")
#XmlElement(name = "InvoicePdf", required = false)
private DataHandler invoicePdf = null;
I have also enabled support for MTOM:
Endpoint endpoint = Endpoint.publish("/myWs", new WsImpl(getServletContext()));
SOAPBinding binding = (SOAPBinding) endpoint.getBinding();
binding.setMTOMEnabled(true);
And the usage:
DataHandler pdf2 = p_invoice.getInvoicePdf();
//pdf2.getInputStream();
//pdf2.writeTo(outputstream);
Everything works great. I am able to receive and process the file. However there might be the case when client do not upload the file since it is optional. The problem is that even though the client do not sent the file I am not able to notice it.
pdf2 is not null
pdf2.getInputStream() is not null
pdf2.getInputStream() contains some data. I would like to skip parsing the input stream and looking for PDF signature. Since it is a lot easier to forward the inputstrem to desired outpustream (write to file)
I have not found in DataHandler or DataSource (pdf2.getDataSource() ) API any appropriate method or field for determining file existance. I see in debug that the empty DataHandler contains DataSource which length is 9, which is a lot less then correct PDF file. Unfortunately the length property is not accessible at all.
Any idea how to determine if the file was sent or not?

The solution is to skip xml tag for this attachment in SOAP message. So my mistake was sending empty tag:
<InvoicePdf></InvoicePdf>
Then you get behavior described in question. However if you skip this tag entirely then DataHandel is null, so I am able to distinguish is file was sent or not.

Related

How to specify the database in an ArangoDb AQL query?

If have multiple databases defined on a particular ArangoDB server, how do I specify the database I'd like an AQL query to run against?
Running the query through the REST endpoint that includes the db name (substituted into [DBNAME] below) ie:
/_db/[DBNAME]/_api/cursor
doesn't seem to work. The error message says 'unknown path /_db/[DBNAME]/_api/cursor'
Is this something I have to specify in the query itself?
Also: The query I'm trying to run is:
FOR col in COLLECTIONS() RETURN col.name
Fwiw, I haven't found a way to set the "current" database through the REST API. Also, I'm accessing the REST API from C++ using fuerte.
Tom Regner deserves primary credit here for prompting the enquiry that produced this answer. I am posting my findings here as an answer to help others who might run into this.
I don't know if this is a fuerte bug, shortcoming or just an api caveat that wasn't clear to me... BUT...
In order for the '/_db/[DBNAME/' prefix in an endpoint (eg full endpoint '/_db/[DBNAME/_api/cursor') to be registered and used in the header of a ::arangodb::fuerte::Request, it is NOT sufficient (as of arangodb 3.5.3 and the fuerte version available at the time of this answer) to simply call:
std::unique_ptr<fuerte::Request> request;
const char *endpoint = "/_db/[DBNAME/_api/cursor";
request = fuerte::createRequest(fuerte::RestVerb::Post,endpoint);
// and adding any arguments to the request using a VPackBuilder...
// in this case the query (omitted)
To have the database name included as part of such a request, you must additionally call the following:
request->header.parseArangoPath(endpoint);
Failure to do so seems to result in an error about an 'unknown path'.
Note 1: Simply setting the database member variable, ie
request->header.database = "[DBNAME]";
does not work.
Note 2: that operations without the leading '/_db/[DBNAME]/' prefix, seem to work fine using the 'current' database. (which at least for me, seems to be stuck at '_system' since as far as I can tell, there doesn't seem to be an endpoint to change this via the HTTP REST Api.)
The docs aren't very helpful right now, so just incase someone is looking for a more complete example, then please consider the following code.
EventLoopService eventLoopService;
// adjust the connection for your environment!
std::shared_ptr<Connection> conn = ConnectionBuilder().endpoint("http://localhost:8529")
.authenticationType(AuthenticationType::Basic)
.user(?) // enter a user with access
.password(?) // enter the password
.connect(eventLoopService);
// create the request
std::unique_ptr<Request> request = createRequest(RestVerb::Post, ContentType::VPack);
// enter the database name (ensure the user has access)
request->header.database = ?;
// API endpoint to submit AQL queries
request->header.path = "/_api/cursor";
// Create a payload to be submitted to the API endpoint
VPackBuilder builder;
builder.openObject();
// here is your query
builder.add("query", VPackValue("for col in collections() return col.name"));
builder.close();
// add the payload to the request
request->addVPack(builder.slice());
// send the request (blocking)
std::unique_ptr<Response> response = conn->sendRequest(std::move(request));
// check the response code - it should be 201
unsigned int statusCode = response->statusCode();
// slice has the response data
VPackSlice slice = response->slices().front();
std::cout << slice.get("result").toJson() << std::endl;

Activating Proxy service consumer from ABAP

I've just created a service consumer and generated its code from ABAP in order to send to the target WSDL system XML file which is created successfully.
The code should fill in the XML_FILE_INFO fields but have no idea if I did it OK:
The code to activate it:
" Call method, and receive a response
try.
lo_proxy->SET_MESSAGE(
exporting
INPUT = gs_input
importing
OUTPUT = ls_output
).
catch CX_AI_SYSTEM_FAULT INTO lo_AI_SYSTEM_FAULT. "lo_ai_system_fault_info.
catch CX_AI_APPLICATION_FAULT INTO LO_AI_APPLICATION_FAULT.
ENDTRY.
How should I fill the XML file's content in the GS_INPUT structure? IS putting the file's path in the FILE_NAME field enough?
If someone had already done something like the mentioned requirement I'd appreciate any help.
Thanks in advance.

How to modify the HTTP::Response after it has been written to

I'm trying to write some tooling for Crystal (specifically Kemal) where I can see if the response content type is text/html and modify the response body thats has already been written to the HTTP::Response before it is sent to the client by injecting an HTML element into the existing html response body.
I've noticed that HTTP::Server::Response is write-only, but things like Gzip::Writer are able to modify the body.
How can I modify the HTTP::Server::Response html body before it is sent to the client?
It's written in Crystal, so let's just take a look at the source on how others do this.
Taking the CompressHandler as an example, the basic idea is to replace the response's IO with something that allows the desired control:
context.response.output = Gzip::Writer.new(context.response.output, sync_close: true)
# ...
call_next(context)
So how can we make use of that to modify the response that's being written?
A naive (and slow) example would be to just keep hold of the original output and provide a IO::Memory instead:
client = context.response.output
io = IO::Memory.new
context.response.output = io
call_next(context)
body = io.to_s
new_body = inject_html(body)
client.print new_body
Of course that would only work when this handler comes before any handler that turns the response into non-plaintext (like the above CompressHandler).
A smarter solution would provide a custom IO implementation that just wraps the original IO, watching what's written to it and inject whatever it wants to inject at the right point. Examples of such wrapping IOs can be found at IO::Delimited, IO::Sized and IO::MultieWriter among others, the pattern is really common to prevent unnecessary allocations.

Retrieve service information from WFS GetCapabilities request with GeoExt

This is probably a very simple question but I just can't seem to figure it out.
I am writing a Javascript app to retrieve layer information from a WFS server using a GetCapabilities request using GeoExt. GetCapabilities returns information about the WFS server -- the server's name, who runs it, etc., in addition to information on the data layers it has on offer.
My basic code looks like this:
var store = new GeoExt.data.WFSCapabilitiesStore({ url: serverURL });
store.on('load', successFunction);
store.on('exception', failureFunction);
store.load();
This works as expected, and when the loading completes, successFunction is called.
successFunction looks like this:
successFunction = function(dataProxy, records, options) {
doSomeStuff();
}
dataProxy is a Ext.data.DataProxy object, records is a list of records, one for each layer on the WFS server, and options is empty.
And here is where I'm stuck: In this function, I can get access to all the layer information regarding data offered by the server. But I also want to extract the server information that is contained in the XML fetched during the store.load() (see below). But I can't figure out how to get it out of the dataProxy object, where I'm sure it must be squirreled away.
Any ideas?
The fields I want are contained in this snippet:
<ows:ServiceIdentification>
<ows:Title>G_WIS_testIvago</ows:Title>
<ows:Abstract/>
<ows:Keywords>
<ows:Keyword/>
</ows:Keywords>
<ows:ServiceType>WFS</ows:ServiceType>
<ows:ServiceTypeVersion>1.1.0</ows:ServiceTypeVersion>
<ows:Fees/>
<ows:AccessConstraints/>
Apparently,GeoExt currently discards the server information, undermining the entire premise of my question.
Here is a code snippet that can be used to tell GeoExt to grab it. I did not write this code, but have tested it, and found it works well for me:
https://github.com/opengeo/gxp/blob/master/src/script/plugins/WMSSource.js#L37

soap web service with symfony2

i need to create a webservices with symfony2 ive read the official article http://symfony.com/doc/current/cookbook/web_services/php_soap_extension.html
in the example it creates a instance of SoapServer with a parameter routing a .wsdl file, what is this file? i didnt found too much documentation about soap in symfony. some help with this please?
public function indexAction()
{
$server = new \SoapServer('/path/to/hello.wsdl');
$server->setObject($this->get('hello_service'));
$response = new Response();
$response->headers->set('Content-Type', 'text/xml; charset=ISO-8859-1');
ob_start();
$server->handle();
$response->setContent(ob_get_clean());
return $response;
}
I'm not sure if you've found your answer or not. Just for anybody else that might run into such a problem:
WSDL is the language in which the Web Services are defined and described. It's basically a XML file containing the input/output parameters of each and every function that are served by a server. it also contains some information about the server itself, that's providing the services.
in order to be able to create a webservice, you need to use the code you've provided, which would in fact prepare Symfony to serve the clients on "/path/to/hello.wsdl" (in my example this path is /YourDesiredPathFor/services.wsdl), and also, you need to provide a valid WSDL document that contains the information mentioned above in correct WSDL format. the problem is that Symfony (or even PHP itself for this matter) has no means of creating the file automatically.
to solve the matter, you need to use an external WSDL generator. I'd suggest using PHP-WSDL-Creator. It uses annotations written inside php files to create the WSDL file and also runs the SoapServer. that means that you won't even need the code that you've provided. it also has proper interfaces and addons that provide you with clients for different protocols and languages.
you need to tweak it a little bit though! if you want it to be by symfony standards, i think you would need to rewrite some parts of it; but if you'd like to use it as an external library, it could also work!
the way i did it was by copying the extracted files into ./vendor/php_wsdl/lib/php_wsdl/src (long it is, isn't it? maybe an easier path would also work!); then defined a php_wsdl.php in ./vender/php_wsdl/lib/php_wsdl:
<?php
require_once __DIR__. '/src/class.phpwsdl.php';
class WSDL_PhpWsdl extends PhpWsdl{
}
next, in the "./app/autoload.php", i added the following line to enable Symfony to use the created extension:
require_once __DIR__. '/../vendor/php_wsdl/lib/php_wsdl/php_wsdl.php';
just one thing! the extension needs a "cache" folder in order to cache the created wsdl files and all. unfortunately because i need to finish the project quickly, i don't have enough time to manage the cache folder as it should be. there are definitely better ways than my way, and i would really be glad to know about them.
anyway, now you need to use the extension's capabilities! to do so, i created a "ServerController" for the bundle i was using:
<?php
namespace Tara\PageControllerBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class ServiceController extends Controller
{
function wsdlAction(){
\PhpWsdlServers::$EnableRest = false;
$soap= \WSDL_PhpWsdl::CreateInstance(
null, // PhpWsdl will determine a good namespace
$this->generateUrl('service_wsdl', array(), true), // Change this to your SOAP endpoint URI (or keep it NULL and PhpWsdl will determine it)
__DIR__. '/cache', // Change this to a folder with write access
Array( // All files with WSDL definitions in comments
dirname(__FILE__). '/../Services/MyService.php'
),
null, // The name of the class that serves the webservice will be determined by PhpWsdl
null, // This demo contains all method definitions in comments
null, // This demo contains all complex types in comments
false, // Don't send WSDL right now
false // Don't start the SOAP server right now
);
if($soap->IsWsdlRequested()) // WSDL requested by the client?
$soap->Optimize=false; // Don't optimize WSDL to send it human readable to the browser
$soap->RunServer();
}
}
as you can see, the path to the cache folder is on a local directory, which means it has to be created manually and in ./src/Tara/PageControllerBundle/Controller (obviously in my case; you will need to change the paths). I'm sure there are better ways to manage the cache folder.
there is a line there:
dirname(__FILE__). '/../Services/MyService.php
this line tells the extension where to look for annotations in order to create the WSDL page. you also need to define a route to "service_wsdl":
service_wsdl:
pattern: /YourDesiredPathFor/services.wsdl
defaults: {_controller: TaraPageControllerBundle:Service:wsdl}
as you can see, the controller is ServiceController and the function responsible for it is wsdlAction; the exact function that was defined!
just as an example, i would provide my own MyService.php:
<?php
namespace Tara\PageControllerBundle\Services;
use Tara\PageControllerBundle\Model\...
/**
* #service Tara\PageControllerBundle\Services\MyService
*/
class MyService
{
/**
* Function Create
*
* #param string $link
* #param string $title
* #param string $image
* #param string $description
* #return boolean Status of the creation
* #pw_rest POST /YourDesiredPathForAction/create Create The Object
*
*/
public function Create($link, $title, $image, $description)
{
// your code that handles the data goes here. this is not a part of the annotations!
return (bool)$result;
}
}
now, you might be able to use a SoapClient to connect to your web service at
http: //your-server.something/YourDesiredPathFor/services.wsdl?wsdl
and call the Create function! you can also check the output of the extension by opening the above written address. the extension also provides a "human-readable" version at
http: //your-server.something/YourDesiredPathFor/services.wsdl.
i would be glad to know if this was any help to anyone! :)
SOAP is a more general concept that Symfony assumes you are familiar with. There is an example WSDL at the bottom of the page that you link to. Take a look at tutorials about SOAP and WSDL and then try to recreate what they're doing in the Symfony page.
SOAP Tutorial
WSDL Tutorial