Hi again, I apologize, I failed to mention that I thought the issue might be related to this https://issues.apache.org/jira/browse/ABDERA-267, but I patched up a local version of 0.3.0 and it did not solve the problem.
I tried a few other things, including the NetBeans profiler, which I had not used before, and it turned out to be a badly designed database query embedded in categoriesExist(workspace, collName). So case closed. I learned a few things about abdera along the way, and will hopefully upgrade my project sometime to the latest version, where I will be better positioned to contribute patches and relevant feedback. Regards, Peter ________________________________________ From: Rushforth, Peter Sent: February 12, 2013 2:47 PM To: [email protected] Subject: 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; }
