This page provides documentation about how to provide your own XMCDA Web Service in Java. We assume you know what is an XMCDA Web Service and have read the documentation about How to create an XMCDA web service-able program?. You should probably download the diviz client and try it a bit to get at ease with the concept of an XMCDA web service, if not done already.
Note that if you use types for which parsing has been implemented in the J-MCDA library (see below), you do not need to know how to parse XML files to provide your own XMCDA web service.
Note that most of the documentation on the Decision Deck website is language independent, while this document explains how to implement an XMCDA Web Service in Java.
Let‘s start with an example: the CutRelation XMCDA web service. It is not necessary to understand exactly what it does to follow this explanation, but here is a short description. The service may be described as taking a fuzzy relation as input and a cut threshold, and outputting a binary relation. If you don't know what are fuzzy relations, think in terms of matrixes: the service takes as input a matrix having Alternatives as rows and Alternatives as columns. The input matrix has, for each pairs (a1, a2), a real value between zero or one that we write V(a1, a2). The service also takes a real number known as the cutting threshold, also between zero and one, that we write C. The service then returns a matrix having Alternatives as rows and Alternatives as columns, the same set of alternatives as the input matrix, and having for each pair (a1, a2) the value one if the input matrix has V(a1, a2) > C, and the value zero otherwise.
As you can see, the supplementary code necessary to turn a class into a web service is minimal. This allows you to concentrate on implementing what your service should really do, in this example, cut the matrix, rather than how it should parse the input files, manage exceptions, and so on. Have a look at other examples available in the same package if you wish so. Note that these examples are also published as web services and are reachable from e.g. the diviz client.
To provide your own XMCDA Web Service:
The @XWSInput and @XWSOutput annotations may come with a "name" field, as in the example, which gives the name of the XML file to be read from, in case of an input, or to be written, in case of an output. This must be a simple file name with no path. The web service executor will ensure the files are read and written at their expected locations. To mark an input as optional, use the field "optional" from @XWSInput.
To start your web service from a command line, use the main method from the class XWSExecutor and give as arguments the qualified name of your class (in our example this would be org.decisiondeck.jmcda.xws.ws.XWSCutRelation), the input directory where the input files are to be found, and the output directory where the input files will be written.
When the XWSExecutor executes your web service, it knows the input directory where the files lie. This is typically given as argument by the application launcher. It reads, from your @XWSInput annotated fields, the source file names. For each input, it considers the corresponding field type. This indicates the XWSExecutor what it is supposed to search for in the input file and how it should be parsed. For example, if your field type is Set<Alternative>, the executor will search in the input file for an XML tag "Alternatives". You do not need to worry about how it does the job, however. The only thing you must ensure is that the input field type you use is a type the executor knows.
The executor also sets the input and output directory to the fields annotated with @XWSInputDirectory and @XWSOutputDirectory, if these exist. When this is done and the input files have been parsed, and supposing no exception occurred while parsing, the executor executes your web service (heh), that is, the #execute() method your class implements. Supposedly, this computes some results using the input data and sets the output fields appropriately. When the execute method returns, the executor writes the output data to the corresponding output files. As with the input parsing, writing the output requires the executor to know how to transform each of your output fields to an XMCDA conforming file. It will choose a transformer by looking at the declared field type. Once again you may use built-in types, which are those for which a transformer exists, or provide your own transformer.
The type of the input and output fields will determine how automatic reading and writing will occur. The executor of the web service must know how to read a given type from an XMCDA conforming file. Here is a list of types you may use without adding your own file reader. The list describes the types you may use by increasing amount of work for the executor, or equivalently, by decreasing amount of work for you. You probably want to jump to the last line of this list, other types are provided for more subtle use cases, for example if parsing of one input file depends on the state of an other input file.
The easy rule to remember, summarizing this subsection, is: if you mark the field as optional, be prepared for its value to be possibly null. If the field is not annotated as optional, its value is guaranteed not be null when the execute method is called by the executor. Read on for the details.
If for a given field the corresponding input file does not exist (or perhaps if one of the transformers in the chain returns null), if the field is not marked as optional, the executor will raise an exception. If the file does not exist and the field is marked as optional, the field value will be null. This remark holds independently of the field type, including if the type is File.
If an exception happens while reading one of the input files (thus, if the field is not optional and the file is missing; or if the parsing failed for some reason), the executor does not call the execute method. Instead, it assigns the exceptions to the field annotated with @XWSException, and it immediately proceeds to writing the output. If the field annotated with @XWSException is also annotated with @XWSOutput, the exceptions will be written to an output file. This is probably the only field that will be written in this case, considering that the other fields have not received a chance to be initialized by the #execute() method. However, you may have initialized other fields before starting the executor, for example in the class constructor. This is useful if you want to give default return values even in case of invalid input files. Not that we think doing this is a good idea, generally speaking...
The executor will try to parse each input file and collect exceptions instead of stopping at the first invalid input file, which is why the field annotated with @XWSException must be a list. This is useful to give the caller more informations about why its input data streams are not valid.
If the execute method itself throws an exception, the executor collects it in the same exceptions list. It then proceeds to write the output files, as indicated above. Note that if the execute method did assign a certain number of output fields before throwing, those values will be written to output files.
Your execute method may also populate the exceptions list on its own.
The J-MCDA project also features a collection of sample XMCDA files that are correctly parsed by its parsing methods. You may want to refer to these files to know what the parsers expect, and direct the users of your web services to appropriate example files if you use the built-in parsers.
You may also start your web service programmatically by instantiating the XWSExecutor class. Methods are provided to enable easy testing, e.g. disable writing, simulate input files, etc. Refer to the javadoc for details. This is especially useful if you like unit testing. Many example tests are available from the XMCDA-WS-Example module, refer to the XWSServicesTest class. This also permits you to write your own main method to replace the one of the executor if deemed necessary.