From: <jbo...@li...> - 2006-05-17 17:52:06
|
Author: adamw Date: 2006-05-17 13:51:54 -0400 (Wed, 17 May 2006) New Revision: 4289 Added: labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/Constants.java labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/ labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/AggregatedFeedTagHandler.java labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/FeedTagHandler.java labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/FeedTagHandlerManager.java labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/RemoteFeedTagHandler.java labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/ShotokuFeedTagHandler.java labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/variables/VariableResolverManager.java Modified: labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/FeedsDescriptor.java labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/variables/XmlNodeTypeVariableResolver.java Log: http://jira.jboss.com/jira/browse/JBSHOTOKU-36 Added: labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/Constants.java =================================================================== --- labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/Constants.java 2006-05-17 17:09:34 UTC (rev 4288) +++ labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/Constants.java 2006-05-17 17:51:54 UTC (rev 4289) @@ -0,0 +1,56 @@ +package org.jboss.shotoku.feeds; + +/** + * @author Adam Warski (ad...@as...) + */ +public class Constants { + /** + * Node with names ending with this string are parsed and + * feed definitons are read from them. + */ + public final static String NODE_ENDING = "-feeds.xml"; + + /* + * Tag names. + */ + public final static String REM_FEED_TAG = "remote-feed"; + public final static String AGG_FEED_TAG = "aggregated-feed"; + public final static String SHO_FEED_TAG = "shotoku-feed"; + + /* + * Property name, under which content manager id and name of the + * directory which contains the configuration file can be found. + */ + public final static String CONFIGDIR = "shotoku.internal.feeds.configdir"; + public final static String CONFIGID = "shotoku.internal.feeds.configcmid"; + + /* + * Property name suffixes, under which content manager id and name + * of the directory which contains the feed files can be found. + */ + public final static String FEEDDIR = ".xml.directory"; + public final static String FEEDID = ".xml.cmid"; + + /** + * Property name suffix, under which information about available + * variables can be found. + */ + public final static String ALL_VARS = ".variables"; + /** + * Property name middle, under which informatin about a concerete + * variable can be found (should be prepended with an id and followed + * by a property name). + */ + public final static String ONE_VAR = ".variable."; + + /** + * Variable, for which feed type will be substituted. + */ + public final static String TYPE_VARIABLE = "${type}"; + + /* + * Xml node attributes denoting a feed name and display name. + */ + public static final String NAME_ATTR = "name"; + public static final String DISP_NAME_ATTR = "display-name"; +} Modified: labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/FeedsDescriptor.java =================================================================== --- labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/FeedsDescriptor.java 2006-05-17 17:09:34 UTC (rev 4288) +++ labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/FeedsDescriptor.java 2006-05-17 17:51:54 UTC (rev 4289) @@ -54,6 +54,10 @@ import org.jboss.shotoku.feeds.variables.VariableResolver; import org.jboss.shotoku.feeds.variables.NullVariableResolver; import org.jboss.shotoku.feeds.variables.Substitution; +import org.jboss.shotoku.feeds.variables.VariableResolverManager; +import org.jboss.shotoku.feeds.tag.FeedDefinition; +import org.jboss.shotoku.feeds.tag.FeedTagHandler; +import org.jboss.shotoku.feeds.tag.FeedTagHandlerManager; import org.jboss.shotoku.tools.Pair; import org.jboss.shotoku.tools.Tools; import org.jboss.shotoku.aop.CacheItem; @@ -92,62 +96,9 @@ search.parametrized = directory,history,limit,property-sort,property-value */ - private class FeedDefinition { - FeedDefinition(String group, Node definition) { - this.group = group; - this.definition = definition; - } - - String group; - Node definition; - } - private static final Logger log = Logger.getLogger(FeedsDescriptor.class); - /** - * Node with names ending with this string are parsed and - * feed definitons are read from them. - */ - private final static String NODE_ENDING = "-feeds.xml"; - /* - * Tag names. - */ - private final static String REM_FEED_TAG = "remote-feed"; - private final static String AGG_FEED_TAG = "aggregated-feed"; - private final static String SHO_FEED_TAG = "shotoku-feed"; - - /* - * Property name, under which content manager id and name of the - * directory which contains the configuration file can be found. - */ - private final static String CONFIGDIR = "shotoku.internal.feeds.configdir"; - private final static String CONFIGID = "shotoku.internal.feeds.configcmid"; - - /* - * Property name suffixes, under which content manager id and name - * of the directory which contains the feed files can be found. - */ - private final static String FEEDDIR = ".xml.directory"; - private final static String FEEDID = ".xml.cmid"; - - /** - * Property name suffix, under which information about available - * variables can be found. - */ - private final static String ALL_VARS = ".variables"; - /** - * Property name middle, under which informatin about a concerete - * variable can be found (should be prepended with an id and followed - * by a property name). - */ - private final static String ONE_VAR = ".variable."; - - /** - * Variable, for which feed type will be substituted. - */ - public final static String TYPE_VARIABLE = "${type}"; - @CacheItem private static FeedsCache feedsCache; @@ -169,60 +120,42 @@ */ private Map<String, Set<String>> feedGroups; - /** - * Map variable name -> variable resolver class (read from properties). - */ - private Map<String, Class> varResolvers; + private VariableResolverManager vrm; + private Map<String, FeedTagHandler> feedTagHandlers; + private String baseServerAddress; public FeedsDescriptor(String id, String baseServerAddress) { this.baseServerAddress = baseServerAddress; - Properties props = conf.get(new Pair<String, String>( - ContentManager.getProperty(CONFIGID, ""), - ContentManager.getProperty(CONFIGDIR, "") + + // From the global Shotoku configuration, reading the config file + // location in Shotoku repositories. + Properties props = conf.get(new Pair<String, String>( + ContentManager.getProperty(Constants.CONFIGID, ""), + ContentManager.getProperty(Constants.CONFIGDIR, "") )); + // From properties read from Shotoku, getting the content manager + // configuration for this feeds id. ContentManager cm = ContentManager.getContentManager( - props.getProperty(id + FEEDID), - props.getProperty(id + FEEDDIR) + props.getProperty(id + Constants.FEEDID), + props.getProperty(id + Constants.FEEDDIR) ); - // Creating maps of available variable resolver classes - varResolvers = new HashMap<String, Class>(); - for (String variable : props.getProperty(id + ALL_VARS, "").split("[,]")) { - try { - varResolvers.put(variable, - Class.forName(props.getProperty(id + ONE_VAR + variable))); - } catch (ClassNotFoundException e) { - log.error("Variable resolver class " + props.getProperty(id + - ONE_VAR + variable) + " for variable " + variable + - " not found", e); - } - } + vrm = new VariableResolverManager(id, props); + feedTagHandlers = new HashMap<String, FeedTagHandler>(); // Creating the maps that will hold the feeds. feeds = new HashMap<String, Map<String, Feed>>(); feedGroups = new LinkedHashMap<String, Set<String>>(); - // Preparing maps which will hold feed definitions. - Map<String, FeedDefinition> remoteFeedDefs = new HashMap<String, FeedDefinition>(); - Map<String, FeedDefinition> aggregatedFeedDefs = new HashMap<String, FeedDefinition>(); - Map<String, FeedDefinition> shotokuFeedDefs = new HashMap<String, FeedDefinition>(); - - Map<String, Map<String, FeedDefinition>> allFeedDefs = - new HashMap<String, Map<String, FeedDefinition>>(); - - allFeedDefs.put(REM_FEED_TAG, remoteFeedDefs); - allFeedDefs.put(AGG_FEED_TAG, aggregatedFeedDefs); - allFeedDefs.put(SHO_FEED_TAG, shotokuFeedDefs); - // Looking for file containing feed definitons and filling the maps. for (org.jboss.shotoku.Node feedNode : cm.getRootDirectory().getNodes().toList()) { - if (feedNode.getName().endsWith(NODE_ENDING)) { + if (feedNode.getName().endsWith(Constants.NODE_ENDING)) { try { - addFeedDefinitions(allFeedDefs, feedNode); + addFeedDefinitions(feedNode); } catch (Exception e) { // We just don't add the feeds ... and log a warning. log.warn("Can't add feed definitions from node " + @@ -231,74 +164,44 @@ } } - // Firstly - the remote feeds. - generateRemoteFeeds(remoteFeedDefs); - - // Later, shotoku feeds. - generateShotokuFeeds(shotokuFeedDefs, props); - - // After that, we can create the aggregated feeds. - generateAggregatedFeeds(aggregatedFeedDefs); - } - - /** - * A pattern which matcher variables of the form "${text with numbers}". - */ - private final static Pattern variablesPattern = Pattern.compile( - Pattern.quote("${") + "[a-zA-Z0-9]*" + Pattern.quote("}")); - - /** - * From the given node's attributes, reads all variables (that is, strings - * which match <code>vairablesPattern</code> and adds them to the given - * set. - * @param toFill Set to which found variables should be added. - * @param n Node from which to read the attributes. - */ - private void addVariablesFromNode(Set<String> toFill, Node n) { - NamedNodeMap nnm = n.getAttributes(); - - for (int i=0; i<nnm.getLength(); i++) { - String text = Tools.unmarshallText(nnm.item(i)); - Matcher m = variablesPattern.matcher(text); - - while (m.find()) { - toFill.add(text.substring(m.start(), m.end()+1)); - } + // Generating the feeds. + while (feedTagHandlers.size() > 0) { + // Trying to generate any feed found in the feed tag handler map. + generateFeed(feedTagHandlers.keySet().iterator().next(), + new HashSet<String>()); } } - private VariableResolver instantiateVariableResolver(String variable, Node n) { - Class varResolverClass = varResolvers.get(variable); - if (varResolverClass == null) { - log.error("Unsupported variable found: " + variable + "."); - return new NullVariableResolver(); - } + private void addFeedDefinitions(org.jboss.shotoku.Node node) throws SAXException, IOException { + DOMParser parser = new DOMParser(); + // Parse the descriptor at the given Shotoku node. + parser.parse(new InputSource(node.getContentInputStream())); + // Get the XML document element of the feed descriptor file. + Node root = parser.getDocument().getDocumentElement(); + String group = Tools.getAttributeValue(root, "display-name"); - try { - Constructor c = varResolverClass.getConstructor(Node.class); - try { - return (VariableResolver) c.newInstance(n); - } catch (Exception e) { - log.error("Error instatiating variable resolver " + varResolverClass + ".", e); - } - } catch (NoSuchMethodException e) { - try { - return (VariableResolver) varResolverClass.getConstructor().newInstance(); - } catch (Exception e2) { - log.error("Error instatiating variable resolver " + varResolverClass + ".", e2); - } - } + Set<String> feedGroup = new LinkedHashSet<String>(); + // Each feed descriptor file has one feed group. + // Get the name of the feed group for this file. + feedGroups.put(group, feedGroup); - return new NullVariableResolver(); - } + NodeList nodes = root.getChildNodes(); + // Iterate through the rest of the feed descriptor + // file to load all feeds for a given feed group. + for (int i = 0; i < nodes.getLength(); i++) { + Node n = nodes.item(i); + feedTagHandlers.putAll(FeedTagHandlerManager.getHandlers(group, n, vrm)); + } + } + /** * Adds a feed of the given name and type. * @param name * @param type * @param feed */ - private void addFeed(String name, String type, Feed feed) { + public void addFeed(String name, String type, Feed feed) { Map<String, Feed> typesMap = feeds.get(name); if (typesMap == null) { typesMap = new HashMap<String, Feed>(); @@ -308,7 +211,35 @@ typesMap.put(type, feed); } - /** + /** + * Tries to generate a feed with the given name. If the feed is absent, + * or is being already generated (this case indicates a loop in feed + * definitions), no actions are taken. + * @param name Feed to be generated. + * @param duringGeneration Set of feed names that are being generated + * at the moment (not yet added). + * @return True iff generation was successfull. + */ + public boolean generateFeed(String name, Set<String> duringGeneration) { + if (duringGeneration.contains(name)) { + return false; + } + + FeedTagHandler fth = feedTagHandlers.get(name); + if (fth != null) { + feedTagHandlers.remove(name); + + duringGeneration.add(name); + boolean ret = fth.generate(this, duringGeneration); + duringGeneration.remove(name); + + return ret; + } + + return false; + } + + /** * In the given <code>map</code>, in its values, replaces each occurence of * <code>replaceWhat</code> with <code>replaceTo</code>. * @param map @@ -326,326 +257,6 @@ } /** - * Recursively generates a search parameter basing on its description that is - * contained in the given node. - * @param n Node from which to read the search parameters. - * @param props Properties with directives classes. - * @param parametrized Set of directive names that accept a parameter map - * in their constructor. - * @return A search parameter that is described in the given node. - * @throws IllegalArgumentException - * @throws SecurityException - * @throws InstantiationException - * @throws IllegalAccessException - * @throws InvocationTargetException - * @throws NoSuchMethodException - * @throws ClassNotFoundException - */ - private SearchParameter generateSearchParameter(Node n, Properties props, - Set<String> parametrized, String siteId) - throws IllegalArgumentException, SecurityException, - InstantiationException, IllegalAccessException, - InvocationTargetException, NoSuchMethodException, - ClassNotFoundException { - String paramName = n.getNodeName(); - - // Looking for a corresponding class. - String paramClass = props.getProperty(paramName); - - if (paramClass == null) - throw new RuntimeException("Unknown parameter name: " + paramName - + "."); - - SearchParameter param; - if (parametrized.contains(paramName)) { - // Instatiating the class, it should have a constructor accepting a - // map. We take the map from node attributes, and replace - // ${site} and ${site-name} in them. - param = (SearchParameter) Class.forName(paramClass).getConstructor( - new Class[] { Map.class }).newInstance( - new Object[] { replaceInMapValues(replaceInMapValues( - XmlTools.getMapFromNodeAttributes(n), - SITE_PARAM, siteId), SITE_NAME_PARAM, - getSiteName(siteId)) }); - } else { - param = (SearchParameter) Class.forName(paramClass).newInstance(); - } - - NodeList nodeList = n.getChildNodes(); - for (int i = 0; i < nodeList.getLength(); i++) { - Node child = nodeList.item(i); - - if (child.getNodeType() == Node.ELEMENT_NODE) { - // If the search paramater has child nodes, then the - // corresponding class should have an "add" method. - // Invoking the function recursively and adding the - // resulting parameter. - SearchParameter childParam = generateSearchParameter(child, - props, parametrized, siteId); - - Method[] methods = param.getClass().getMethods(); - for (Method method : methods) { - if ("add".equals(method.getName())) { - method.invoke(param, new Object[] { childParam }); - break; - } - } - } - } - - return param; - } - - private final static String RFC_822_DATE = "EEE, d MMM yyyy HH:mm:ss Z"; - - private void generateShotokuFeeds(Map<String, Node> feedDefs, - Properties props) { - Set<String> parametrized = new HashSet<String>(Arrays.asList(props.getProperty( - "parametrized").split("[,]"))); - - for (String feedName : feedDefs.keySet()) { - Node feedNode = feedDefs.get(feedName); - String templateAttr = XmlTools.getAttributeValue(feedNode, - "template"); - String prefixAttr = XmlTools.getAttributeValue(feedNode, "prefix"); - String idAttr = XmlTools.getAttributeValue(feedNode, "id"); - String typesAttr = XmlTools.getAttributeValue(feedNode, "type"); - String displayNameAttr = XmlTools.getAttributeValue(feedNode, - "display-name"); - - ContentManager cm = ContentManager.getContentManager(idAttr, - prefixAttr); - - // Generating the type of feeds that have been requested. - String[] types = getTypes(typesAttr); - - // Generating the sites for which the feeds have been requested, - // if any. - String[] siteIds = getSiteIds(); - - Map<String, String> attributes = XmlTools - .getMapFromNodeAttributes(feedNode); - - // Creating a velcoity context with parts that won't change in all - // generated feeds. - VelocityContext vc = new VelocityContext(attributes); - - String dateFormat = attributes.get("dateFormat"); - vc.put("dateFormat", new SimpleDateFormat( - dateFormat == null ? "MM/dd/yy" : dateFormat)); - vc.put("rssDateFormat", new SimpleDateFormat(RFC_822_DATE)); - vc.put("rdfDateFormat", new RdfDateFormat()); - vc.put("now", Calendar.getInstance().getTime()); - vc.put("baseServerAddress", baseServerAddress); - vc.put("escape", new TextEscaping()); - - for (String site : siteIds) { - String feedNameReplaced = feedName.replace(SITE_PARAM, - site); - - NodeList feedNodeChildren = feedNode.getChildNodes(); - for (int i = 0; i < feedNodeChildren.getLength(); i++) { - // For each search node, generating and performing the - // search, placing it under the demanded attribute in - // velocity's context. - Node searchNode = feedNodeChildren.item(i); - if ("search".equals(searchNode.getNodeName())) { - try { - Search search = (Search) generateSearchParameter( - searchNode, props, parametrized, site); - - org.jboss.shotoku.NodeList list = cm.search(search); - - String searchNameAttr = XmlTools.getAttributeValue( - searchNode, "name"); - if (searchNameAttr == null) - searchNameAttr = "nodes"; - - vc.put(searchNameAttr, list); - } catch (Exception e) { - log.warn("Error generating shotoku feed " - + feedName + ".", e); - continue; - } - } - } - - for (String type : types) { - String template = templateAttr.replace(TYPE_PARAM, type); - - Feed newFeed = FeedFactory.getNewFeed(type); - try { - vc.put("link", generateFeedLink(true, feedNameReplaced, - type)); - - // Now that we have type and site, we can render the - // template. - Writer w = newFeed.getWriter(); - cm.getVelocityEngine().mergeTemplate(template, vc, w); - w.flush(); - w.close(); - - // And add the feed. - addFeed(feedNameReplaced, type, newFeed); - } catch (Exception e) { - log.warn("Cannot merge template " + template + " for " - + feedName + ".", e); - continue; - } - } - } - } - } - - private void generateRemoteFeeds(Map<String, FeedDefinition> feedDefs) { - for (String feedName : feedDefs.keySet()) { - FeedDefinition feedDefinition = feedDefs.get(feedName); - Node feedNode = feedDefinition.definition; - - String addressAttr = XmlTools.getAttributeValue(feedNode, "address"); - String displayNameAttr = XmlTools.getAttributeValue(feedNode, "display-name"); - - Set<String> variables = new HashSet<String>(); - // We always have to substitute the type variable. - variables.add(TYPE_VARIABLE); - addVariablesFromNode(variables, feedNode); - - Set<Substitution> substitutions = Substitution.newEmptySubstitionsSet(); - - for (String variable : new HashSet<String>(variables)) { - instantiateVariableResolver(variable, feedNode). - expandSubstitutionsSet(variables, substitutions); - } - - if (variables.size() > 0) { - log.warn("Unresolved variables left: " + variables.toString() + " in feed " + - feedName + "."); - continue; - } - - for (Substitution s : substitutions) { - String feedNameSub = s.make(feedName); - String feedType = s.getSubstitutionFor(TYPE_VARIABLE); - try { - Feed feed = FeedFactory.getNewFeed(feedNameSub, - s.make(displayNameAttr), feedType); - feed.generate(new URL(s.make(addressAttr))); - addFeed(feedNameSub, feedType, feed); - } catch (Exception e) { - log.debug("Can't add feed " + s.make(addressAttr) + " : " + feedType - + ".", e); - } - } - } - } - - private void generateAggregatedFeeds(Map<String, Node> feedDefs) { - // First building requirements of each feed - that is, whit which - // feeds each aggregated feed is built. - Map<String, Set<String>> requirements = - new HashMap<String, Set<String>>(); - Map<String, String[]> parameters = new HashMap<String, String[]>(); - - // Gathering information about defined feeds, their requirements - // (that is, from which other feeds they are composed) and their - // parameters. - for (String feedName : feedDefs.keySet()) { - Node n; - Node feedNode = feedDefs.get(feedName); - NodeList nodes = feedNode.getChildNodes(); - - Set<String> feedRequirements = new HashSet<String>(); - requirements.put(feedName, feedRequirements); - parameters.put(feedName, new String[] { - XmlTools.getAttributeValue(feedNode, "title"), - XmlTools.getAttributeValue(feedNode, "description"), - XmlTools.getAttributeValue(feedNode, "author") }); - feedDisplayNames.put(feedName, XmlTools.getAttributeValue(feedNode, - "display-name")); - - for (int i = 0; i < nodes.getLength(); i++) { - n = nodes.item(i); - if ("include".equals(n.getNodeName())) { - String nameAttr = XmlTools.getAttributeValue(n, "name"); - - String[] siteIds = getSiteIds(); - - for (String siteId : siteIds) { - feedRequirements.add(nameAttr.replace(SITE_PARAM, - siteId)); - } - } - } - } - - while (requirements.size() > 0) { - int sizeBefore = requirements.size(); - - for (Iterator<String> iter = requirements.keySet().iterator(); iter - .hasNext();) { - String feedName = iter.next(); - Set<String> feedRequirements = requirements.get(feedName); - String[] feedParameters = parameters.get(feedName); - Set<String> types = null; - - // Checking if the all of this feed's requirements are met - // and if so, of what types it can consist. - boolean allRequirementsMet = true; - for (String requirement : feedRequirements) { - Map<String, Feed> feedTypes = feeds.get(requirement); - - if (feedTypes == null) { - // Missing feed - cannot create the aggregated one. - allRequirementsMet = false; - break; - } - - // Intersecting the types set with the types of the current - // feed. - if (types == null) { - types = new HashSet<String>(); - types.addAll(feedTypes.keySet()); - } else - types.retainAll(feedTypes.keySet()); - } - - if (!allRequirementsMet) - continue; - - // Gathering the feed parts from which the new feed will be - // composed. - for (String type : types) { - List<Feed> requiredFeeds = new ArrayList<Feed>(); - for (String requirement : feedRequirements) { - requiredFeeds.add(feeds.get(requirement).get(type)); - } - - Feed newFeed = FeedFactory.getNewFeed(type); - try { - newFeed.generate(requiredFeeds.toArray(new Feed[0]), - feedParameters[0], feedParameters[1], - feedParameters[2], generateFeedLink(true, - feedName, type)); - } catch (Exception e) { - log.warn("Can't add aggregated feed " + feedName - + " : " + type + ".", e); - } - - addFeed(feedName, type, newFeed); - } - - iter.remove(); - } - - if (sizeBefore == requirements.size()) { - log.warn("Aggregation feed definitions cycle, or demanded " - + "feeds are not defined; unable to create feeds."); - return; - } - } - } - - /** * Generates a link to a specified feed. If the link is to be full, an * absolute address is returned. Otherwise, a relative one. * @param full @@ -660,6 +271,27 @@ } /** + * Gets a feed of the given type and name. + * @param name Name of the feed to get. + * @param type Type of the feed to get. + * @return Feed with the given name and type. + */ + public Feed getFeed(String name, String type) { + Map<String, Feed> feedTypes = feeds.get(name); + if (feedTypes == null) + return null; + + return feedTypes.get(type); + } + + /** + * Releases all feeds - deletes temporary files which contain them. + */ + public void release() { + + } + + /** * Fills the <code>context</code> variable with feed information that is * to be shown in the portlet. */ @@ -701,61 +333,4 @@ } } - /** - * Adds definitions of feeds to the given map, placing child nodes of - * the given node in the appropriate map. - * @param feedDefinitions A map into which feeds will be added. - * @param node The node of the feeds descriptor file. - * @throws SAXException - * @throws IOException - */ - private void addFeedDefinitions(Map<String, Map<String, FeedDefinition>> feedDefinitions, - org.jboss.shotoku.Node node) throws SAXException, IOException { - DOMParser parser = new DOMParser(); - // Parse the descriptor at the given Shotoku node. - parser.parse(new InputSource(node.getContentInputStream())); - // Get the XML document element of the feed descriptor file. - Node root = parser.getDocument().getDocumentElement(); - String groupName = XmlTools.getAttributeValue(root, "display-name"); - - Set<String> feedGroup = new LinkedHashSet<String>(); - // Each feed descriptor file has one feed group. - // Get the name of the feed group for this file. - feedGroups.put(groupName, feedGroup); - - NodeList nodes = root.getChildNodes(); - - // Iterate through the rest of the feed descriptor - // file to load all feeds for a given feed group. - for (int i = 0; i < nodes.getLength(); i++) { - Node n = nodes.item(i); - if (feedDefinitions.get(n.getNodeName()) != null) { - // Get the name of the feed. - String feedName = XmlTools.getAttributeValue(n, "name"); - feedDefinitions.get(n.getNodeName()).put(feedName, - new FeedDefinition(groupName, n)); - } - } - } - - /** - * Gets a feed of the given type and name. - * @param name - * @param type - * @return - */ - public Feed getFeed(String name, String type) { - Map<String, Feed> feedTypes = feeds.get(name); - if (feedTypes == null) - return null; - - return feedTypes.get(type); - } - - /** - * Releases all feeds - deletes temporary files which contain them. - */ - public void release() { - - } } Added: labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/AggregatedFeedTagHandler.java =================================================================== --- labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/AggregatedFeedTagHandler.java 2006-05-17 17:09:34 UTC (rev 4288) +++ labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/AggregatedFeedTagHandler.java 2006-05-17 17:51:54 UTC (rev 4289) @@ -0,0 +1,123 @@ +package org.jboss.shotoku.feeds.tag; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.jboss.shotoku.feeds.data.Feed; +import org.jboss.shotoku.feeds.FeedFactory; + +import java.util.*; + +/** + * @author Adam Warski (ad...@as...) + */ +public class AggregatedFeedTagHandler extends FeedTagHandler { + public AggregatedFeedTagHandler(String group, Node definition) { + super(group, definition); + } + + private void generateAggregatedFeeds(Map<String, Node> feedDefs) { + // First building requirements of each feed - that is, whit which + // feeds each aggregated feed is built. + Map<String, Set<String>> requirements = + new HashMap<String, Set<String>>(); + Map<String, String[]> parameters = new HashMap<String, String[]>(); + + // Gathering information about defined feeds, their requirements + // (that is, from which other feeds they are composed) and their + // parameters. + for (String feedName : feedDefs.keySet()) { + Node n; + Node feedNode = feedDefs.get(feedName); + NodeList nodes = feedNode.getChildNodes(); + + Set<String> feedRequirements = new HashSet<String>(); + requirements.put(feedName, feedRequirements); + parameters.put(feedName, new String[] { + XmlTools.getAttributeValue(feedNode, "title"), + XmlTools.getAttributeValue(feedNode, "description"), + XmlTools.getAttributeValue(feedNode, "author") }); + feedDisplayNames.put(feedName, XmlTools.getAttributeValue(feedNode, + "display-name")); + + for (int i = 0; i < nodes.getLength(); i++) { + n = nodes.item(i); + if ("include".equals(n.getNodeName())) { + String nameAttr = XmlTools.getAttributeValue(n, "name"); + + String[] siteIds = getSiteIds(); + + for (String siteId : siteIds) { + feedRequirements.add(nameAttr.replace(SITE_PARAM, + siteId)); + } + } + } + } + + while (requirements.size() > 0) { + int sizeBefore = requirements.size(); + + for (Iterator<String> iter = requirements.keySet().iterator(); iter + .hasNext();) { + String feedName = iter.next(); + Set<String> feedRequirements = requirements.get(feedName); + String[] feedParameters = parameters.get(feedName); + Set<String> types = null; + + // Checking if the all of this feed's requirements are met + // and if so, of what types it can consist. + boolean allRequirementsMet = true; + for (String requirement : feedRequirements) { + Map<String, Feed> feedTypes = feeds.get(requirement); + + if (feedTypes == null) { + // Missing feed - cannot create the aggregated one. + allRequirementsMet = false; + break; + } + + // Intersecting the types set with the types of the current + // feed. + if (types == null) { + types = new HashSet<String>(); + types.addAll(feedTypes.keySet()); + } else + types.retainAll(feedTypes.keySet()); + } + + if (!allRequirementsMet) + continue; + + // Gathering the feed parts from which the new feed will be + // composed. + for (String type : types) { + List<Feed> requiredFeeds = new ArrayList<Feed>(); + for (String requirement : feedRequirements) { + requiredFeeds.add(feeds.get(requirement).get(type)); + } + + Feed newFeed = FeedFactory.getNewFeed(type); + try { + newFeed.generate(requiredFeeds.toArray(new Feed[0]), + feedParameters[0], feedParameters[1], + feedParameters[2], generateFeedLink(true, + feedName, type)); + } catch (Exception e) { + log.warn("Can't add aggregated feed " + feedName + + " : " + type + ".", e); + } + + addFeed(feedName, type, newFeed); + } + + iter.remove(); + } + + if (sizeBefore == requirements.size()) { + log.warn("Aggregation feed definitions cycle, or demanded " + + "feeds are not defined; unable to create feeds."); + return; + } + } + } +} Added: labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/FeedTagHandler.java =================================================================== --- labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/FeedTagHandler.java 2006-05-17 17:09:34 UTC (rev 4288) +++ labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/FeedTagHandler.java 2006-05-17 17:51:54 UTC (rev 4289) @@ -0,0 +1,54 @@ +package org.jboss.shotoku.feeds.tag; + +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.jboss.shotoku.tools.Tools; +import org.jboss.shotoku.feeds.variables.Substitution; +import org.jboss.shotoku.feeds.data.Feed; +import org.jboss.shotoku.feeds.FeedFactory; +import org.jboss.shotoku.feeds.FeedsDescriptor; + +import java.util.Map; +import java.util.Set; +import java.util.HashSet; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.net.URL; + +/** + * @author Adam Warski (ad...@as...) + */ +public abstract class FeedTagHandler { + private String group; + private Node definition; + private Substitution s; + + public FeedTagHandler(String group, Node definition, Substitution s) { + this.group = group; + this.definition = definition; + this.s = s; + } + + public String getGroup() { + return group; + } + + public Node getDefinition() { + return definition; + } + + public Substitution getSubstitution() { + return s; + } + + /** + * Generates a feed handled by this instance. The generated feed should + * be added to the FeedsDescriptor by calling the FeedsDescriptor.addFeed() + * method. + * @param fd + * @param duringGeneration A set of feed names that are being generated + * at the moment. + * @return True iff the generation was successfull and the feed was added. + */ + public abstract boolean generate(FeedsDescriptor fd, Set<String> duringGeneration); +} Added: labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/FeedTagHandlerManager.java =================================================================== --- labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/FeedTagHandlerManager.java 2006-05-17 17:09:34 UTC (rev 4288) +++ labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/FeedTagHandlerManager.java 2006-05-17 17:51:54 UTC (rev 4289) @@ -0,0 +1,127 @@ +package org.jboss.shotoku.feeds.tag; + +import org.jboss.shotoku.tools.Tools; +import org.jboss.shotoku.feeds.Constants; +import org.jboss.shotoku.feeds.FeedsDescriptor; +import org.jboss.shotoku.feeds.variables.VariableResolverManager; +import org.jboss.shotoku.feeds.variables.Substitution; +import org.jboss.logging.Logger; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Map; +import java.util.Set; +import java.util.HashSet; +import java.util.HashMap; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +/** + * @author Adam Warski (ad...@as...) + */ +public class FeedTagHandlerManager { + private static final Logger log = Logger.getLogger(FeedTagHandlerManager.class); + + /** + * Tag name -> tag handler constructor. + */ + private static Map<String, Constructor> handlers; + + private static void addHandler(String tagName, Class handlerClass) { + try { + handlers.put(tagName, handlerClass.getConstructor(String.class, Node.class, + Substitution.class)); + } catch (NoSuchMethodException e) { + log.error("No (String, Node) constructor found in tag " + tagName + " handler " + + handlerClass + "."); + } + } + + static { + addHandler(Constants.AGG_FEED_TAG, AggregatedFeedTagHandler.class); + addHandler(Constants.REM_FEED_TAG, RemoteFeedTagHandler.class); + addHandler(Constants.SHO_FEED_TAG, ShotokuFeedTagHandler.class); + } + + /** + * A pattern which matcher variables of the form "${text with numbers}". + */ + private final static Pattern variablesPattern = Pattern.compile( + Pattern.quote("${") + "[a-zA-Z0-9]*" + Pattern.quote("}")); + + /** + * From the given node's attributes, reads all variables (that is, strings + * which match <code>vairablesPattern</code> and adds them to the given + * set. + * @param toFill Set to which found variables should be added. + * @param n Node from which to read the attributes. + */ + private static void addVariablesFromNode(Set<String> toFill, Node n) { + NamedNodeMap nnm = n.getAttributes(); + + for (int i=0; i<nnm.getLength(); i++) { + String text = Tools.unmarshallText(nnm.item(i)); + Matcher m = variablesPattern.matcher(text); + + while (m.find()) { + toFill.add(text.substring(m.start(), m.end()+1)); + } + } + } + + protected static Set<String> getVariablesForNode(Node n) { + Set<String> variables = new HashSet<String>(); + // We always have to substitute the type variable. + variables.add(Constants.TYPE_VARIABLE); + addVariablesFromNode(variables, n); + + return variables; + } + + public static Map<String, FeedTagHandler> getHandlers(String group, Node definition, + VariableResolverManager vrm) { + // Getting an appropriate construtor to the given feed tag type. + Constructor handlerConstr = handlers.get(definition.getNodeName()); + if (handlerConstr == null) { + log.error("Unknown feed tag: " + definition.getNodeName() + "."); + return new HashMap<String, FeedTagHandler>(); + } + + // Reading the feed name attribute. + String feedNameAttr = Tools.getAttributeValue(definition, Constants.NAME_ATTR); + + // Getting all variables that are in the given node. + Set<String> variables = getVariablesForNode(definition); + + // Generating substitutions induced by the found variables. + Set<Substitution> substitutions = Substitution.newEmptySubstitionsSet(); + + for (String variable : new HashSet<String>(variables)) { + vrm.instantiateVariableResolver(variable, definition). + expandSubstitutionsSet(variables, substitutions); + } + + if (variables.size() > 0) { + log.warn("Unresolved variables left: " + variables.toString() + " in feed " + + feedNameAttr + "."); + } + + // Creating the map which will be returned. + Map<String, FeedTagHandler> ret = new HashMap<String, FeedTagHandler>(); + + // For each substitution, adding a tag handler to FeedsDescriptor. + for (Substitution s : substitutions) { + String feedName = s.make(feedNameAttr); + try { + ret.put(feedName, + (FeedTagHandler) handlerConstr.newInstance(group, definition, s)); + } catch (Exception e) { + log.error("Error constructing feed " + feedName + " tag handler.", e); + } + } + + return ret; + } +} Added: labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/RemoteFeedTagHandler.java =================================================================== --- labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/RemoteFeedTagHandler.java 2006-05-17 17:09:34 UTC (rev 4288) +++ labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/RemoteFeedTagHandler.java 2006-05-17 17:51:54 UTC (rev 4289) @@ -0,0 +1,60 @@ +package org.jboss.shotoku.feeds.tag; + +import org.w3c.dom.Node; +import org.jboss.shotoku.tools.Tools; +import org.jboss.shotoku.feeds.variables.Substitution; +import org.jboss.shotoku.feeds.data.Feed; +import org.jboss.shotoku.feeds.FeedFactory; +import org.jboss.shotoku.feeds.Constants; + +import java.util.Map; +import java.util.Set; +import java.util.HashSet; +import java.net.URL; + +/** + * @author Adam Warski (ad...@as...) + */ +public class RemoteFeedTagHandler extends FeedTagHandler { + public void generate(Map<String, FeedDefinition> feedDefs) { + for (String feedName : feedDefs.keySet()) { + FeedDefinition feedDefinition = feedDefs.get(feedName); + Node feedNode = feedDefinition.definition; + + String addressAttr = Tools.getAttributeValue(feedNode, "address"); + String displayNameAttr = Tools.getAttributeValue(feedNode, "display-name"); + + Set<String> variables = new HashSet<String>(); + // We always have to substitute the type variable. + variables.add(Constants.TYPE_VARIABLE); + addVariablesFromNode(variables, feedNode); + + Set<Substitution> substitutions = Substitution.newEmptySubstitionsSet(); + + for (String variable : new HashSet<String>(variables)) { + instantiateVariableResolver(variable, feedNode). + expandSubstitutionsSet(variables, substitutions); + } + + if (variables.size() > 0) { + log.warn("Unresolved variables left: " + variables.toString() + " in feed " + + feedName + "."); + continue; + } + + for (Substitution s : substitutions) { + String feedNameSub = s.make(feedName); + String feedType = s.getSubstitutionFor(Constants.TYPE_VARIABLE); + try { + Feed feed = FeedFactory.getNewFeed(feedNameSub, + s.make(displayNameAttr), feedType); + feed.generate(new URL(s.make(addressAttr))); + addFeed(feedNameSub, feedType, feed); + } catch (Exception e) { + log.debug("Can't add feed " + s.make(addressAttr) + " : " + feedType + + ".", e); + } + } + } + } +} Added: labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/ShotokuFeedTagHandler.java =================================================================== --- labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/ShotokuFeedTagHandler.java 2006-05-17 17:09:34 UTC (rev 4288) +++ labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/tag/ShotokuFeedTagHandler.java 2006-05-17 17:51:54 UTC (rev 4289) @@ -0,0 +1,201 @@ +package org.jboss.shotoku.feeds.tag; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.jboss.shotoku.search.SearchParameter; +import org.jboss.shotoku.search.Search; +import org.jboss.shotoku.ContentManager; +import org.jboss.shotoku.feeds.tools.RdfDateFormat; +import org.jboss.shotoku.feeds.tools.TextEscaping; +import org.jboss.shotoku.feeds.data.Feed; +import org.jboss.shotoku.feeds.FeedFactory; +import org.apache.velocity.VelocityContext; + +import java.util.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.text.SimpleDateFormat; +import java.io.Writer; + +/** + * @author Adam Warski (ad...@as...) + */ +public class ShotokuFeedTagHandler extends FeedTagHandler { + public ShotokuFeedTagHandler(String group, Node definition) { + super(group, definition); + } + + + /** + * Recursively generates a search parameter basing on its description that is + * contained in the given node. + * @param n Node from which to read the search parameters. + * @param props Properties with directives classes. + * @param parametrized Set of directive names that accept a parameter map + * in their constructor. + * @return A search parameter that is described in the given node. + * @throws IllegalArgumentException + * @throws SecurityException + * @throws InstantiationException + * @throws IllegalAccessException + * @throws java.lang.reflect.InvocationTargetException + * @throws NoSuchMethodException + * @throws ClassNotFoundException + */ + private SearchParameter generateSearchParameter(Node n, Properties props, + Set<String> parametrized, String siteId) + throws IllegalArgumentException, SecurityException, + InstantiationException, IllegalAccessException, + InvocationTargetException, NoSuchMethodException, + ClassNotFoundException { + String paramName = n.getNodeName(); + + // Looking for a corresponding class. + String paramClass = props.getProperty(paramName); + + if (paramClass == null) + throw new RuntimeException("Unknown parameter name: " + paramName + + "."); + + SearchParameter param; + if (parametrized.contains(paramName)) { + // Instatiating the class, it should have a constructor accepting a + // map. We take the map from node attributes, and replace + // ${site} and ${site-name} in them. + param = (SearchParameter) Class.forName(paramClass).getConstructor( + new Class[] { Map.class }).newInstance( + new Object[] { replaceInMapValues(replaceInMapValues( + XmlTools.getMapFromNodeAttributes(n), + SITE_PARAM, siteId), SITE_NAME_PARAM, + getSiteName(siteId)) }); + } else { + param = (SearchParameter) Class.forName(paramClass).newInstance(); + } + + NodeList nodeList = n.getChildNodes(); + for (int i = 0; i < nodeList.getLength(); i++) { + Node child = nodeList.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + // If the search paramater has child nodes, then the + // corresponding class should have an "add" method. + // Invoking the function recursively and adding the + // resulting parameter. + SearchParameter childParam = generateSearchParameter(child, + props, parametrized, siteId); + + Method[] methods = param.getClass().getMethods(); + for (Method method : methods) { + if ("add".equals(method.getName())) { + method.invoke(param, new Object[] { childParam }); + break; + } + } + } + } + + return param; + } + + private final static String RFC_822_DATE = "EEE, d MMM yyyy HH:mm:ss Z"; + + private void generateShotokuFeeds(Map<String, Node> feedDefs, + Properties props) { + Set<String> parametrized = new HashSet<String>(Arrays.asList(props.getProperty( + "parametrized").split("[,]"))); + + for (String feedName : feedDefs.keySet()) { + Node feedNode = feedDefs.get(feedName); + String templateAttr = XmlTools.getAttributeValue(feedNode, + "template"); + String prefixAttr = XmlTools.getAttributeValue(feedNode, "prefix"); + String idAttr = XmlTools.getAttributeValue(feedNode, "id"); + String typesAttr = XmlTools.getAttributeValue(feedNode, "type"); + String displayNameAttr = XmlTools.getAttributeValue(feedNode, + "display-name"); + + ContentManager cm = ContentManager.getContentManager(idAttr, + prefixAttr); + + // Generating the type of feeds that have been requested. + String[] types = getTypes(typesAttr); + + // Generating the sites for which the feeds have been requested, + // if any. + String[] siteIds = getSiteIds(); + + Map<String, String> attributes = XmlTools + .getMapFromNodeAttributes(feedNode); + + // Creating a velcoity context with parts that won't change in all + // generated feeds. + VelocityContext vc = new VelocityContext(attributes); + + String dateFormat = attributes.get("dateFormat"); + vc.put("dateFormat", new SimpleDateFormat( + dateFormat == null ? "MM/dd/yy" : dateFormat)); + vc.put("rssDateFormat", new SimpleDateFormat(RFC_822_DATE)); + vc.put("rdfDateFormat", new RdfDateFormat()); + vc.put("now", Calendar.getInstance().getTime()); + vc.put("baseServerAddress", baseServerAddress); + vc.put("escape", new TextEscaping()); + + for (String site : siteIds) { + String feedNameReplaced = feedName.replace(SITE_PARAM, + site); + + NodeList feedNodeChildren = feedNode.getChildNodes(); + for (int i = 0; i < feedNodeChildren.getLength(); i++) { + // For each search node, generating and performing the + // search, placing it under the demanded attribute in + // velocity's context. + Node searchNode = feedNodeChildren.item(i); + if ("search".equals(searchNode.getNodeName())) { + try { + Search search = (Search) generateSearchParameter( + searchNode, props, parametrized, site); + + org.jboss.shotoku.NodeList list = cm.search(search); + + String searchNameAttr = XmlTools.getAttributeValue( + searchNode, "name"); + if (searchNameAttr == null) + searchNameAttr = "nodes"; + + vc.put(searchNameAttr, list); + } catch (Exception e) { + log.warn("Error generating shotoku feed " + + feedName + ".", e); + continue; + } + } + } + + for (String type : types) { + String template = templateAttr.replace(TYPE_PARAM, type); + + Feed newFeed = FeedFactory.getNewFeed(type); + try { + vc.put("link", generateFeedLink(true, feedNameReplaced, + type)); + + // Now that we have type and site, we can render the + // template. + Writer w = newFeed.getWriter(); + cm.getVelocityEngine().mergeTemplate(template, vc, w); + w.flush(); + w.close(); + + // And add the feed. + addFeed(feedNameReplaced, type, newFeed); + } catch (Exception e) { + log.warn("Cannot merge template " + template + " for " + + feedName + ".", e); + continue; + } + } + } + } + } + +} Added: labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/variables/VariableResolverManager.java =================================================================== --- labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/variables/VariableResolverManager.java 2006-05-17 17:09:34 UTC (rev 4288) +++ labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/variables/VariableResolverManager.java 2006-05-17 17:51:54 UTC (rev 4289) @@ -0,0 +1,79 @@ +package org.jboss.shotoku.feeds.variables; + +import org.w3c.dom.Node; +import org.jboss.shotoku.feeds.Constants; +import org.jboss.logging.Logger; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.lang.reflect.Constructor; + +/** + * @author Adam Warski (ad...@as...) + */ +public class VariableResolverManager { + private static final Logger log = Logger.getLogger(VariableResolverManager.class); + + private Map<String, Constructor> varResolversNodeConstructor; + private Map<String, Constructor> varResolversEmptyConstructor; + + public VariableResolverManager(String id, Properties props) { + // Creating maps of available variable resolver classes + varResolversNodeConstructor = new HashMap<String, Constructor>(); + varResolversEmptyConstructor = new HashMap<String, Constructor>(); + + for (String variable : props.getProperty(id + Constants.ALL_VARS, "").split("[,]")) { + Class varResolverClass; + + try { + varResolverClass = Class.forName(props.getProperty(id + Constants.ONE_VAR + variable)); + } catch (ClassNotFoundException e) { + log.error("Variable resolver class " + props.getProperty(id + + Constants.ONE_VAR + variable) + " for variable " + variable + + " not found", e); + continue; + } + + try { + varResolversNodeConstructor.put(variable, varResolverClass.getConstructor(Node.class)); + } catch (NoSuchMethodException e) { + try { + varResolversEmptyConstructor.put(variable, varResolverClass.getConstructor()); + } catch (NoSuchMethodException e1) { + log.error("No (org.w3c.dom.Node) or () constructor foound for variable " + + props.getProperty(id + Constants.ONE_VAR + variable) + " resolver " + + varResolverClass.getName() + ".", e); + } + } + } + } + + public VariableResolver instantiateVariableResolver(String variable, Node n) { + Constructor c; + c = varResolversNodeConstructor.get(variable); + + if (c != null) { + try { + return (VariableResolver) c.newInstance(n); + } catch (Exception e) { + log.error("Error instatiating variable resolver " + c.getDeclaringClass().getName() + + ".", e); + } + } + + c = varResolversEmptyConstructor.get(variable); + + if (c != null) { + try { + return (VariableResolver) c.newInstance(); + } catch (Exception e) { + log.error("Error instatiating variable resolver " + c.getDeclaringClass().getName() + + ".", e); + } + } + + log.error("Unsupported variable found: " + variable + "."); + return new NullVariableResolver(); + } +} Modified: labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/variables/XmlNodeTypeVariableResolver.java =================================================================== --- labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/variables/XmlNodeTypeVariableResolver.java 2006-05-17 17:09:34 UTC (rev 4288) +++ labs/shotoku/trunk/shotoku-feeds/src/java/org/jboss/shotoku/feeds/variables/XmlNodeTypeVariableResolver.java 2006-05-17 17:51:54 UTC (rev 4289) @@ -4,6 +4,7 @@ import org.jboss.shotoku.tools.Tools; import org.jboss.shotoku.feeds.FeedFactory; import org.jboss.shotoku.feeds.FeedsDescriptor; +import org.jboss.shotoku.feeds.Constants; import java.util.Set; @@ -29,7 +30,7 @@ } protected String getVarName() { - return FeedsDescriptor.TYPE_VARIABLE; + return Constants.TYPE_VARIABLE; } protected String[] getValues() { |