Sesat > Docs + Support > Architecture Overview > Design Proposals > New design proposal for SearchCommand and AbstractSearchCommand > Stream manipulation code examples

Restful

This example defines an interface outside of the SearchCommand class/interface hierarchy.
This approach permits delegation to occur from SearchCommand class to a Restful instance.

/** SearchCommands that are RESTful should implement this behaviour.
 * http://en.wikipedia.org/wiki/Representational_State_Transfer
 */
public interface Restful {

    /** A RESTful service requires a URL.
     * The resource part of the URL is usually constant to the particular command instance,
     *  but each search creates a seperate URL.
     *
     * @return the URL to use to the RESTful service.
     */
    String createRequestURL();
    
    /** Obtain a BufferedReader, in the given encoding, of the RESTful result.
     * Makes the presumption that the RESTful service returns an ascii and not binary response.
     *
     * @param encoding
     * @return
     * @throws java.io.IOException
     */
    BufferedReader getHttpReader(String encoding) throws IOException;
    
}

XmlResultful

An extension to the Restful interface when it is known the response will be in xml format.

/** SearchCommands that are RESTful and return XML response.
 * http://en.wikipedia.org/wiki/Representational_State_Transfer
 *
 * It makes the presumption of working with JAXP's DOM.
 */
public interface XmlRestful extends Restful{

    Document getXmlResult() throws IOException, SAXException;
}

Example abstract XmlRestful implementation

/**
 * Base implementation for search commands that are RESTful and have XML responses.
 * 
 * The RESTful server is defined through:
 * host: AbstractXmlSearchConfiguration.getHost()
 * port: AbstractXmlSearchConfiguration.getPort()
 *
 * @version $Id: AbstractXmlSearchCommand.java 6596 2008-05-10 10:05:48Z ssmiweve $
 */
public abstract class AbstractXmlRestful implements XmlRestful{

    // Attributes ----------------------------------------------------

    private final transient HTTPClient client;

    // Constructors --------------------------------------------------


    /**
     * Create new xml based command.
     */
    public AbstractXmlRestful(final Context cxt) {

        final AbstractXmlSearchConfiguration conf = (AbstractXmlSearchConfiguration)cxt.getSearchConfiguration();

        final SiteConfiguration siteConf = cxt.getDataModel().getSite().getSiteConfiguration();
        final String host = siteConf.getProperty(conf.getHost());
        final int port = Integer.parseInt(siteConf.getProperty(conf.getPort()));

        client = HTTPClient.instance(conf.getHostHeader().length() > 0 ? conf.getHostHeader() : host, port, host);
    }

    // Public --------------------------------------------------------

    public final Document getXmlResult() throws IOException, SAXException {
        final String url = createRequestURL();
        return client.getXmlDocument(url);
    }

    public final BufferedReader getHttpReader(final String encoding) throws IOException {
        final String url = createRequestURL();
        return client.getBufferedReader(url, encoding);
    }
}

Example Usecase

AbstractXmlSearchCommand here is largely refactored (simplified) and now holds an Restful instance and delegates to it as needed

AbstractXmlSearchCommand also introduces the createItem behaviour requirement as it's the norm that each individual result is defined within a separate element within the xml response.

/**
 * Helper base implementation for search commands that are RESTful and have XML responses.
 * 
 * The RESTful server is defined through:
 * host: AbstractXmlSearchConfiguration.getHost()
 * port: AbstractXmlSearchConfiguration.getPort()
 */
public abstract class AbstractXmlSearchCommand extends AbstractSearchCommand{

    // Attributes ----------------------------------------------------

    private XmlRestful restful;

    // Constructors --------------------------------------------------


    /**
     * Create new xml based command.
     *
     * @param cxt The context to execute in.
     */
    protected AbstractXmlSearchCommand(final Context cxt) {
        super(cxt);
    }

    // Public --------------------------------------------------------

    public final String createRequestURL() {
        
        return restful.createRequestURL();
    }
    
    // Protected -----------------------------------------------------


    /** Each individual result is usually defined within one given Element.
     *
     * @param result the w3c element
     * @return the ResultItem
     */
    protected abstract ResultItem createItem(final Element result);

    protected final XmlRestful getXmlRestful(){
        return restful;
    }

    protected final void setXmlRestful(final XmlRestful restful){
        this.restful = restful;
    }
}

PicSearchCommand extends a final implementation on AbstractXmlSearchCommand displaying the delegation pattern at whole.

/**
 * A search command that uses the picsearch API.
 */
public class PicSearchCommand extends AbstractXmlSearchCommand {

    private static final Logger LOG = Logger.getLogger(PicSearchCommand.class);
    private final transient HTTPClient client;
    private final int port;
    private static final String REQ_URL_FMT = "/query?ie=UTF-8&tldb={0}&filter={1}&custid={2}&version=2.6"
            + "&thumbs={3}&q={4}&start={5}&site={6}&color={7}&size={8}";

    /**
     * Creates a new command in given context.
     *
     * @param cxt Context to run in.
     */
    public PicSearchCommand(final Context cxt) {

        super(cxt);
        
        setXmlRestful(
                new AbstractXmlRestful(cxt) {
                    public String createRequestURL() {


                        final PictureCommandConfig cfg = (PictureCommandConfig) cxt.getSearchConfiguration();

                        try {

                            final String query = URLEncoder.encode(PicSearchCommand.this.getTransformedQuery(), "utf-8");
                            
                            final String color = null != PicSearchCommand.this.getParameter("color") 
                                    ? PicSearchCommand.this.getParameter("color") 
                                    : "";
                            
                            final String size = null != PicSearchCommand.this.getParameter("size") 
                                    ? PicSearchCommand.this.getParameter("size") 
                                    : "";
                            
                            final String urlBoost = PicSearchCommand.this.getFilterBuilder().getFilter("tldb")
                                    .replace('=', ':')
                                    .replace(' ', ',');

                            if(null != cfg.getSite() && cfg.getSite().length() > 0){
                                PicSearchCommand.this.getFilterBuilder().addFilter("site", cfg.getSite());
                            }

                            // The boost can eiter be from the URL or from the configuration.
                            final String boost = URLEncoder.encode(urlBoost.length() > 0 ? urlBoost : cfg.getDomainBoost(), "utf-8");

                            return MessageFormat.format(REQ_URL_FMT,
                                    boost,
                                    cfg.getFilter(),
                                    cfg.getCustomerId(),
                                    cfg.getResultsToReturn(),
                                    query,
                                    PicSearchCommand.this.getOffset()+1,
                                    PicSearchCommand.this.getFilterBuilder().getFilter("site").replace(' ', ','),
                                    color,
                                    size);

                        } catch (UnsupportedEncodingException e) {
                           throw new SearchCommandException(e);
                        }
                            
                    }
        });

        final SiteConfiguration siteConfig = datamodel.getSite().getSiteConfiguration();
        final PictureCommandConfig psConfig = (PictureCommandConfig) context.getSearchConfiguration();

        final String host = siteConfig.getProperty(psConfig.getQueryServerHost());
        port = Integer.parseInt(siteConfig.getProperty(psConfig.getQueryServerPort()));
        client = HTTPClient.instance(host, port);

    }

    public ResultList<ResultItem> execute() {

            final BasicResultList<ResultItem> searchResult = new BasicResultList<ResultItem>();

            if (port > 0){
                try {

                    final Document doc = getXmlRestful().getXmlResult();
                    if (doc != null) {
                        final Element resultElement = doc.getDocumentElement();
                        searchResult.setHitCount(Integer.parseInt(resultElement.getAttribute("hits")));
                        final NodeList list = resultElement.getElementsByTagName("image");
                        for (int i = 0; i < list.getLength(); i++) {
                            searchResult.addResult(createItem((Element) list.item(i)));
                        }
                    }

                } catch (IOException ex) {
                    throw new SearchCommandException(ex);
                } catch (SAXException ex) {
                    throw new SearchCommandException(ex);
                }
            }

            return searchResult;

    }

    @Override
    protected ResultItem createItem(final Element picture) {

        final BasicResultItem item = new BasicResultItem();
        for (final Map.Entry<String, String> entry : getSearchConfiguration().getResultFieldMap().entrySet()) {
            item.addField(entry.getValue(), picture.getAttribute(entry.getKey()));
        }
        return item;
        
    }
}
 © 2007-2009 Schibsted ASA
Contact us