Author: apetrelli Date: Sun Dec 3 08:11:53 2006 New Revision: 481785 URL: http://svn.apache.org/viewvc?view=rev&rev=481785 Log: SB-91 Ported ChannelFactorySet to extend UrlDefinitionsFactory and renamed to ChannelDefinitionsFactory.
Added: struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/ChannelDefinitionsFactory.java - copied, changed from r481578, struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/ChannelFactorySet.java Removed: struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/ChannelFactorySet.java Modified: struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/SelectChannelAction.java Copied: struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/ChannelDefinitionsFactory.java (from r481578, struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/ChannelFactorySet.java) URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/ChannelDefinitionsFactory.java?view=diff&rev=481785&p1=struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/ChannelFactorySet.java&r1=481578&p2=struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/ChannelDefinitionsFactory.java&r2=481785 ============================================================================== --- struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/ChannelFactorySet.java (original) +++ struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/ChannelDefinitionsFactory.java Sun Dec 3 08:11:53 2006 @@ -24,29 +24,23 @@ import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; import java.util.HashMap; +import java.util.List; +import java.util.Locale; import java.util.Map; -import javax.servlet.ServletContext; -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.struts.tiles.xmlDefinition.FactorySet; -import org.apache.struts.tiles.xmlDefinition.XmlDefinitionsSet; -import org.apache.struts.tiles.xmlDefinition.XmlParser; -import org.apache.tiles.definition.DefinitionsFactory; +import org.apache.tiles.context.TilesRequestContext; +import org.apache.tiles.definition.ComponentDefinition; +import org.apache.tiles.definition.ComponentDefinitions; import org.apache.tiles.definition.DefinitionsFactoryException; -import org.apache.tiles.definition.FactoryNotFoundException; -import org.xml.sax.SAXException; +import org.apache.tiles.definition.UrlDefinitionsFactory; /** - * Component Definitions factory. - * This implementation of Component Definitions factory allows i18n - * Component definitions factory allowing i18n definition. + * Channel Definitions factory. * A definition is retrieved by its name, and using locale setted in appropriate * session context. Definitions are defined in different files, one for each locale. * A definition file is loaded using common name extended with locale code @@ -55,164 +49,117 @@ * * @version $Rev$ $Date$ */ -public class ChannelFactorySet extends FactorySet { - - private static final Log log = LogFactory.getLog(ChannelFactorySet.class); - - /** - * Debug flag. - * @deprecated This will be removed in a release after Struts 1.2. - */ - public static final boolean debug = false; - - /** - * Default name. - */ - public static final String DEFAULT_DEFINITIONS_FILE_NAME = - "/WEB-INF/templateDefinitions.xml"; +public class ChannelDefinitionsFactory extends UrlDefinitionsFactory { - /** - * Config file parameter name. - */ - public static final String DEFINITIONS_CONFIG_PARAMETER_NAME = - "definitions-config"; + // FIXME This class contains too much code from UrlDefinitionsFactory. + // Probably that class must be refactored to allow an easier extension, but + // maybe we should wait until Dimensions is incubated. + + private static final Log log = LogFactory.getLog(ChannelDefinitionsFactory.class); /** * Config file parameter name. */ public static final String FACTORY_SELECTOR_KEY = - "ChannelFactorySet.factorySelectorKey"; - - /** - * Default filenames extension. - */ - public static final String FILENAME_EXTENSION = ".xml"; - - /** - * Default factory. - */ - protected DefinitionsFactory defaultFactory = null; - - /** - * Xml parser used. - */ - protected XmlParser xmlParser = null; - - /** - * Names of default file containing definition descriptions. - */ - private String filename = null; - - /** - * Collection of already loaded definitions set, referenced by their suffix. - */ - private Map loaded = null; - - /** - * Parameterless Constructor. - * Method initFactory must be called prior to any use of created factory. - */ - public ChannelFactorySet() { - super(); - } + "ChannelDefinitionsFactory.factorySelectorKey"; /** - * Constructor. - * Init the factory by reading appropriate configuration file. - * @throws FactoryNotFoundException Can't find factory configuration file. + * Contains a list of locales that have been processed. */ - public ChannelFactorySet(ServletContext servletContext, Map properties) - throws DefinitionsFactoryException { + private List<String> processedLocales; - initFactory(servletContext, properties); - } + private ComponentDefinitions definitions; /** - * Initialization method. - * Init the factory by reading appropriate configuration file. - * This method is called exactly once immediately after factory creation in - * case of internal creation (by DefinitionUtil). - * @param servletContext Servlet Context passed to newly created factory. - * @param properties Map of name/property passed to newly created factory. Map can contains - * more properties than requested. - * @throws DefinitionsFactoryException An error occur during initialization. + * Returns a ComponentDefinition object that matches the given name and + * Tiles context + * + * @param name The name of the ComponentDefinition to return. + * @param tilesContext The Tiles context to use to resolve the definition. + * @return the ComponentDefinition matching the given name or null if none + * is found. + * @throws DefinitionsFactoryException if an error occurs reading definitions. */ - public void initFactory(ServletContext servletContext, Map properties) + @Override + public ComponentDefinition getDefinition(String name, + TilesRequestContext tilesContext) throws DefinitionsFactoryException { - // read properties values - String proposedFilename = - (String) properties.get(DEFINITIONS_CONFIG_PARAMETER_NAME); - - // Compute filenames to use - boolean isFileSpecified = true; - if (proposedFilename == null) { - proposedFilename = DEFAULT_DEFINITIONS_FILE_NAME; - isFileSpecified = false; - } - - try { - initFactory(servletContext, proposedFilename); - return; - - } catch (FileNotFoundException ex) { - // If a filename is specified, throw appropriate error. - if (isFileSpecified) { - throw new FactoryNotFoundException( - ex.getMessage() - + " : Can't find file '" - + proposedFilename - + "'"); + ComponentDefinitions definitions = getComponentDefinitions(); + String key = getLocaleKey(tilesContext); + Locale locale = getLocale(key); + + if (tilesContext != null) { + locale = tilesContext.getRequestLocale(); + if (!isContextProcessed(tilesContext)) { + synchronized (definitions) { + addDefinitions(definitions, tilesContext); + } } } + return definitions.getDefinition(name, locale); } /** - * Initialization method. - * Init the factory by reading appropriate configuration file. - * This method is called exactly once immediately after factory creation in - * case of internal creation (by DefinitionUtil). - * @param servletContext Servlet Context passed to newly created factory. - * @param proposedFilename File names, comma separated, to use as base file names. - * @throws DefinitionsFactoryException An error occur during initialization. + * Appends locale-specific [EMAIL PROTECTED] ComponentDefinition} objects to an existing + * [EMAIL PROTECTED] ComponentDefinitions} set by reading locale-specific versions of + * the applied sources. + * + * @param definitions The ComponentDefinitions object to append to. + * @param tilesContext The requested locale. + * @throws DefinitionsFactoryException if an error occurs reading definitions. */ - protected void initFactory( - ServletContext servletContext, - String proposedFilename) - throws DefinitionsFactoryException, FileNotFoundException { - - filename = proposedFilename; - loaded = new HashMap(); - defaultFactory = createDefaultFactory(servletContext); - } - - /** - * Get default factory. - * @return Default factory - */ - protected DefinitionsFactory getDefaultFactory() { - return defaultFactory; - } - - /** - * Create default factory . - * @param servletContext Current servlet context. Used to open file. - * @return Created default definition factory. - * @throws DefinitionsFactoryException If an error occur while creating factory. - * @throws FileNotFoundException if factory can't be loaded from filenames. - */ - protected DefinitionsFactory createDefaultFactory(ServletContext servletContext) - throws DefinitionsFactoryException, FileNotFoundException { - - XmlDefinitionsSet rootXmlConfig = parseXmlKeyFile(servletContext, "", null); + @Override + protected void addDefinitions(ComponentDefinitions definitions, + TilesRequestContext tilesContext) + throws DefinitionsFactoryException { + String key = getLocaleKey(tilesContext); - if (rootXmlConfig == null) { - throw new FileNotFoundException(); + if (isContextProcessed(tilesContext)) { + return; + } + + if (key == null) { + return; } - rootXmlConfig.resolveInheritances(); - return new DefinitionsFactory(rootXmlConfig); + processedLocales.add(key); + Locale locale = getLocale(key); + List<String> postfixes = calculatePostfixes(locale); + Map localeDefsMap = new HashMap(); + for (Object postfix : postfixes) { + // For each postfix, all the sources must be loaded. + for (Object source : sources) { + URL url = (URL) source; + String path = url.toExternalForm(); + + String newPath = concatPostfix(path, (String) postfix); + try { + URL newUrl = new URL(newPath); + URLConnection connection = newUrl.openConnection(); + connection.connect(); + lastModifiedDates.put(newUrl.toExternalForm(), + connection.getLastModified()); + + // Definition must be collected, starting from the base + // source up to the last localized file. + Map defsMap = reader.read(connection.getInputStream()); + if (defsMap != null) { + localeDefsMap.putAll(defsMap); + } + } catch (FileNotFoundException e) { + // File not found. continue. + } catch (IOException e) { + throw new DefinitionsFactoryException( + "I/O error processing configuration."); + } + } + } + + // At the end of definitions loading, they can be assigned to + // ComponentDefinitions implementation, to allow inheritance resolution. + definitions.addDefinitions(localeDefsMap, locale); } /** @@ -223,173 +170,49 @@ * @return the key or null if not found. * @roseuid 3AF6F887018A */ - protected Object getDefinitionsFactoryKey( - String name, - ServletRequest request, - ServletContext servletContext) { - Object key = null; + @Override + protected boolean isContextProcessed(TilesRequestContext tilesContext) { + String key = null; - HttpSession session = ((HttpServletRequest) request).getSession(false); + Map session = tilesContext.getSessionScope(); if (session != null) { - key = session.getAttribute(FACTORY_SELECTOR_KEY); + key = (String) session.get(FACTORY_SELECTOR_KEY); } - - return key; + + return processedLocales.contains(key); } - /** - * Create a factory for specified key. - * If creation failed, return default factory, and output an error message in - * console. - * @param key - * @return Definition factory for specified key. - * @throws DefinitionsFactoryException If an error occur while creating factory. - */ - protected DefinitionsFactory createFactory( - Object key, - ServletRequest request, - ServletContext servletContext) + private ComponentDefinitions getComponentDefinitions() throws DefinitionsFactoryException { - - if (key == null) { - return getDefaultFactory(); + if (definitions == null) { + definitions = readDefinitions(); } - - // Already loaded ? - DefinitionsFactory factory = (DefinitionsFactory) loaded.get(key); - if (factory != null) { // yes, stop loading - return factory; - } - - // Try to load file associated to key. If fail, stop and return default factory. - XmlDefinitionsSet lastXmlFile = - parseXmlKeyFile(servletContext, "_" + (String) key, null); - - if (lastXmlFile == null) { - log.warn( - "No definition factory associated to key '" - + key - + "'. Use default factory instead."); - - factory = getDefaultFactory(); - loaded.put(key, factory); - return factory; - } - - // Parse default file, and add key file. - XmlDefinitionsSet rootXmlConfig = parseXmlKeyFile(servletContext, "", null); - - rootXmlConfig.extend(lastXmlFile); - rootXmlConfig.resolveInheritances(); - - factory = new DefinitionsFactory(rootXmlConfig); - loaded.put(key, factory); - - log.info(factory); - - // return last available found ! - return factory; - } - - /** - * Parse files associated to postix if they exist. - * For each name in filenames, append postfix before file extension, - * then try to load the corresponding file. - * If file doesn't exist, try next one. Each file description is added to - * the XmlDefinitionsSet description. - * The XmlDefinitionsSet description is created only if there is a definition file. - * Inheritance is not resolved in the returned XmlDefinitionsSet. - * If no description file can be opened, and no definiion set is provided, return null. - * @param postfix Postfix to add to each description file. - * @param xmlDefinitions Definitions set to which definitions will be added. If null, a definitions - * set is created on request. - * @return XmlDefinitionsSet The definitions set created or passed as parameter. - * @throws DefinitionsFactoryException If an error happen during file parsing. - */ - private XmlDefinitionsSet parseXmlKeyFile( - ServletContext servletContext, - String postfix, - XmlDefinitionsSet xmlDefinitions) - throws DefinitionsFactoryException { - if (postfix != null && postfix.length() == 0) - postfix = null; - - String fullName = concatPostfix(filename, postfix); - return parseXmlFile(servletContext, fullName, xmlDefinitions); + return definitions; } + + private String getLocaleKey(TilesRequestContext tilesContext) { + String key = null; - /** - * Parse specified xml file and add definition to specified definitions set. - * This method is used to load several description files in one instances list. - * If filename exist and definition set is null, create a new set. Otherwise, return - * passed definition set (can be null). - * @param servletContext Current servlet context. Used to open file. - * @param filename Name of file to parse. - * @param xmlDefinitions Definitions set to which definitions will be added. If null, a definitions - * set is created on request. - * @return XmlDefinitionsSet The definitions set created or passed as parameter. - * @throws DefinitionsFactoryException If an error happen during file parsing. - */ - private XmlDefinitionsSet parseXmlFile( - ServletContext servletContext, - String filename, - XmlDefinitionsSet xmlDefinitions) - throws DefinitionsFactoryException { - - try { - log.debug("Trying to load '" + filename + "'."); - - InputStream input = servletContext.getResourceAsStream(filename); - if (input == null) { - return xmlDefinitions; - } - - xmlParser = new XmlParser(); - - // Check if definition set already exist. - if (xmlDefinitions == null) { - xmlDefinitions = new XmlDefinitionsSet(); - } - - xmlParser.parse(input, xmlDefinitions); - - } catch (SAXException ex) { - log.debug("Error while parsing file '" + filename + "'.", ex); - - throw new DefinitionsFactoryException( - "Error while parsing file '" + filename + "'. " + ex.getMessage(), - ex); - - } catch (IOException ex) { - throw new DefinitionsFactoryException( - "IO Error while parsing file '" + filename + "'. " + ex.getMessage(), - ex); + Map session = tilesContext.getSessionScope(); + if (session != null) { + key = (String) session.get(FACTORY_SELECTOR_KEY); } - - return xmlDefinitions; + + return key; } - - /** - * Concat postfix to the name. Take care of existing filename extension. - * Transform the given name "name.ext" to have "name" + "postfix" + "ext". - * If there is no ext, return "name" + "postfix". - */ - private String concatPostfix(String name, String postfix) { - if (postfix == null) { - return name; + + private Locale getLocale(String localeKey) { + Locale retValue = null; + String[] localeArray; + + if (localeKey != null) { + localeArray = localeKey.split("_"); + + retValue = new Locale(localeArray.length > 0 ? localeArray[0] : null, + localeArray.length > 1 ? localeArray[1] : null, + localeArray.length > 2 ? localeArray[2] : null); } - - //postfix = "_" + postfix; - // Search file name extension. - // take care of Unix files starting with . - int dotIndex = name.lastIndexOf("."); - int lastNameStart = name.lastIndexOf(java.io.File.pathSeparator); - if (dotIndex < 1 || dotIndex < lastNameStart) - return name + postfix; - - String ext = name.substring(dotIndex); - name = name.substring(0, dotIndex); - return name + postfix + ext; + + return retValue; } - } Modified: struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/SelectChannelAction.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/SelectChannelAction.java?view=diff&rev=481785&r1=481784&r2=481785 ============================================================================== --- struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/SelectChannelAction.java (original) +++ struts/sandbox/trunk/tiles/tiles-showcase/src/main/java/org/apache/tiles/showcase/channel/SelectChannelAction.java Sun Dec 3 08:11:53 2006 @@ -70,7 +70,7 @@ // set in session HttpSession session = request.getSession(false); if (session != null) - session.setAttribute(ChannelFactorySet.FACTORY_SELECTOR_KEY, requested); + session.setAttribute(ChannelDefinitionsFactory.FACTORY_SELECTOR_KEY, requested); System.out.println( "Set channel to '" + requested + "'" ); return (mapping.findForward("success")); }