You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@abdera.apache.org by "Rushforth, Peter" <Pe...@NRCan-RNCan.gc.ca> on 2013/02/12 20:47:07 UTC

advice: upgrade or work around?

Hi There,

Thanks for abdera, it is wonderful.  

I am developing in an abdera 0.3.0 project (atomserver), and I have come across an issue that is a bit puzzling.

I want to add an 'annotated' link element (to a collection element inside the feed)  which describes our api, but the mere addition of this element is very slow, so slow that I am wondering if it is a bug somewhere deep inside that version of abdera.

(I tried the obvious, to upgrade to version 1.1.3 of abdera, but there are a lot of red lines in my ide after that, so it's a bit daunting).

I believe it is my Element getCollectionAPI(RequestContext request) implementation, with its heavy use of ExtensibleElement, which performs badly, but I'm not 100% sure.  I know for a fact that my getCollection(RequestContext request) performs badly, based on timings ( 1-10 seconds!).

Can you advise what the best path to take is, please?

Thanks and cheers,
Peter  

The code which I'm using to add the link element goes like this:

    public org.apache.abdera.model.Collection
            getCollection(RequestContext request) {

        Abdera abdera = request.getServiceContext().getAbdera();
        AtomWorkspace atomWorkspace = this.parentAtomWorkspace;
        AtomService atomService = atomWorkspace.getParentAtomService();
        URIHandler uriHandler = atomService.getURIHandler();

        String workspace = atomWorkspace.getName();
        String collName = this.name;

        Factory factory = AtomServer.getFactory(abdera);
        org.apache.abdera.model.Collection e = factory.newCollection();

        TargetType t = atomService.getURIHandler().resolve(request).getType();

        try {
            // set a relative URI as the href; this assumes xml:base is
            // set to the service document
            e.setHref("./");
            e.setBaseUri(uriHandler.constructURIString(workspace, collName)+"/");
            e.setTitle(collName);

            // e.addAccepts("");
            // does not have the desired effect, which is to place an
            // empty accept element in the document, which indicates that a
            // feed is read-only: http://tools.ietf.org/html/rfc5023#section-8.3.4

            // workaround: use a namespaced extension to accomplish that
            e.addExtension(AtomServerConstants.COLLECTION_ACCEPT);

            boolean categoriesExist = atomWorkspace.
                    getAtomCollection(collName).
                    categoriesExist(workspace, collName);

            // admin can setup workspaceBeans.xml to allow
            // stand-alone category documents, accessible via the
            // href attribute.  Whether this is true or not
            // is based on the value of the
            // defaultProducingInLineCategoriesServiceElement value.
            if (categoriesExist) {
                Categories categories = e.addCategories();

                // when the target of the request is the service document,
                // it is up to the administrator as to whether the categories
                // are placed in-line or not.
                if (t == TargetType.TYPE_SERVICE) {
                    if (atomWorkspace.getOptions()
                            .getDefaultProducingInLineCategoriesServiceElement()) {
                        java.util.Collection<Category> categoryList =
                            atomWorkspace.getAtomCollection(collName).listCategories(request);
                        for (Category category : categoryList) {
                            categories.addCategory(category);
                        }
                    }
                } else {
                    // when the target is a collection or the admin has said
                    // that categories are always referenced, put the URI of
                    // the categories doc in the categories@href value.
                    String chref = atomService.getURIHandler()
                            .constructURIString(workspace, collName) + "/$categories/";
                    categories.setHref(chref);
                }
            } else {
                e.addCategories().setFixed(false);
            }

            Element api = getCollectionAPI(request);
            if (api != null) {
                e.addExtension(getCollectionAPI(request));
            }


        } catch (IRISyntaxException ie) {
            throw new BadRequestException(ie);
        }
        return e;
    }
    private Element getCollectionAPI(RequestContext request) {
        Abdera abdera = request.getServiceContext().getAbdera();
        AtomWorkspace atomWorkspace = this.parentAtomWorkspace;
        AtomService atomService = atomWorkspace.getParentAtomService();
        URIHandler uriHandler = atomService.getURIHandler();
        
        String workspace = atomWorkspace.getName();
        String collName = this.name;
        
        Factory factory = AtomServer.getFactory(abdera);

        ExtensibleElement api =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS,"link"));
        api.setAttributeValue("rel","api");
        api.setAttributeValue("type", "{+mediaType}");
        api.setAttributeValue("href",".");
        api.setAttributeValue("tref", "{+categoryQuery}?{+uriQuery}");
        
        ExtensibleElement mediaType =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "mediaType"));

        for (String mt : this.getNegotiableMimeTypes()) {
            ExtensibleElement mvalue =
                factory.newExtensionElement(
                new QName(AtomServerConstants.ATOM_NS, "value"));
            mvalue.setText(mt);
            if (mt.equalsIgnoreCase(this.defaultMediaType.getMediaType()))
                mvalue.setAttributeValue("default", "true");
            mediaType.addExtension(mvalue);
        }
        api.addExtension(mediaType);

        ExtensibleElement categoryQuery =
            factory.newExtensionElement(
            new QName( AtomServerConstants.ATOM_NS, "categoryQuery"));
        categoryQuery.setAttributeValue("tref", "{+catPathSep}/{+categories}");

        ExtensibleElement catpathsep =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "catPathSep"));
        Element cpsvalue = factory.newElement(new QName(AtomServerConstants.ATOM_NS,"value"));
        cpsvalue.setText("-");
        catpathsep.addExtension(cpsvalue);
        categoryQuery.addExtension(catpathsep);
        ExtensibleElement categories =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "categories"));

        Element catpattern =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "pattern"));
        catpattern.setText("(\\([.:a-zA-Z0-9]*\\)[a-zA-Z0-9]+/|[aA][nN][dD]/|[oO][rR]/)*");
        categories.addExtension(catpattern);

        ExtensibleElement link =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS,"link"));
        link.setAttributeValue("rel","suggestions");
        link.setAttributeValue("type","application/vnd.nrcan.suggestions+{subtype}");
        link.setAttributeValue("href", ".");
        link.setAttributeValue("tref", "{?q}");
        link.setBaseUri("./$categories/");
        ExtensibleElement cat_sugg_link_subtype =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "subtype"));
        cat_sugg_link_subtype.addSimpleExtension(new QName(AtomServerConstants.ATOM_NS, "value"), "json");
        cat_sugg_link_subtype.addSimpleExtension(new QName(AtomServerConstants.ATOM_NS, "value"), "xml");
        link.addExtension(cat_sugg_link_subtype);
        ExtensibleElement cat_sugg_link_q =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "q"));
        cat_sugg_link_q.addSimpleExtension(new QName(AtomServerConstants.ATOM_NS, "pattern"), "[ \\+.:\\-a-zA-Z0-9]*");
        link.addExtension(cat_sugg_link_q);
        categories.addExtension(link);
        categoryQuery.addExtension(categories);

        api.addExtension(categoryQuery);

        ExtensibleElement uriQuery =
            factory.newExtensionElement(
            new QName( AtomServerConstants.ATOM_NS, "uriQuery"));
            uriQuery.setAttributeValue("tref", "{+q}{bbox}&updated-min={updatedMin}&updated-max={updatedMax}&max-results={maxResults}&entry-type={entryType}{&callback}&sort-field={sortField}{&alt}");



        ExtensibleElement q =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "q"));
        Element qpattern =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "pattern"));
        qpattern.setText("[ \\+.:\\-a-zA-Z0-9]*");
        q.addExtension(qpattern);

        ExtensibleElement q_sugg_link =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "link"));
        q_sugg_link.setAttributeValue("rel", "suggestions");
        q_sugg_link.setAttributeValue("type", "application/vnd.nrcan.suggestions+{subtype}");
        q_sugg_link.setAttributeValue("href", ".");
        q_sugg_link.setAttributeValue("tref", "{?q}");
        ExtensibleElement subtype =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "subtype"));
        ExtensibleElement valuexml =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "value"));
        valuexml.setText("xml");
        ExtensibleElement valuejson =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "value"));
        valuejson.setText("json");
        subtype.addExtension(valuexml);
        subtype.addExtension(valuejson);
        q_sugg_link.addExtension(subtype);

        ExtensibleElement q_sugg_link_q =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "q"));
        q_sugg_link_q.addExtension((Element)qpattern.clone());
        q_sugg_link.addExtension(q_sugg_link_q);
        q.addExtension(q_sugg_link);

        uriQuery.addExtension(q);

        ExtensibleElement bbox =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "bbox"));
        bbox.setAttributeValue("format","west,south,east,north");
        bbox.setAttributeValue("units","decimal degrees");
        Element bboxpattern =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "pattern"));
        bboxpattern.setText("-[\\d]{1,3}[\\.][\\d]{1,},-[\\d]{1,2}[\\.][\\d]{1,},[\\d]{1,3}[\\.][\\d]{1,},[\\d]{1,2}[\\.][\\d]{1,}");
        bbox.addExtension(bboxpattern);
        ExtensibleElement bbox_sugg_link =
             factory.newExtensionElement(
             new QName(AtomServerConstants.ATOM_NS, "link"));
        //http://geogratis.gc.ca/loc/en/loc?q=false&match=leading
        bbox_sugg_link.setAttributeValue("rel", "suggestions");
        bbox_sugg_link.setAttributeValue("type", "application/vnd.nrcan.suggestions+{subtype}");
        bbox_sugg_link.setAttributeValue("href", ".");
        bbox_sugg_link.setAttributeValue("tref", "?match=leading&{q}");
        bbox_sugg_link.setBaseUri("http://geogratis.gc.ca/loc/en/loc/");
        bbox_sugg_link.addExtension((Element)subtype.clone());
        ExtensibleElement bbox_sugg_link_q =
             factory.newExtensionElement(
             new QName(AtomServerConstants.ATOM_NS, "q"));
        Element bbox_sugg_link_qpattern =
             factory.newElement(
             new QName(AtomServerConstants.ATOM_NS, "pattern"));
        bbox_sugg_link_qpattern.setText("[ \\+.:\\-a-zA-Z0-9]*");
        bbox_sugg_link_q.addExtension(bbox_sugg_link_qpattern);
        bbox_sugg_link.addExtension(bbox_sugg_link_q);
        bbox.addExtension(bbox_sugg_link);

        String date_regex = "[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))(T{0,1}(([0-1][0-9])|([2][0-3])):[0-5][0-9]:([0-5][0-9])(\\.[\\d]{0,2}){0,1})(Z|([\\-+]([0-1][0-9]|2[0-3]))(:[0-5][0-9])?)";

        uriQuery.addExtension(bbox);
        ExtensibleElement updated_min =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "updatedMin"));
        Element updated_minpattern =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "pattern"));
        // the regex is rough and may allow more than it should...
        updated_minpattern.setText(date_regex);
        updated_min.addExtension(updated_minpattern);
        uriQuery.addExtension(updated_min);

        ExtensibleElement updated_max =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "updatedMax"));
        Element updated_maxpattern =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "pattern"));
        updated_maxpattern.setText(date_regex);
        updated_max.addExtension(updated_maxpattern);
        uriQuery.addExtension(updated_max);

        ExtensibleElement max_results =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "maxResults"));
        Element max_resultspattern =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "pattern"));

        int maxfull = atomWorkspace.getOptions().getMaxFullMaxResults();
        int maxlink = atomWorkspace.getOptions().getMaxLinkMaxResults();
        int max = maxlink > maxfull ? maxlink : maxfull;

        // the domain of max-results depends on the value of entry-type, but
        // for the sake of simplicity, we'll put out a regex which covers the
        // maximum possible range
        max_resultspattern.setText(NumericRangeRegexGenerator.rangeRegex(0, max));
        max_results.addExtension(max_resultspattern);
        uriQuery.addExtension(max_results);

        ExtensibleElement entry_type =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "entryType"));

        for (EntryType et : EntryType.values()) {
            ExtensibleElement entry_type_value =
                factory.newExtensionElement(
                new QName(AtomServerConstants.ATOM_NS, "value"));
            entry_type_value.setText(et.name());
            if (et == EntryType.link)
                entry_type_value.setAttributeValue("default", "true");
            entry_type.addExtension(entry_type_value);
        }

        uriQuery.addExtension(entry_type);

        ExtensibleElement callback =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "callback"));
        Element callbackpattern =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "pattern"));
        callbackpattern.setText("[a-zA-Z0-9]{1,64}");
        callback.addExtension(callbackpattern);
        
        uriQuery.addExtension(callback);

        ExtensibleElement locale =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "locale"));

        Element locale_en_CA =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "value"));
        locale_en_CA.setText("en_CA");
        Element locale_fr_CA =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "value"));
        locale_fr_CA.setText("fr_CA");

        // set the default value of locale to correspond with the en/fr
        // servlet mapping convention.  This can be overridden by the client
        // inserting a locale parameter.
        if (uriHandler.getServletMapping().equalsIgnoreCase("fr"))
            locale_fr_CA.setAttributeValue("default", "true");
        else
            locale_en_CA.setAttributeValue("default", "true");

        locale.addExtension(locale_en_CA);
        locale.addExtension(locale_fr_CA);

        uriQuery.addExtension(locale);

        ExtensibleElement sort =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "sortField"));
        Element sort_value =
            factory.newElement(
            new QName(AtomServerConstants.ATOM_NS, "value"));
        sort_value.setText(SortType.spatial.name());
        Element default_sort_value = (Element)sort_value.clone();
        default_sort_value.setText(SortType.edited.name());
        default_sort_value.setAttributeValue("default", "true");
        sort.addExtension(default_sort_value);
        sort.addExtension(sort_value);
        
        uriQuery.addExtension(sort);

        // alt will have a more elaborate contents, in which the media type and
        // the 'file extension' are associated.  The media type is intended as
        // legitimate values of the Accept: protocol header, whereas the file
        // extension is intended to be supplied via the alt parameter to identify
        // a resource which is a view onto a single format among many possible
        // formats negotiable from the un-parameterized resource.  The header
        // takes priority over the URI parameter (? - verify before putting this
        // in api documentation.
        ExtensibleElement alt =
            factory.newExtensionElement(
            new QName(AtomServerConstants.ATOM_NS, "alt"));

        for (String ext:this.getNegotiableFormatExtensions()) {
            ExtensibleElement mvalue =
                factory.newExtensionElement(
                new QName(AtomServerConstants.ATOM_NS, "value"));
            mvalue.setText(ext);
            if (ext.equalsIgnoreCase(this.defaultMediaType.getExtension()))
                mvalue.setAttributeValue("default", "true");
            alt.addExtension(mvalue);
        }
        uriQuery.addExtension(alt);
        api.addExtension(uriQuery);

        return api;
    }