You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2008/03/18 21:22:18 UTC

svn commit: r638552 - in /incubator/sling/trunk/sling/i18n: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/sling/ src/main/java/org/apache/sling/i18n/ src/main/java/org/apache/sling/i18n/impl/ src...

Author: fmeschbe
Date: Tue Mar 18 13:22:14 2008
New Revision: 638552

URL: http://svn.apache.org/viewvc?rev=638552&view=rev
Log:
SLING-335 Add new Internationalization Support Bundle

Added:
    incubator/sling/trunk/sling/i18n/   (with props)
    incubator/sling/trunk/sling/i18n/pom.xml   (with props)
    incubator/sling/trunk/sling/i18n/src/
    incubator/sling/trunk/sling/i18n/src/main/
    incubator/sling/trunk/sling/i18n/src/main/java/
    incubator/sling/trunk/sling/i18n/src/main/java/org/
    incubator/sling/trunk/sling/i18n/src/main/java/org/apache/
    incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/
    incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/
    incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/LocaleResolver.java   (with props)
    incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/ResourceBundleProvider.java   (with props)
    incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/
    incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/I18NFilter.java   (with props)
    incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundle.java   (with props)
    incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java   (with props)
    incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/ResourceBundleEnumeration.java   (with props)
    incubator/sling/trunk/sling/i18n/src/main/resources/
    incubator/sling/trunk/sling/i18n/src/main/resources/OSGI-INF/
    incubator/sling/trunk/sling/i18n/src/main/resources/OSGI-INF/metatype/
    incubator/sling/trunk/sling/i18n/src/main/resources/OSGI-INF/metatype/metatype.properties   (with props)
    incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/
    incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/
    incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/language.cnd   (with props)
    incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/message.cnd   (with props)

Propchange: incubator/sling/trunk/sling/i18n/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Mar 18 13:22:14 2008
@@ -0,0 +1,4 @@
+.classpath
+.project
+.settings
+target

Added: incubator/sling/trunk/sling/i18n/pom.xml
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/sling/i18n/pom.xml?rev=638552&view=auto
==============================================================================
--- incubator/sling/trunk/sling/i18n/pom.xml (added)
+++ incubator/sling/trunk/sling/i18n/pom.xml Tue Mar 18 13:22:14 2008
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>1-incubator-SNAPSHOT</version>
+        <relativePath>../../parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>org.apache.sling.i18n</artifactId>
+    <packaging>bundle</packaging>
+    <version>2.0.0-incubator-SNAPSHOT</version>
+
+    <name>Sling Internationalization Support</name>
+    <description>
+        Support for creating Java I18N ResourceBundles from repository
+        resources.
+    </description>
+
+    <scm>
+        <connection>
+            scm:svn:http://svn.apache.org/repos/asf/incubator/sling/trunk/sling/i18n
+        </connection>
+        <developerConnection>
+            scm:svn:https://svn.apache.org/repos/asf/incubator/sling/trunk/sling/i18n
+        </developerConnection>
+        <url>
+            http://svn.apache.org/viewvc/incubator/sling/trunk/sling/i18n
+        </url>
+    </scm>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            org.apache.sling.i18n
+                        </Export-Package>
+                        <Private-Package>
+                            org.apache.sling.i18n.impl
+                        </Private-Package>
+
+                        <Sling-Nodetypes>
+                            SLING-INF/nodetypes/language.cnd,
+                            SLING-INF/nodetypes/message.cnd
+                        </Sling-Nodetypes>
+
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+    <version>2.0.0-incubator-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.jcr.api</artifactId>
+    <version>2.0.0-incubator-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.jcr.resource</artifactId>
+    <version>2.0.0-incubator-SNAPSHOT</version>
+</dependency>
+        <dependency>
+            <groupId>javax.jcr</groupId>
+            <artifactId>jcr</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

Propchange: incubator/sling/trunk/sling/i18n/pom.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/LocaleResolver.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/LocaleResolver.java?rev=638552&view=auto
==============================================================================
--- incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/LocaleResolver.java (added)
+++ incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/LocaleResolver.java Tue Mar 18 13:22:14 2008
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.i18n;
+
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+
+public interface LocaleResolver {
+
+    List<Locale> resolveLocale(SlingHttpServletRequest request);
+    
+}

Propchange: incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/LocaleResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/ResourceBundleProvider.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/ResourceBundleProvider.java?rev=638552&view=auto
==============================================================================
--- incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/ResourceBundleProvider.java (added)
+++ incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/ResourceBundleProvider.java Tue Mar 18 13:22:14 2008
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.i18n;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+/**
+ * The <code>ResourceBundleProvider</code> service interface defines the API
+ * for a service, which is capable of returned <code>ResourceBundle</code> for
+ * given any <code>Locale</code>.
+ */
+public interface ResourceBundleProvider {
+
+    /**
+     * Returns a <code>ResourceBundle</code> for the given locale.
+     * 
+     * @param locale The <code>Locale</code> for which to return the resource
+     *            bundle. If this is <code>null</code> the platform default
+     *            locale as returned by <code>Locale.getDefault()</code> is
+     *            assumed.
+     * @return The <code>ResourceBundle</code> for the given locale
+     * @throws MissingResourceException If the service is not capable of
+     *             returning a <code>ResourceBundle</code>
+     */
+    ResourceBundle getResourceBundle(Locale locale);
+
+}

Propchange: incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/ResourceBundleProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/I18NFilter.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/I18NFilter.java?rev=638552&view=auto
==============================================================================
--- incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/I18NFilter.java (added)
+++ incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/I18NFilter.java Tue Mar 18 13:22:14 2008
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.i18n.impl;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper;
+import org.apache.sling.i18n.LocaleResolver;
+import org.apache.sling.i18n.ResourceBundleProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>I18NFilter</code> class is a request level filter, which provides
+ * the resource bundle for the current request.
+ * 
+ * @scr.component immediate="true" metatype="false"
+ * @scr.property name="service.description" value="Internationalization Support
+ *               Filter"
+ * @scr.property name="service.vendor" value="The Apache Software Foundation"
+ * @scr.property name="filter.scope" value="request" private="true"
+ * @scr.property name="filter.order" value="-700" type="Integer" private="true"
+ * @scr.service
+ */
+public class I18NFilter implements Filter {
+
+    /** default log */
+    private static final Logger log = LoggerFactory.getLogger(I18NFilter.class.getName());
+
+    /**
+     * @scr.property value="en_US"
+     */
+    public static final String PAR_DEFAULT_LOCALE = "locale.default";
+
+    private static LocaleResolver DEFAULT_LOCALE_RESOLVER = new LocaleResolver() {
+        public List<Locale> resolveLocale(SlingHttpServletRequest request) {
+
+            // unwrap the request which is a I18NSlingHttpServletRequest
+            request = ((SlingHttpServletRequestWrapper) request).getSlingRequest();
+
+            Enumeration<?> locales = request.getLocales();
+            List<Locale> localeList = new ArrayList<Locale>();
+            while (locales.hasMoreElements()) {
+                localeList.add((Locale) locales.nextElement());
+            }
+
+            return localeList;
+        }
+    };
+
+    /**
+     * @scr.reference cardinality="0..1" policy="dynamic"
+     */
+    private LocaleResolver localeResolver = DEFAULT_LOCALE_RESOLVER;
+
+    /** @scr.reference cardinality="0..1" policy="dynamic" */
+    ResourceBundleProvider resourceBundleProvider;
+
+    private Locale defaultLocale;
+
+    public void init(FilterConfig filterConfig) {
+        // nothing to do
+    }
+
+    public void doFilter(ServletRequest request, ServletResponse response,
+            FilterChain chain) throws IOException, ServletException {
+
+        // wrap with our ResourceBundle provisioning
+        request = new I18NSlingHttpServletRequest(request,
+            resourceBundleProvider, localeResolver, defaultLocale);
+
+        // and forward the request
+        chain.doFilter(request, response);
+    }
+
+    public void destroy() {
+        // nothing to do
+    }
+
+    // ---------- SCR Integration ----------------------------------------------
+
+    protected void activate(org.osgi.service.component.ComponentContext context) {
+        @SuppressWarnings("unchecked")
+        Dictionary<String, Object> configuration = context.getProperties();
+        String localeString = (String) configuration.get(PAR_DEFAULT_LOCALE);
+        this.defaultLocale = this.toLocale(localeString);
+    }
+
+    protected void bindLocaleResolver(LocaleResolver localeResolver) {
+        this.localeResolver = localeResolver;
+    }
+
+    protected void unbindLocaleResolver(LocaleResolver localeResolver) {
+        if (this.localeResolver == localeResolver) {
+            this.localeResolver = DEFAULT_LOCALE_RESOLVER;
+        }
+    }
+
+    // ---------- internal -----------------------------------------------------
+
+    private Locale toLocale(String localeString) {
+        if (localeString == null || localeString.length() == 0) {
+            return Locale.getDefault();
+        }
+
+        // check whether we have an exact match locale
+        Locale[] available = Locale.getAvailableLocales();
+        for (int i = 0; i < available.length; i++) {
+            if (available[i].toString().equals(localeString)) {
+                return available[i];
+            }
+        }
+
+        // check language and country
+        String[] parts = localeString.split("_");
+        if (parts.length == 0) {
+            return Locale.getDefault();
+        }
+
+        // at least language is available
+        String lang = parts[0];
+        String[] langs = Locale.getISOLanguages();
+        for (int i = 0; i < langs.length; i++) {
+            if (langs[i].equals(lang)) {
+                lang = null; // signal ok
+                break;
+            }
+        }
+        if (lang != null) {
+            parts[0] = Locale.getDefault().getLanguage();
+        }
+
+        // only language
+        if (parts.length == 1) {
+            return new Locale(parts[0]);
+        }
+
+        // country is also available
+        String country = parts[1];
+        String[] countries = Locale.getISOCountries();
+        for (int i = 0; i < countries.length; i++) {
+            if (countries[i].equals(lang)) {
+                lang = null; // signal ok
+                break;
+            }
+        }
+        if (country != null) {
+            parts[1] = Locale.getDefault().getCountry();
+        }
+
+        // language and country
+        if (parts.length == 2) {
+            return new Locale(parts[0], parts[1]);
+        }
+
+        // language, country and variant
+        return new Locale(parts[0], parts[1], parts[2]);
+    }
+
+    // ---------- internal class -----------------------------------------------
+
+    private static class I18NSlingHttpServletRequest extends
+            SlingHttpServletRequestWrapper {
+
+        private final ResourceBundleProvider bundleProvider;
+
+        private final LocaleResolver localeResolver;
+
+        private final Locale defaultLocale;
+
+        private Locale locale;
+
+        private List<Locale> localeList;
+
+        I18NSlingHttpServletRequest(ServletRequest delegatee,
+                ResourceBundleProvider bundleProvider,
+                LocaleResolver localeResolver, Locale defaultLocale) {
+            super((SlingHttpServletRequest) delegatee);
+            this.bundleProvider = bundleProvider;
+            this.localeResolver = localeResolver;
+            this.defaultLocale = defaultLocale;
+        }
+
+        @Override
+        public ResourceBundle getResourceBundle(Locale locale) {
+            if (bundleProvider != null) {
+                if (locale == null) {
+                    locale = getLocale();
+                }
+
+                try {
+                    return bundleProvider.getResourceBundle(locale);
+                } catch (MissingResourceException mre) {
+                    log.warn(
+                        "getResourceBundle: Cannot get ResourceBundle from provider",
+                        mre);
+                }
+            } else {
+                log.info("getResourceBundle: ResourceBundleProvider not available, calling default implementation");
+            }
+
+            return super.getResourceBundle(locale);
+        }
+
+        @Override
+        public Locale getLocale() {
+            if (locale == null) {
+                List<Locale> localeList = getLocaleList();
+                if (localeList.isEmpty()) {
+                    locale = defaultLocale;
+                } else {
+                    locale = localeList.get(0);
+                }
+            }
+
+            return locale;
+        }
+
+        @Override
+        public Enumeration<?> getLocales() {
+            return Collections.enumeration(getLocaleList());
+        }
+
+        private List<Locale> getLocaleList() {
+            if (localeList == null) {
+                localeList = localeResolver.resolveLocale(this);
+            }
+
+            return localeList;
+        }
+    }
+
+}

Propchange: incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/I18NFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundle.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundle.java?rev=638552&view=auto
==============================================================================
--- incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundle.java (added)
+++ incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundle.java Tue Mar 18 13:22:14 2008
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.i18n.impl;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import javax.jcr.query.Query;
+
+import org.apache.sling.api.resource.ResourceResolver;
+
+public class JcrResourceBundle extends ResourceBundle {
+
+    static final String PROP_KEY = "sling:key";
+
+    private static final String PROP_VALUE = "sling:message";
+
+    private static final String QUERY_BASE = "//element(*,mix:language)[@jcr:language='%s']/*";
+
+    private static final String QUERY_LOAD_FULLY = QUERY_BASE + "/(@"
+        + PROP_KEY + "|@" + PROP_VALUE + ")";
+
+    private static final String QUERY_LOAD_RESOURCE = QUERY_BASE + "[@"
+        + PROP_KEY + "='%s']/@" + PROP_VALUE;
+
+    private final ResourceResolver resourceResolver;
+
+    private Map<String, Object> resources;
+
+    private boolean fullyLoaded;
+
+    protected final Locale locale;
+
+    JcrResourceBundle(Locale locale, ResourceResolver resourceResolver) {
+        this.locale = locale;
+        this.resourceResolver = resourceResolver;
+        this.resources = new HashMap<String, Object>();
+        this.fullyLoaded = false;
+    }
+
+    @Override
+    protected void setParent(ResourceBundle parent) {
+        super.setParent(parent);
+    }
+
+    @Override
+    public Locale getLocale() {
+        return locale;
+    }
+
+    @Override
+    public Enumeration<String> getKeys() {
+        // ensure all keys are loaded
+        loadFully();
+
+        Enumeration<String> parentKeys = (parent != null)
+                ? parent.getKeys()
+                : null;
+        return new ResourceBundleEnumeration(resources.keySet(), parentKeys);
+    }
+
+    @Override
+    protected Object handleGetObject(String key) {
+        Object value = resources.get(key);
+        if (value == null && !fullyLoaded) {
+            value = loadResource(key);
+            if (value != null) {
+                resources.put(key, value);
+            }
+        }
+        return value;
+    }
+
+    private void loadFully() {
+        if (!fullyLoaded) {
+            Iterator<Map<String, Object>> bundles = resourceResolver.queryResources(
+                getFullLoadQuery(), Query.XPATH);
+            while (bundles.hasNext()) {
+                Map<String, Object> resource = bundles.next();
+                Object key = resource.get(PROP_KEY);
+                if (key instanceof String && !resources.containsKey(key)) {
+                    Object value = resource.get(PROP_VALUE);
+                    if (value != null) {
+                        resources.put((String) key, value);
+                    }
+                }
+            }
+        }
+    }
+
+    private Object loadResource(String key) {
+        // query for the resource
+        Iterator<Map<String, Object>> bundles = resourceResolver.queryResources(
+            getResourceQuery(key), Query.XPATH);
+        if (bundles.hasNext()) {
+            Map<String, Object> resource = bundles.next();
+            return resource.get(PROP_VALUE);
+        }
+
+        return null;
+    }
+
+    private String getFullLoadQuery() {
+        return String.format(QUERY_LOAD_FULLY, getLocale());
+    }
+
+    private String getResourceQuery(String key) {
+        return String.format(QUERY_LOAD_RESOURCE, getLocale(), key);
+    }
+
+}

Propchange: incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundle.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java?rev=638552&view=auto
==============================================================================
--- incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java (added)
+++ incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java Tue Mar 18 13:22:14 2008
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.i18n.impl;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+import javax.jcr.observation.ObservationManager;
+
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.i18n.ResourceBundleProvider;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.resource.JcrResourceResolverFactory;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The <code>JcrResourceBundleProvider</code> TODO
+ * 
+ * @scr.component immediate="true"  label="%provider.name"
+ *                description="%provider.description"
+ * @scr.service interface="org.apache.sling.i18n.ResourceBundleProvider"
+ */
+public class JcrResourceBundleProvider implements ResourceBundleProvider,
+        EventListener {
+
+    /** default log */
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    
+    /** @scr.property value="" */
+    private static final String PROP_USER = "user";
+
+    /** @scr.property value="" */
+    private static final String PROP_PASS = "password";
+
+    /** @scr.reference cardinality="0..1" policy="dynamic" */
+    private SlingRepository repository;
+
+    /** @scr.reference cardinality="0..1" policy="dynamic" */
+    private JcrResourceResolverFactory resourceResolverFactory;
+
+    private Credentials repoCredentials;
+
+    private ResourceResolver resourceResolver;
+
+    private Map<Locale, ResourceBundle> resourceBundleCache = new HashMap<Locale, ResourceBundle>();
+
+    public ResourceBundle getResourceBundle(Locale locale) {
+        if (locale == null) {
+            locale = Locale.getDefault();
+        }
+
+        ResourceResolver resolver = getResourceResolver();
+        if (resolver == null) {
+            log.info("createResourceBundle: Missing Resource Resolver, cannot create Resource Bundle");
+            throw new MissingResourceException(
+                "ResourceResolver not available", getClass().getName(), "");
+        }
+        
+        return getResourceBundleInternal(resolver, locale);
+    }
+    
+    private ResourceBundle getResourceBundleInternal(ResourceResolver resolver, Locale locale) {
+        ResourceBundle resourceBundle = resourceBundleCache.get(locale);
+        if (resourceBundle == null) {
+            resourceBundle = createResourceBundle(resolver, locale);
+            resourceBundleCache.put(locale, resourceBundle);
+        }
+
+        return resourceBundle;
+    }
+
+    private ResourceBundle createResourceBundle(ResourceResolver resolver, Locale locale) {
+
+        JcrResourceBundle bundle = new JcrResourceBundle(locale, resolver);
+
+        // set parent resource bundle
+        Locale parent = getParentLocale(locale);
+        if (parent != null) {
+            bundle.setParent(getResourceBundleInternal(resolver, parent));
+        }
+
+        return bundle;
+    }
+
+    private Locale getParentLocale(Locale locale) {
+        if (locale.getVariant().length() != 0) {
+            return new Locale(locale.getLanguage(), locale.getCountry());
+        } else if (locale.getCountry().length() != 0) {
+            return new Locale(locale.getLanguage());
+        } else if (!locale.getLanguage().equals(
+            Locale.getDefault().getLanguage())) {
+            return Locale.getDefault();
+        }
+
+        // no more parents
+        return null;
+    }
+
+    private ResourceResolver getResourceResolver() {
+        if (resourceResolver == null) {
+            SlingRepository repo = this.repository;
+            JcrResourceResolverFactory fac = this.resourceResolverFactory;
+            if (repo != null && fac != null) {
+                Session s = null;
+                try {
+                    if (repoCredentials == null) {
+                        s = repo.login();
+                    } else {
+                        s = repo.login(repoCredentials);
+                    }
+
+                    ObservationManager om = s.getWorkspace().getObservationManager();
+                    om.addEventListener(this, 255, "/", true, null,
+                        new String[] { "mix:language" }, true);
+                    
+                    resourceResolver = fac.getResourceResolver(s);
+                    
+                } catch (RepositoryException re) {
+                    // TODO log
+                } finally {
+                    // drop session if we can login but not get the resource resolver
+                    if (resourceResolver == null && s != null) {
+                        s.logout();
+                    }
+                }
+
+            }
+        }
+
+        return resourceResolver;
+    }
+
+    private void releaseRepository() {
+        ResourceResolver resolver = this.resourceResolver;
+
+        this.resourceResolver = null;
+        this.resourceBundleCache.clear();
+
+        if (resolver != null) {
+            
+            Session s = resolver.adaptTo(Session.class);
+            if (s != null) {
+
+                try {
+                    ObservationManager om = s.getWorkspace().getObservationManager();
+                    om.removeEventListener(this);
+                } catch (Throwable t) {
+                    // TODO log
+                }
+
+                try {
+                    s.logout();
+                } catch (Throwable t) {
+                    // TODO log
+                }
+            }
+        }
+    }
+
+    // ---------- EventListener ------------------------------------------------
+
+    public void onEvent(EventIterator events) {
+        // don't care for the concrete events, just drop the cache
+        log.info("onEvent: Got " + events.getSize() + " events");
+        resourceBundleCache.clear();
+    }
+
+    // ---------- SCR Integration ----------------------------------------------
+
+    protected void activate(ComponentContext context) {
+        Dictionary<?, ?> props = context.getProperties();
+
+        String user = (String) props.get(PROP_USER);
+        if (user == null || user.length() == 0) {
+            repoCredentials = null;
+        } else {
+            String pass = (String) props.get(PROP_PASS);
+            char[] pwd = (pass == null) ? new char[0] : pass.toCharArray();
+            repoCredentials = new SimpleCredentials(user, pwd);
+        }
+    }
+
+    protected void bindRepository(SlingRepository repository) {
+        if (this.repository != null) {
+            releaseRepository();
+        }
+        this.repository = repository;
+    }
+
+    protected void unbindRepository(SlingRepository repository) {
+        if (this.repository == repository) {
+            releaseRepository();
+            this.repository = null;
+        }
+    }
+
+    protected void bindResourceResolverFactory(
+            JcrResourceResolverFactory resourceResolverFactory) {
+        if (this.resourceResolverFactory != null) {
+            releaseRepository();
+        }
+        this.resourceResolverFactory = resourceResolverFactory;
+    }
+
+    protected void unbindResourceResolverFactory(
+            JcrResourceResolverFactory resourceResolverFactory) {
+        if (this.resourceResolverFactory == resourceResolverFactory) {
+            releaseRepository();
+            this.resourceResolverFactory = null;
+        }
+    }
+}

Propchange: incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/ResourceBundleEnumeration.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/ResourceBundleEnumeration.java?rev=638552&view=auto
==============================================================================
--- incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/ResourceBundleEnumeration.java (added)
+++ incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/ResourceBundleEnumeration.java Tue Mar 18 13:22:14 2008
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.i18n.impl;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+public class ResourceBundleEnumeration implements Enumeration<String> {
+
+    private final Set<String> keys;
+    private Enumeration<String> parentKeys;
+
+    private Iterator<String> keysIterator;
+    private String next;
+    
+    public ResourceBundleEnumeration(Set<String> keys, Enumeration<String> parentKeys) {
+        this.keys = keys;
+        this.parentKeys = parentKeys;
+        this.keysIterator = keys.iterator();
+        
+        next = seek();
+    }
+    
+    public boolean hasMoreElements() {
+        return next != null;
+    }
+
+    public String nextElement() {
+        if (!hasMoreElements()) {
+            throw new NoSuchElementException();
+        }
+        
+        String result = next;
+        next = seek();
+        return result;
+    }
+
+    private String seek() {
+        if (keysIterator != null) {
+            
+            if (keysIterator.hasNext()) {
+                return keysIterator.next();
+            }
+            
+            // my keys are exhausted, set iterator to null
+            keysIterator = null;
+        }
+        
+        if (parentKeys != null) {
+            while (parentKeys.hasMoreElements()) {
+                String parentKey = parentKeys.nextElement();
+                if (!keys.contains(parentKey)) {
+                    return parentKey;
+                }
+            }
+            
+            // no more parent keys, set enumeration to null
+            parentKeys = null;
+        }
+        
+        // parentKeys are also exhausted, nothing more to return
+        return null;
+    }
+}

Propchange: incubator/sling/trunk/sling/i18n/src/main/java/org/apache/sling/i18n/impl/ResourceBundleEnumeration.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/sling/i18n/src/main/resources/OSGI-INF/metatype/metatype.properties
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/sling/i18n/src/main/resources/OSGI-INF/metatype/metatype.properties?rev=638552&view=auto
==============================================================================
--- incubator/sling/trunk/sling/i18n/src/main/resources/OSGI-INF/metatype/metatype.properties (added)
+++ incubator/sling/trunk/sling/i18n/src/main/resources/OSGI-INF/metatype/metatype.properties Tue Mar 18 13:22:14 2008
@@ -0,0 +1,48 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+#
+
+#
+# (Default) Locale Resolver Filter
+locale.name = Locale Resolver Filter
+locale.description = Request Level filter which extracts the primary locale \
+ from the Request, which has been transferred from the client in the HTTP \
+ Accept-Language header. If a LocaleResolver service has been registered, that \
+ service is called instead of getting the HTTP header value for the locale.
+locale.default.name = Default Locale
+locale.default.description = The default locale to assume if none can be \
+ resolved from a LocalResolver service or the HTTP Accept-Language header. This \
+ value must be in the form acceptable to the =java.util.Locale= class.
+ 
+
+provider.name = JCR ResourceBundle Provider
+provider.description = ResourceBundleProvider service which loads the messages \
+ from the repository. You may configure how the provider accesses the \
+ repository if the user name field is left empty, the provider accesses the \
+ repository anonymously. Otherwise the given user name and password are used \
+ to access the repository. Failing to access the repository, effectively \
+ disables the provider.
+
+user.name = User Name
+user.description = The name of the user to log in to the repository to get the \
+ resources. If this field is empty, the provider accesses the repository as \
+ the anonymous user.
+
+password.name = Password
+password.description = The password used to log in to the repository to get \
+ the resources. This field is only used if the user name field is not empty.
\ No newline at end of file

Propchange: incubator/sling/trunk/sling/i18n/src/main/resources/OSGI-INF/metatype/metatype.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/language.cnd
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/language.cnd?rev=638552&view=auto
==============================================================================
--- incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/language.cnd (added)
+++ incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/language.cnd Tue Mar 18 13:22:14 2008
@@ -0,0 +1,27 @@
+//
+//  Licensed to the Apache Software Foundation (ASF) under one
+//  or more contributor license agreements.  See the NOTICE file
+//  distributed with this work for additional information
+//  regarding copyright ownership.  The ASF licenses this file
+//  to you under the Apache License, Version 2.0 (the
+//  "License"); you may not use this file except in compliance
+//  with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the License is distributed on an
+//  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//  KIND, either express or implied.  See the License for the
+//  specific language governing permissions and limitations
+//  under the License.
+//
+
+<mix = 'http://www.jcp.org/jcr/mix/1.0'>
+
+// Mixin node type for I18N resources
+// This mixin node type is defined in the JSR-283 specification and is
+// listed here to backport this node type into JSR-170 repositories
+[mix:language]
+    mixin
+  - jcr:language (string)

Propchange: incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/language.cnd
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/message.cnd
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/message.cnd?rev=638552&view=auto
==============================================================================
--- incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/message.cnd (added)
+++ incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/message.cnd Tue Mar 18 13:22:14 2008
@@ -0,0 +1,30 @@
+//
+//  Licensed to the Apache Software Foundation (ASF) under one
+//  or more contributor license agreements.  See the NOTICE file
+//  distributed with this work for additional information
+//  regarding copyright ownership.  The ASF licenses this file
+//  to you under the Apache License, Version 2.0 (the
+//  "License"); you may not use this file except in compliance
+//  with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the License is distributed on an
+//  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//  KIND, either express or implied.  See the License for the
+//  specific language governing permissions and limitations
+//  under the License.
+//
+
+<sling = 'http://sling.apache.org/jcr/sling/1.0'>
+
+// Mixin node type for a single key to message mapping
+// This mixin node type may be applied to any node such, that mappings
+// may be located just inside some existing node hierarchy
+[sling:message]
+    mixin
+  - sling:key (string)
+  - sling:message (undefined)
+
+[sling:messageEntry] > nt:unstructured, sling:message  
\ No newline at end of file

Propchange: incubator/sling/trunk/sling/i18n/src/main/resources/SLING-INF/nodetypes/message.cnd
------------------------------------------------------------------------------
    svn:eol-style = native