Hello All,
I'm experiencing some issues with the WADL extension of Restlet
(version 2.0.0). The method describeGet(MethodInfo) works fine for
resources with URI of the form /resource but does not return information
about the method for resources of the form /resource/{resource_id}. For
example the curl command:
curl -X OPTIONS http://localhost:3000/algorithm
returns the following WADL document:
<?xml version="1.0" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="wadl2html.xslt"?>
<application xmlns="http://wadl.dev.java.net/2009/02">
<doc title="Algorithm Service"/>
<resources>
<resource path="algorithm">
<doc title="Algorithm Service">This Service can provide a
listing of Algorithms that YAQP application supports. The result can be
provided either as a list of URIs where each one corresponds to the
according service, or as an RDF document with complete information about
each Algorithm.</doc>
<method name="GET">
<doc>Retrieve a list of Training and Prediction Algorithms
supported by YAQP.</doc>
<response>
<representation mediaType="application/rdf+xml">
<doc>Supported Algorithms in RDF format</doc>
</representation>
<representation mediaType="text/uri-list">
<doc>Supported Algorithms in URI-List format</doc>
</representation>
</response>
</method>
</resource>
</resources>
</application>
which is fine since all information about the GET method are included in
it, while the following request:
curl -X OPTIONS http://localhost:3000/algorithm/mlr
will return just:
<?xml version="1.0" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="wadl2html.xslt"?>
<application xmlns="http://wadl.dev.java.net/2009/02">
<doc title="QSAR Training Algorithm Web Service"/>
<resources>
<resource path="algorithm/mlr">
<doc title="QSAR Training Algorithm Web Service">This service
aims at generating predictive QSAR models and also supports auxiliary
algorithms for data filtering. Domain of applicability algorithms are
also supported (based on the leverages algorithm). This service is
compliant to the OpenTox API (version 1.1. and 1.2 - compliant) which
can be found at http://www.opentox.org/dev/apis/api-1.1/Algorithm</doc>
</resource>
</resources>
</application>
which does not include information about the method GET. Let me mention
that I have explicitly declared the allowed methods in both resources
and have overridden the methods describe and describeGet in both cases
as well. A simple System.out.println("x") inside describeGet reveals
that the method is invoked however the included method specific
information are missing from the WADL representation of the resource
at /resource/{resource_id}. Find attached the related source code files.
Is there something wrong in the source code or is it a bug of WADL?
Cheers,
Sopasakis Pantelis
Dipl. Chemical Engineer,
MSc. Applied Mathematics
National Technical University of Athens
Automatic Control Laboratory
email: [email protected]
tel(office): +30 210 7723236
------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=2655150/*
*
* YAQP - Yet Another QSAR Project:
* Machine Learning algorithms designed for the prediction of toxicological
* features of chemical compounds become available on the Web. Yaqp is developed
* under OpenTox (http://opentox.org) which is an FP7-funded EU research project.
* This project was developed at the Automatic Control Lab in the Chemical Engineering
* School of the National Technical University of Athens. Please read README for more
* information.
*
* Copyright (C) 2009-2010 Pantelis Sopasakis & Charalampos Chomenides
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Contact:
* Pantelis Sopasakis
* [email protected]
* Address: Iroon Politechniou St. 9, Zografou, Athens Greece
* tel. +30 210 7723236
*/
package org.opentox.www.rest.resources;
import com.hp.hpl.jena.ontology.OntModel;
import java.util.ArrayList;
import java.util.Set;
import org.opentox.components.Algorithm;
import org.opentox.components.ComponentList;
import org.opentox.components.collections.Algorithms;
import org.opentox.components.publish.DataModelPublishable;
import org.opentox.components.publish.Publishable;
import org.opentox.components.publish.Publisher;
import org.opentox.core.exceptions.Cause;
import org.opentox.core.exceptions.YaqpException;
import org.opentox.util.logging.YaqpLogger;
import org.opentox.util.logging.levels.Fatal;
import org.opentox.www.rest.RestProcessor;
import org.opentox.www.rest.URITemplate;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.ReferenceList;
import org.restlet.data.Status;
import org.restlet.ext.wadl.DocumentationInfo;
import org.restlet.ext.wadl.MethodInfo;
import org.restlet.ext.wadl.RepresentationInfo;
import org.restlet.ext.wadl.RequestInfo;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.representation.Variant;
import org.restlet.resource.Get;
import org.restlet.resource.Options;
import org.restlet.resource.ResourceException;
/**
* Representation of the set of all algorithms.
* @author Pantelis Sopasakis
* @author Charalampos Chomenides
*/
public class AlgorithmsResource extends YaqpResource {
public static final URITemplate template = new URITemplate("algorithm", null, null);
private static OntModel allAlgorithms = null;
@Override
protected void doInit() throws ResourceException {
super.doInit();
initialize(
MediaType.TEXT_HTML,
MediaType.APPLICATION_RDF_XML,
MediaType.APPLICATION_WADL,
MediaType.APPLICATION_RDF_TURTLE,
MediaType.TEXT_URI_LIST);
Set<Method> allowedMethods = getAllowedMethods();
allowedMethods.add(Method.GET);
allowedMethods.add(Method.OPTIONS);
setAllowedMethods(allowedMethods);
}
/**
* Representation of the set of available algorithms.
* @param variant
* The requested variant for the algorithm list.
* @return
* Representation of list of algorithms
* @throws ResourceException
* In case the representation could not be generated
*/
@Get
@Override
protected Representation get(final Variant variant) throws ResourceException {
final MediaType requestMediaType = variant.getMediaType();
if (requestMediaType.isCompatible(MediaType.APPLICATION_WADL)) {
return options(variant);
}
Set<Algorithm> algorithms = Algorithms.getAll();
if (requestMediaType.isCompatible(MediaType.TEXT_ALL)) {
ReferenceList refList = new ReferenceList();
for (Algorithm algorithm : algorithms) {
refList.add(algorithm.getUri());
}
if (requestMediaType == MediaType.TEXT_URI_LIST) {
return refList.getTextRepresentation();
} else if (requestMediaType == MediaType.TEXT_HTML) {
Representation rep = refList.getWebRepresentation();
rep.setMediaType(requestMediaType);
return rep;
}
}
ComponentList<Algorithm> algorithmList = new ComponentList<Algorithm>(new ArrayList<Algorithm>(algorithms));
try {
Publishable toBePublished;
/** List of algorithms is static, so there is no need to create it
on request; using cached version instead! **/
if (allAlgorithms == null) {
final Publisher publisher = new Publisher(requestMediaType);
DataModelPublishable p = (DataModelPublishable) publisher.process(algorithmList);
allAlgorithms = p.getOntModel();
toBePublished = p;
} else {
toBePublished = new DataModelPublishable(allAlgorithms, requestMediaType);
}
RestProcessor r = new RestProcessor(false);
return r.process(toBePublished);
} catch (YaqpException ex) {
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
YaqpLogger.LOG.log(new Fatal(getClass(), "Unexpected condition : " + ex.toString()));
String message =
"Unexpected condition while generating the representation "
+ "of the list of all algorithms on the server. We apologize for the "
+ "inconvenience. This issue has been logged and the server administrators will try to "
+ "fix it as soon as possible.\n";
return errorReport(Cause.PublicationError, message, ex.getMessage(), requestMediaType, false);
} finally {
algorithmList.clear();
}
}
@Override
protected Representation describe() {
setName("Algorithm Service");
setDescription("This Service can provide a listing of Algorithms that YAQP"
+ " application supports. The result can be provided either as a list"
+ " of URIs where each one corresponds to the according service,"
+ " or as an RDF document with complete information about each Algorithm.");
return super.describe();
}
@Override
public Representation options(Variant variant) {
return options();
}
@Override
protected void describeGet(MethodInfo info) {
super.describeGet(info);
info.setDocumentation("Retrieve a list of Training and Prediction Algorithms supported by YAQP.");
ArrayList<RepresentationInfo> repList = new ArrayList<RepresentationInfo>();
RepresentationInfo rdfRep = new RepresentationInfo(MediaType.APPLICATION_RDF_XML);
rdfRep.setDocumentation("Supported Algorithms in RDF format");
RepresentationInfo urilistRep = new RepresentationInfo(MediaType.TEXT_URI_LIST);
urilistRep.setDocumentation("Supported Algorithms in URI-List format");
repList.add(rdfRep);
repList.add(urilistRep);
info.getResponse().setRepresentations(repList);
}
}
package org.opentox.www.rest.resources;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import org.kinkydesign.decibell.exceptions.DuplicateKeyException;
import org.kinkydesign.decibell.exceptions.ImproperRegistration;
import org.opentox.YAQP;
import org.opentox.components.Algorithm;
import org.opentox.components.Task;
import org.opentox.components.collections.Algorithms;
import org.opentox.components.collections.AlgorithmsOntModelCache;
import org.opentox.components.publish.DataModelPublishable;
import org.opentox.components.publish.HTMLPublishable;
import org.opentox.components.publish.Publishable;
import org.opentox.components.publish.Publisher;
import org.opentox.config.Configuration;
import org.opentox.core.exceptions.Cause;
import org.opentox.core.exceptions.YaqpException;
import org.opentox.core.interfaces.JProcessor;
import org.opentox.qsar.processors.trainers.AbstractTrainer;
import org.opentox.util.logging.YaqpLogger;
import org.opentox.util.logging.levels.ScrewedUp;
import org.opentox.util.tasks.TaskPool;
import org.opentox.util.tasks.TaskWrapper;
import org.opentox.www.rest.RestProcessor;
import org.opentox.www.rest.URITemplate;
import org.opentox.www.rest.YaqpForm;
import org.opentox.www.rest.services.FilteringService;
import org.opentox.www.rest.services.TrainingService;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.ext.wadl.MethodInfo;
import org.restlet.representation.Representation;
import org.restlet.representation.Variant;
import org.restlet.resource.ResourceException;
/**
*
* @author Pantelis Sopasakis
* @author Charalampos Chomenides
*/
public class AlgorithmResource extends YaqpResource {
/** URI Template */
public static final URITemplate template = new URITemplate("algorithm", "algorithm_id", null);
/**
* Name of the algorithm.
*/
private String algorithmName;
private String acceptString = null;
@Override
protected void doInit() throws ResourceException {
super.doInit();
setAutoCommitting(false);
initialize(
MediaType.TEXT_HTML,
MediaType.APPLICATION_RDF_XML,
MediaType.register("application/rdf+xml-abbrev", NEWLINE),
MediaType.APPLICATION_WADL,
MediaType.APPLICATION_RDF_TURTLE,
MediaType.APPLICATION_PDF,
MediaType.TEXT_URI_LIST,
MediaType.TEXT_RDF_N3,
MediaType.TEXT_RDF_NTRIPLES);
algorithmName = Reference.decode(getRequest().getAttributes().get(template.getPrimaryKey()).toString());
acceptString = getRequest().getResourceRef().getQueryAsForm().getFirstValue("accept");
allowMethods(Method.GET, Method.OPTIONS, Method.POST);
}
@Override
protected Representation describe() {
setName("QSAR Training Algorithm Web Service");
setDescription("This service aims at generating predictive QSAR models and also supports auxiliary algorithms " +
"for data filtering. Domain of applicability algorithms are also supported (based on the leverages algorithm). " +
"This service is compliant to the OpenTox API (version 1.1. and 1.2 - compliant) which can be found at " +
"http://www.opentox.org/dev/apis/api-1.1/Algorithm");
return super.describe();
}
@Override
protected void describeGet(MethodInfo info) {
info.setDocumentation("Algorithm representation in one of the supported MIME types. " +
"Ask for the representation of an algorithm in RDF format (application/rdf+xml) that contains " +
"all meta-data about the algorith. Every algorithm is also available in HTML format (text/html) to " +
"be presented to an end-user via a browser.");
java.util.ArrayList<org.restlet.ext.wadl.RepresentationInfo> repList =
new java.util.ArrayList<org.restlet.ext.wadl.RepresentationInfo>();
org.restlet.ext.wadl.RepresentationInfo rdfRep = new org.restlet.ext.wadl.RepresentationInfo(MediaType.APPLICATION_RDF_XML);
rdfRep.setDocumentation("Supported Algorithms in RDF format (Representation containing all meta-data related to the algorithm).");
org.restlet.ext.wadl.RepresentationInfo turtleRep = new org.restlet.ext.wadl.RepresentationInfo(MediaType.APPLICATION_RDF_TURTLE);
rdfRep.setDocumentation("An equivalent representation of the RDF one ");
org.restlet.ext.wadl.RepresentationInfo urilistRep = new org.restlet.ext.wadl.RepresentationInfo(MediaType.TEXT_URI_LIST);
urilistRep.setDocumentation("The URI of the requested algorithm.");
repList.add(rdfRep);
repList.add(urilistRep);
repList.add(turtleRep);
info.getResponse().setRepresentations(repList);
super.describeGet(info);
}
@Override
protected Representation get(final Variant variant) throws ResourceException {
if (acceptString != null) {
variant.setMediaType(MediaType.valueOf(acceptString));
}
Representation rep = null;
Algorithm alg = Algorithms.getByName(algorithmName);
if (alg == null) {
return errorReport(Cause.AlgorithmNotFoundInCache, "Algorithm Not Found",
"The algorithm you are looking for was not found on the server thus causing "
+ "this exceptional event. You can get a complete list of all available algorithms at "
+ Configuration.BASE_URI + "/algorithm", variant.getMediaType(), false);
}
try {
Publishable x = null;
if (MediaType.TEXT_HTML.equals(variant.getMediaType())) {
x = new HTMLPublishable(Algorithms.getByName(algorithmName).inHtml());
} else {
x = new DataModelPublishable(AlgorithmsOntModelCache.forName(algorithmName), variant.getMediaType());
}
RestProcessor p = new RestProcessor(false);
rep = p.process(x);
} catch (YaqpException ex) {
toggleNotFound();
if (Cause.AlgorithmNotFoundInCache.equals(ex.getCode())) {
String details = "The algorithm you are looking for was not found on the server thus causing "
+ "this exceptional event. You can get a complete list of all available algorithms at "
+ Configuration.BASE_URI + "/algorithm";
getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);
return errorReport(Cause.AlgorithmNotFoundInCache, ex.getMessage(), details, variant.getMediaType(), false);
} else {
YaqpLogger.LOG.log(new ScrewedUp(getClass(), ex.getMessage(), ex));
toggleServerError();
return errorReport(Cause.PublicationError, ex.getMessage(), null, variant.getMediaType(), false);
}
}
return rep;
}
/**
* Exploits the {...@link TrainingService } to train a new model, and returns the
* trained model in a supported mime type accerding to the request of the client.
* @param entity
* The parameters posted by the client as application/x-www-form-urlencoded
* @param variant
* The requested variant for the respponse from the server including the
* Accept field in the header of the request.
* @return
* Representation of the trained model.
* @throws ResourceException
* In case the training cannot be performed due to client or server error
*/
@Override
@SuppressWarnings({"unchecked"})
protected Representation post(Representation entity, final Variant variant) throws ResourceException {
// CHECK IF THE ALGORITHM EXISTS...
final Algorithm algor1thm = Algorithms.getByName(algorithmName);
if (algor1thm == null) {
toggleNotFound();
String message =
"You have requested an algorithm which does not exist (" + algorithmName + "). You can"
+ "get a complete list of all available algorithms at " + Configuration.BASE_URI + "/algorithm" + NEWLINE;
return errorReport(Cause.AlgorithmNotFoundInCache, message, null, variant.getMediaType(), false);
}
AbstractTrainer trainer = Algorithms.getTrainerByName(algorithmName);
/*
* Create a task whose URI will be the response to the client.
*/
UUID uuid = UUID.randomUUID();
Task newTask = new Task(uuid.toString(), algor1thm, Task.TaskStatus.RUNNING, 202, System.currentTimeMillis(), 0, 0, null, null);
try {
newTask.register(YAQP.getDb());
} catch (DuplicateKeyException ex) {
YaqpLogger.LOG.log(new ScrewedUp(getClass(), "Task already exists in the database. Exception :" + ex));
toggleServerError();
return errorReport(Cause.TrainingError, "Internal Server Error", null, variant.getMediaType(), false);
} catch (ImproperRegistration ex) {
YaqpLogger.LOG.log(new ScrewedUp(getClass(), "Task could not be registered in the database. Exception :" + ex));
toggleServerError();
return errorReport(Cause.TrainingError, "Internal Server Error", null, variant.getMediaType(), false);
}
JProcessor trainingService =
algor1thm.equals(Algorithms.filter()) ? new FilteringService(uuid) : new TrainingService(uuid, trainer);
/**
* Start a task in the background which carries out the training.
* This task is fed with the parameters provided by the client, in terms of
* the corresponding form.
*/
TaskWrapper backgroundJob = new TaskWrapper();
backgroundJob.setProcessor(trainingService);
backgroundJob.setFood(new YaqpForm(entity));
backgroundJob.setUuid(uuid);
TaskPool.POOL.run(backgroundJob);
/**
* The training service returns the task URI withinh the response body
* in text/uri-list format.
*/
toggleAccepted();
Representation representation = null;
if (variant.getMediaType() == MediaType.APPLICATION_RDF_XML) {
try {
final Publisher publisher = new Publisher(variant.getMediaType());
Publishable p = publisher.process(newTask);
RestProcessor r = new RestProcessor(false);
return r.process(p);
} catch (YaqpException ex) {
toggleServerError();
return errorReport(Cause.TrainingError, ex.getMessage(), null, variant.getMediaType(), false);
}
} else {
representation = sendMessage(Configuration.BASE_URI + "/task/" + uuid.toString() + NEWLINE);
representation.setMediaType(MediaType.TEXT_URI_LIST);
}
return representation;
}
}