teichmann@5835: package org.dive4elements.river.client.server; ingo@32: ingo@32: import org.w3c.dom.Document; ingo@32: ingo@1367: import org.apache.log4j.Logger; ingo@1367: teichmann@5835: import org.dive4elements.artifacts.common.ArtifactNamespaceContext; teichmann@5835: import org.dive4elements.artifacts.common.utils.ClientProtocolUtils; teichmann@5835: import org.dive4elements.artifacts.common.utils.XMLUtils; ingo@32: teichmann@5835: import org.dive4elements.artifacts.httpclient.exceptions.ConnectionException; teichmann@5835: import org.dive4elements.artifacts.httpclient.http.HttpClient; teichmann@5835: import org.dive4elements.artifacts.httpclient.http.HttpClientImpl; teichmann@5835: import org.dive4elements.artifacts.httpclient.http.response.DocumentResponseHandler; ingo@32: teichmann@5835: import org.dive4elements.river.client.shared.exceptions.ServerException; teichmann@5835: import org.dive4elements.river.client.shared.model.Artifact; teichmann@5835: import org.dive4elements.river.client.shared.model.ArtifactDescription; teichmann@5835: import org.dive4elements.river.client.shared.model.Data; teichmann@5835: import org.dive4elements.river.client.shared.model.DataItem; teichmann@5835: import org.dive4elements.river.client.client.services.StepForwardService; ingo@32: ingo@32: ingo@32: /** ingo@32: * This interface provides a method that bundles the artifact specific ingo@32: * operations FEED and ADVANCE. ingo@32: * ingo@32: * @author Ingo Weinzierl ingo@32: */ ingo@32: public class StepForwardServiceImpl ingo@32: extends AdvanceServiceImpl ingo@32: implements StepForwardService ingo@32: { ingo@1367: private static final Logger logger = ingo@1367: Logger.getLogger(StepForwardServiceImpl.class); ingo@1367: ingo@1367: ingo@215: /** XPath that points to the result type of a feed or advance operation.*/ ingo@215: public static final String XPATH_RESULT = "/art:result/@art:type"; ingo@32: ingo@230: /** XPath that points to the result type of a feed or advance operation.*/ ingo@230: public static final String XPATH_RESULT_MSG = "/art:result/text()"; ingo@230: ingo@32: /** A constant that marks errors.*/ ingo@215: public static final String OPERATION_FAILURE = "FAILURE"; ingo@215: ingo@215: /** The error message key that is thrown if an error occured while feeding ingo@215: * new data.*/ ingo@215: public static final String ERROR_FEED_DATA = "error_feed_data"; ingo@32: ingo@32: ingo@32: /** ingo@32: * This method wraps the artifact operations FEED and ADVANCE. FEED is ingo@32: * always triggerd, ADVANCE only, if there is at least one reachable state. ingo@32: * ingo@229: * @param locale The locale used for the request. ingo@32: * @param artifact The artifact that needs to be fed. ingo@32: * @param data An array of Data objects that contain the information that ingo@32: * ingo@32: * @return the modified artifact. ingo@32: */ raimund@1425: public Artifact go(String locale, Artifact artifact, Data[] data) ingo@215: throws ServerException ingo@215: { ingo@1367: logger.info("StepForwardServiceImpl.go"); ingo@229: raimund@1425: String url = getServletContext().getInitParameter("server-url"); raimund@1425: ingo@229: Artifact afterFeed = feed(url, locale, artifact, data); ingo@32: ingo@32: if (afterFeed == null) { ingo@1367: logger.warn("StepForwardService.feed() - FAILED"); ingo@215: throw new ServerException(ERROR_FEED_DATA); ingo@32: } ingo@32: ingo@32: ArtifactDescription desc = afterFeed.getArtifactDescription(); ingo@32: String[] reachable = desc.getReachableStates(); ingo@32: ingo@32: if (reachable == null || reachable.length == 0) { ingo@1367: logger.debug("Did not find any reachable state."); ingo@32: return afterFeed; ingo@32: } ingo@32: ingo@32: // We use the first reachable state as default target, maybe we need to ingo@32: // change this later. raimund@1425: return advance(locale, afterFeed, reachable[0]); ingo@32: } ingo@32: ingo@32: ingo@32: /** ingo@32: * This method triggers the FEED operation. ingo@32: * ingo@229: * @param url The url of the artifact server. ingo@32: * @param artifact The artifact that needs to be fed. ingo@32: * @param data An array of Data objects that contain the information that ingo@32: * are used for the FEED operation. ingo@32: * ingo@32: * @return a new artifact parsed from the description of FEED. ingo@32: */ ingo@229: protected Artifact feed( ingo@229: String url, ingo@229: String locale, ingo@229: Artifact artifact, ingo@229: Data[] data) ingo@215: throws ServerException ingo@215: { ingo@1367: logger.info("StepForwardServiceImpl.feed"); ingo@229: ingo@32: Document feed = ClientProtocolUtils.newFeedDocument( ingo@32: artifact.getUuid(), ingo@32: artifact.getHash(), ingo@32: createKVP(data)); ingo@32: ingo@229: HttpClient client = new HttpClientImpl(url, locale); ingo@32: ingo@32: try { ingo@32: Document description = (Document) client.feed( teichmann@5835: new org.dive4elements.artifacts.httpclient.objects.Artifact( ingo@32: artifact.getUuid(), ingo@32: artifact.getHash()), ingo@32: feed, ingo@32: new DocumentResponseHandler()); ingo@32: ingo@32: if (description == null) { ingo@1367: logger.warn("StepForwardService.feed() - FAILED"); ingo@215: throw new ServerException(ERROR_FEED_DATA); ingo@32: } ingo@32: ingo@32: String result = XMLUtils.xpathString( ingo@32: description, ingo@32: XPATH_RESULT, ingo@32: ArtifactNamespaceContext.INSTANCE); ingo@32: ingo@32: if (result == null || !result.equals(OPERATION_FAILURE)) { ingo@1367: logger.debug("StepForwardService.feed() - SUCCESS"); ingo@32: return (Artifact) new FLYSArtifactCreator().create(description); ingo@32: } ingo@215: else if (result != null && result.equals(OPERATION_FAILURE)) { ingo@230: String msg = XMLUtils.xpathString( ingo@230: description, ingo@230: XPATH_RESULT_MSG, ingo@230: ArtifactNamespaceContext.INSTANCE); ingo@230: throw new ServerException(msg); ingo@215: } ingo@32: } ingo@32: catch (ConnectionException ce) { ingo@1367: logger.error(ce, ce); ingo@32: } ingo@32: ingo@1367: logger.warn("StepForwardService.feed() - FAILED"); ingo@215: throw new ServerException(ERROR_FEED_DATA); ingo@32: } ingo@32: ingo@32: ingo@32: /** ingo@32: * This method creates an array of key/value pairs from an array of Data ingo@32: * objects. The string array is used as parameter for the feed() operation. ingo@32: * ingo@32: * @param data The data that should be transformed into the string array. ingo@32: * ingo@32: * @return a string array that contains key/value pairs. ingo@32: */ ingo@32: protected String[][] createKVP(Data[] data) { ingo@32: String[][] kvp = new String[data.length][]; ingo@32: ingo@32: int i = 0; ingo@32: ingo@32: for (Data d: data) { ingo@32: DataItem[] items = d.getItems(); ingo@32: String key = d.getLabel(); raimund@2535: String value = d.getStringValue(); ingo@32: ingo@32: kvp[i++] = new String[] { key, value }; ingo@32: } ingo@32: ingo@32: return kvp; ingo@32: } ingo@32: } ingo@32: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :