/*
* Copyright (c) 2001-2007, Inversoft Inc., All Rights Reserved
*
* Licensed 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.primeframework.mvc.message.l10n;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.List;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import org.primeframework.mvc.PrimeException;
import org.primeframework.mvc.config.MVCConfiguration;
import org.primeframework.mvc.container.ContainerResolver;
import com.google.inject.Inject;
import static java.util.Collections.singletonList;
/**
* This class handles loading of resource bundles using the servlet context from the directory WEB-INF/messages.
* <p>
* This reloads based on the setting in the {@link MVCConfiguration} interface.
*
* @author Brian Pontarelli
*/
public class WebControl extends ResourceBundle.Control {
private final MVCConfiguration configuration;
private final ContainerResolver containerResolver;
private final long reloadCheckSeconds;
@Inject
public WebControl(ContainerResolver containerResolver, MVCConfiguration configuration) {
this.configuration = configuration;
this.containerResolver = containerResolver;
this.reloadCheckSeconds = configuration.l10nReloadSeconds();
}
/**
* Always returns null because there are no fallback locales.
*
* @param uri Not used.
* @param locale Not used.
* @return Always null.
*/
@Override
public Locale getFallbackLocale(String uri, Locale locale) {
return null;
}
/**
* Only properties.
*
* @param uri Not used.
* @return An array containing only "java.properties".
*/
@Override
public List<String> getFormats(String uri) {
return singletonList("java.properties");
}
/**
* See class comment for reload information.
*
* @param baseName Not used.
* @param locale Not used.
* @return The reload check seconds multiplied times 1000.
*/
@Override
public long getTimeToLive(String baseName, Locale locale) {
return reloadCheckSeconds * 1000;
}
/**
* Returns true if the ServletContext getRealPath method returns non-null for the bundle name and the file has been
* modified since the last load.
*
* @param uri The current URI, used to construct the bundle name.
* @param locale The locale used to find the properties file.
* @param format Not used.
* @param loader Not used.
* @param bundle Not used.
* @param loadTime Not used.
* @return True if the file needs a reload.
*/
@Override
public boolean needsReload(String uri, Locale locale, String format, ClassLoader loader, ResourceBundle bundle, long loadTime) {
// Create the bundle from the WEB-INF/messages folder. basename is the uri
String name = name(uri, locale);
String realPath = containerResolver.getRealPath(name);
if (realPath != null) {
File file = new File(realPath);
long time = file.lastModified();
return time > loadTime;
}
return false;
}
/**
* First tries to load the bundle using the getRealPath method on the ServletContext. If that doesn't work because
* the application is a WAR, this uses the getResourceAsStream method on the ServletContext.
*
* @param uri The current URI.
* @param locale The locale used to find the properties file.
* @param format Not used.
* @param loader Used to lookup the resource if the ContainerResolver can't find it.
* @param reload Not used.
* @return The property resource bundle and never null.
* @throws IOException If the file couldn't be read.
* @throws IllegalArgumentException If the bundle doesn't exist.
*/
@Override
public ResourceBundle newBundle(String uri, Locale locale, String format, ClassLoader loader, boolean reload)
throws IOException {
// Create the bundle from the ${configuration.resourceDirectory}/messages folder. basename is the uri
String name = name(uri, locale);
String realPath = containerResolver.getRealPath(name);
if (realPath != null) {
File file = new File(realPath);
if (file.isFile()) {
return new PropertyResourceBundle(new FileInputStream(file));
}
}
URL url = containerResolver.getResource(name);
if (url == null) {
// Otherwise, check the classpath
url = loader.getResource(name.substring(1));
}
if (url != null) {
return new PropertyResourceBundle(new InputStreamReader(url.openStream(), "UTF-8"));
}
throw new PrimeException("Invalid bundle [" + uri + "]");
}
/**
* Makes the file name.
*
* @param uri The current URI.
* @param locale The locale.
* @return The file name.
*/
private String name(String uri, Locale locale) {
// Normaly URIs like /foo/ to /foo/index
if (uri.endsWith("/")) {
uri = uri + "index";
}
return configuration.resourceDirectory() + "/messages" + toBundleName(uri, locale) + ".properties";
}
}