How to load a resource bundle from a file resource in Java? - java

I have a file called mybundle.txt in c:/temp -
c:/temp/mybundle.txt
How do I load this file into a java.util.ResourceBundle? The file is a valid resource bundle.
This does not seem to work:
java.net.URL resourceURL = null;
String path = "c:/temp/mybundle.txt";
java.io.File fl = new java.io.File(path);
try {
resourceURL = fl.toURI().toURL();
} catch (MalformedURLException e) {
}
URLClassLoader urlLoader = new URLClassLoader(new java.net.URL[]{resourceURL});
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle( path ,
java.util.Locale.getDefault(), urlLoader );

As long as you name your resource bundle files correctly (with a .properties extension), then this works:
File file = new File("C:\\temp");
URL[] urls = {file.toURI().toURL()};
ClassLoader loader = new URLClassLoader(urls);
ResourceBundle rb = ResourceBundle.getBundle("myResource", Locale.getDefault(), loader);
where "c:\temp" is the external folder (NOT on the classpath) holding the property files, and "myResource" relates to myResource.properties, myResource_fr_FR.properties, etc.
Credit to http://www.coderanch.com/t/432762/java/java/absolute-path-bundle-file

When you say it's "a valid resource bundle" - is it a property resource bundle? If so, the simplest way of loading it probably:
try (FileInputStream fis = new FileInputStream("c:/temp/mybundle.txt")) {
return new PropertyResourceBundle(fis);
}

1) Change the extension to properties (ex. mybundle.properties.)
2) Put your file into a jar and add it to your classpath.
3) Access the properties using this code:
ResourceBundle rb = ResourceBundle.getBundle("mybundle");
String propertyValue = rb.getString("key");

From the JavaDocs for ResourceBundle.getBundle(String baseName):
baseName - the base name of the
resource bundle, a fully qualified
class name
What this means in plain English is that the resource bundle must be on the classpath and that baseName should be the package containing the bundle plus the bundle name, mybundle in your case.
Leave off the extension and any locale that forms part of the bundle name, the JVM will sort that for you according to default locale - see the docs on java.util.ResourceBundle for more info.

For JSF Application
To get resource bundle prop files from a given file path to use them in a JSF app.
Set the bundle with URLClassLoader for a class that extends
ResourceBundle to load the bundle from the file path.
Specify the class at basename property of loadBundle tag.
<f:loadBundle basename="Message" var="msg" />
For basic implementation of extended RB please see the sample at Sample Customized Resource Bundle
/* Create this class to make it base class for Loading Bundle for JSF apps */
public class Message extends ResourceBundle {
public Messages (){
File file = new File("D:\\properties\\i18n");
ClassLoader loader=null;
try {
URL[] urls = {file.toURI().toURL()};
loader = new URLClassLoader(urls);
ResourceBundle bundle = getBundle("message", FacesContext.getCurrentInstance().getViewRoot().getLocale(), loader);
setParent(bundle);
} catch (MalformedURLException ex) { }
}
.
.
.
}
Otherwise, get the bundle from getBundle method but locale from others source like Locale.getDefault(), the new (RB)class may not require in this case.

If, like me, you actually wanted to load .properties files from your filesystem instead of the classpath, but otherwise keep all the smarts related to lookup, then do the following:
Create a subclass of java.util.ResourceBundle.Control
Override the newBundle() method
In this silly example, I assume you have a folder at C:\temp which contains a flat list of ".properties" files:
public class MyControl extends Control {
#Override
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
if (!format.equals("java.properties")) {
return null;
}
String bundleName = toBundleName(baseName, locale);
ResourceBundle bundle = null;
// A simple loading approach which ditches the package
// NOTE! This will require all your resource bundles to be uniquely named!
int lastPeriod = bundleName.lastIndexOf('.');
if (lastPeriod != -1) {
bundleName = bundleName.substring(lastPeriod + 1);
}
InputStreamReader reader = null;
FileInputStream fis = null;
try {
File file = new File("C:\\temp\\mybundles", bundleName);
if (file.isFile()) { // Also checks for existance
fis = new FileInputStream(file);
reader = new InputStreamReader(fis, Charset.forName("UTF-8"));
bundle = new PropertyResourceBundle(reader);
}
} finally {
IOUtils.closeQuietly(reader);
IOUtils.closeQuietly(fis);
}
return bundle;
}
}
Note also that this supports UTF-8, which I believe isn't supported by default otherwise.

This worked for me very well. And it doesn't reload the bundle everytime. I tried to take some stats to load and reload the bundle from external file location.
File file = new File("C:\\temp");
URL[] urls = {file.toURI().toURL()};
ClassLoader loader = new URLClassLoader(urls);
ResourceBundle rb = ResourceBundle.getBundle("myResource", Locale.getDefault(), loader);
where "c:\temp" is the external folder (NOT on the classpath) holding the property files, and "myResource" relates to myResource.properties, myResource_fr_FR.properties, etc.
Note: If you have the same bundle name on your classpath then it will be picked up by default using this constructor of URLClassLoader.
Credit to http://www.coderanch.com/t/432762/java/java/absolute-path-bundle-file
Find some of the stats below, all time in ms.
I am not worried about the initial load time as that could be something with my workspace or code that I am trying to figure out but what I am trying to show is the reload took way lesser telling me its coming from memory.
Here some of the stats:
Initial Locale_1 load took 3486
Reload Locale_1 took 24
Reload Locale_1 took 23
Reload Locale_1 took 22
Reload Locale_1 took 15
Initial Locale_2 load took 870
Reload Locale_2 took 22
Reload Locale_2 took 18
Initial Locale_3 load took 2298
Reload Locale_3 took 8
Reload Locale_3 took 4

I would prefer to use the resourceboundle class to load the properties - just to get it done in one line instead of 5 lines code through stream, Properties class and load().
FYI ....
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
try {
/*** Type1 */
Properties props = new Properties();
String fileName = getServletContext().getRealPath("WEB-INF/classes/com/test/my.properties");
// stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
// stream = ClassLoader.getSystemResourceAsStream("WEB-INF/class/com/test/my.properties");
InputStream stream = getServletContext().getResourceAsStream("/WEB-INF/classes/com/test/my.properties");
// props.load(new FileInputStream(fileName));
props.load(stream);
stream.close();
Iterator keyIterator = props.keySet().iterator();
while(keyIterator.hasNext()) {
String key = (String) keyIterator.next();
String value = (String) props.getProperty(key);
System.out.println("key:" + key + " value: " + value);
}
/*** Type2: */
// Just get it done in one line by rb instead of 5 lines to load the properties
// WEB-INF/classes/com/test/my.properties file
// ResourceBundle rb = ResourceBundle.getBundle("com.test.my", Locale.ENGLISH, getClass().getClassLoader());
ResourceBundle rb = ResourceBundle.getBundle("com.ibm.multitool.customerlogs.ui.nl.redirect");
Enumeration<String> keys = rb.getKeys();
while(keys.hasMoreElements()) {
String key = keys.nextElement();
System.out.println(key + " - " + rb.getObject(key));
}
} catch (IOException e) {
e.printStackTrace();
throw new ServletException("Error loading config.", e);
} catch (Exception e) {
e.printStackTrace();
throw new ServletException("Error loading config.", e);
}
}

I think that you want the file's parent to be on the classpath, not the actual file itself.
Try this (may need some tweaking):
String path = "c:/temp/mybundle.txt";
java.io.File fl = new java.io.File(path);
try {
resourceURL = fl.getParentFile().toURL();
} catch (MalformedURLException e) {
e.printStackTrace();
}
URLClassLoader urlLoader = new URLClassLoader(new java.net.URL[]{resourceURL});
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("mybundle.txt",
java.util.Locale.getDefault(), urlLoader );

The file name should have .properties extension and the base directory should be in classpath. Otherwise it can also be in a jar which is in classpath
Relative to the directory in classpath the resource bundle can be specified with / or . separator. "." is preferred.

If you wanted to load message files for different languages, just use the
shared.loader=
of catalina.properties...
for more info, visit http://theswarmintelligence.blogspot.com/2012/08/use-resource-bundle-messages-files-out.html

This works for me:
File f = new File("some.properties");
Properties props = new Properties();
FileInputStream fis = null;
try {
fis = new FileInputStream(f);
props.load(fis);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
fis = null;
} catch (IOException e2) {
e2.printStackTrace();
}
}
}

public class One {
private static One one = null;
Map<String, String> configParameter = Collections.synchronizedMap(new HashMap<String, String>());
private One() {
ResourceBundle rb = ResourceBundle.getBundle("System", Locale.getDefault());
Enumeration en = rb.getKeys();
while (en.hasMoreElements()) {
String key = (String) en.nextElement();
String value = rb.getString(key);
configParameter.put(key, value);
}
}
public static One getInstance() {
if (one == null) {
one= new One();
}
return one;
}
public Map<String, String> getParameter() {
return configParameter;
}
public static void main(String[] args) {
String string = One.getInstance().getParameter().get("subin");
System.out.println(string);
}
}

ResourceBundle rb = ResourceBundle.getBundle("service"); //service.properties
System.out.println(rb.getString("server.dns")); //server.dns=http://....

Related

Relative path to image folder java spring

I have a problem with a relative path to an image folder.
I want to list the images into a folder to add their urls to a list and show them in a jsp.
The code is this:
File carpetaImagenes = new File("../../../../../webapp/resources/img/maquinas/"+seleccion);
List<String> listaUrlImagenes = new ArrayList<String>();
/** Recorremos el directorio de imagenes de la maquina */
for(File imagen : carpetaImagenes.listFiles()){
String imageFileName = imagen.getName();
listaUrlImagenes.add(imageFileName);
}
The result of "carpetaImagenes.listFiles()" is always null. I suppose the path is bad.
Here you can see the image of the folder tree. The class is into "controlador" folder and the images are into "webbapp/resources/img/maquinas/1"
I haeve tried several paths with no luck.
Thank you very much.
User following method getImageList and it should work
relativeFilePath = "img/maquinas/"+seleccion;
This is a relative path from classpath. I assume ../../../../../webapp/resources is your actual folder where everyrthing is deployed .
private List getImageList(String relativeFilePath) {
List<String> listaUrlImagenes = new ArrayList<String>();
try {
InputStream in = getResourceAsStream(relativeFilePath);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String resource;
while ((resource = br.readLine()) != null) {
listaUrlImagenes.add(resource);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return listaUrlImagenes;
}
private InputStream getResourceAsStream(String resource) {
final InputStream in = ClassLoader cl = this.getClass().getClassLoader().getResourceAsStream(
resource);
return in == null ? getClass().getResourceAsStream(resource) : in;
}
And in spring following should work.
ClassLoader cl = this.getClass().getClassLoader();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
cl);
Resource[] resources = resolver.getResources("classpath:/img/maquinas/"+seleccion);// or *.png
for (Resource resource : resources) {
listaUrlImagenes.add(resource.getFilename());
}
Finally working!!
File carpetaImagenes = new ClassPathResource("imagenes/maquinas/"+seleccion).getFile();
File[] listaImagenes = carpetaImagenes.listFiles();

Loading classes from a set of files

I have a task to check a set of conditions for unknown set of classes from classpath. I want to scan it for classes, load each of them and perform my checks. Now I have a set of urls to class files and I try to use URLClassLoader. But to load a class I need to specify a fully qualified class name, but I don't have them (I have only file path). I don't think that building class name from class file path is relieble, is it a better way to do it?
Thanks!
I'd just parse the beginning of the class file, looking for "package" keyword and first occurrence of "class" keyword. Then, when you combine those two (packageName + "." + className), it should result in a proper class name.
I started a project once to automatically test classes found on the class path for run time exceptions, by calling constructors and methods reflectively with dodgy arguments like null, 0, 1, -1, "" etc.
That project has a class called Finder wich does roughly what you need:
static List<Class<?>> findClassesForPackage(String packagename, Report report) throws ClassNotFoundException {
// 'classes' will hold a list of directories matching the package name.
// There may be more than one if a package is split over multiple
// jars/paths
List<Class<?>> classes = new ArrayList<Class<?>>();
List<File> directories = new ArrayList<File>();
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
throw new ClassNotFoundException("Can't get class loader.");
}
// Ask for all resources for the path
String path = packagename.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
while (resources.hasMoreElements()) {
URL res = resources.nextElement();
if (res.getProtocol().equalsIgnoreCase("jar")) {
JarURLConnection conn = (JarURLConnection) res.openConnection();
JarFile jar = conn.getJarFile();
for (JarEntry entry : Collections.list(jar.entries())) {
if (entry.getName().startsWith(path) && entry.getName().endsWith(".class")
&& !entry.getName().contains("$")) {
String className = entry.getName().replace("/", ".").substring(0,
entry.getName().length() - 6);
LOG.debug("Adding JAR className " + className);
try {
Class<?> clazz = Class.forName(className);
classes.add(clazz);
report.addClass(className);
} catch (Throwable throwable) {
ParamSet params = new ParamSet();
params.addParamValue(new ParamValue(className, "fully qualified classname"));
report.addError(className, new Error("Class.forName()", params, throwable));
}
}
}
} else
directories.add(new File(URLDecoder.decode(res.getPath(), "UTF-8")));
}
} catch (NullPointerException e) {
throw new ClassNotFoundException(String.format("%s does not appear to be a valid package", packagename), e);
} catch (UnsupportedEncodingException e) {
throw new ClassNotFoundException(String.format("%s does not appear to be a valid package", packagename), e);
} catch (IOException e) {
throw new ClassNotFoundException(String.format("Could not get all resources for %s", packagename), e);
}
List<String> subPackages = new ArrayList<String>();
// For every directory identified capture all the .class files
for (File directory : directories) {
if (directory.exists()) {
// Get the list of the files contained in the package
File[] files = directory.listFiles();
for (File file : files) {
// add .class files to results
String fileName = file.getName();
if (file.isFile() && fileName.endsWith(".class")) {
// removes the .class extension
String className = packagename + '.' + fileName.substring(0, fileName.length() - 6);
LOG.debug("Adding FILE className " + className);
try {
Class<?> clazz = Class.forName(className);
classes.add(clazz);
report.addClass(className);
} catch (Throwable throwable) {
ParamSet params = new ParamSet();
params.addParamValue(new ParamValue(className, "fully qualified classname"));
report.addError(className, new Error("Class.forName()", params, throwable));
}
}
// keep track of subdirectories
if (file.isDirectory()) {
subPackages.add(packagename + "." + fileName);
}
}
} else {
throw new ClassNotFoundException(String.format("%s (%s) does not appear to be a valid package",
packagename, directory.getPath()));
}
}
// check all potential subpackages
for (String subPackage : subPackages) {
classes.addAll(findClassesForPackage(subPackage, report));
}
return classes;
}
You probably have to strip some code that does reporting etc.

Class loading class from jar

This is the common approach with working with loading class dynamically:
try {
File file = new File(JAR_FILE);
String classToLoad = "com.mycompany.MyClass";
URL jarUrl = new URL("jar", "","file:" + file.getAbsolutePath()+"!/");
URLClassLoader loader = new URLClassLoader(new URL[] {jarUrl}, Thread.currentThread().getContextClassLoader());
Class c = loader.loadClass(classToLoad);
} catch (Exception e) {
e.printStackTrace();
}
However, I need an approach where:
We don't need to create a File (as the jar I am trying to process is a byte array[] when fetched)
Or we won't need to create a temporary file from byte[] array (as AppEngine, the platform I work with does not allow to create temporary files)
You will have to create your own class loader.
Something like this, basic idea in pseudo code:
class MyClassLoader extends ClassLoader {
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
JarInputStream jis = new JarInputStream(new ByteArrayInputStream(bytearrayJarData));
JarEntry entry = jis.getNextJarEntry();
while (entry != null) {
//compare entry to requested class
// if match, return Byte data
// else entry = jis.getNextJarEntry();
}
return null; // nothing found
}
}
Write your own ClassLoader and override findClass(). There you can use defineClass() to load your byte[].

How to clear ResourceBundle cache

This is a webapp running on Tomcat, using Guice. According to the docs we should be able to call ResourceBundle.clearCache(); to clear the ResourceBundle cache and presumably get the latest from the bundle property files.
We have also tried the following:
Class klass = ResourceBundle.getBundle("my.bundle").getClass().getSuperclass();
Field field = klass.getDeclaredField("cacheList");
field.setAccessible(true);
ConcurrentHashMap cache = (ConcurrentHashMap) field.get(null);
cache.clear(); // If i debug here I can see the cache is now empty!
and
ResourceBundle.clearCache(this.class.getClassLoader());
The behavior that I am expecting is:
Start up tomcat and hit a page and it says 'Hello World'
Change the properties file containing 'Hello World' to 'Goodbye Earth'
Clear the cache using a servlet
Hit the page and expect to see 'Goodbye Earth'
So question is, how is ResourceBundle.clearCache() actually working ? And is there some generic file cache we need to clear also ?
This worked for me:
ResourceBundle.clearCache();
ResourceBundle resourceBundle= ResourceBundle.getBundle("YourBundlePropertiesFile");
String value = resourceBundle.getString("your_resource_bundle_key");
Notes:
ResourceBundle.clearCache() is added at Java 1.6
Do not use a static resourceBundle property, use
ResourceBundle.getBundle() method after invoking clearCache()
method.
I do not believe you can effect the reloading of an already created ResourceBundle instance since its internal control class has already been created. You may try this as an alternative for initializing your bundle:
ResourceBundle.getBundle("my.bundle", new ResourceBundle.Control() {
#Override
public long getTimeToLive(String arg0, Locale arg1) {
return TTL_DONT_CACHE;
}
});
I have found this solution (works with tomcat):
use a custom ResourceBundle.Control (because I need UTF8)
add the getTimeToLive as "Perception" description
force the reload flag
the "ResourceBundle.clearCache" doesn't work
How to call :
ResourceBundle bundle = ResourceBundle.getBundle("yourfile", new UTF8Control());
The custom class :
public class UTF8Control extends Control
{
public ResourceBundle newBundle(
String baseName,
Locale locale,
String format,
ClassLoader loader,
boolean reload)
throws IllegalAccessException, InstantiationException, IOException
{
// The below is a copy of the default implementation.
String bundleName = toBundleName(baseName, locale);
String resourceName = toResourceName(bundleName, "properties");
ResourceBundle bundle = null;
InputStream stream = null;
// FORCE RELOAD because needsReload doesn't work and reload is always false
reload = true;
if (reload) {
URL url = loader.getResource(resourceName);
if (url != null) {
URLConnection connection = url.openConnection();
if (connection != null) {
connection.setUseCaches(false);
stream = connection.getInputStream();
}
}
}
else {
stream = loader.getResourceAsStream(resourceName);
}
if (stream != null) {
try {
// Only this line is changed to make it to read properties files as UTF-8.
bundle = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"));
}
finally {
stream.close();
}
}
return bundle;
}
// ASK NOT TO CACHE
public long getTimeToLive(String arg0, Locale arg1) {
return TTL_DONT_CACHE;
}
}
maybe this post can solve your problem.
this is one more possibility to clear cache
Class<ResourceBundle> type = ResourceBundle.class;
try {
Field cacheList = type.getDeclaredField("cacheList");
cacheList.setAccessible(true);
((Map<?, ?>) cacheList.get(ResourceBundle.class)).clear();
}
catch (Exception e) {
system.out.print("Failed to clear ResourceBundle cache" + e);
}
This works, iff you can intercept the very first creation of the resource bundle:
while (true) {
ResourceBundle resourceBundle = ResourceBundle.getBundle("SystemMessages", new Locale("hu", "HU"),
new ResourceBundle.Control() {
#Override
public List<String> getFormats(String baseName) {
return ResourceBundle.Control.FORMAT_PROPERTIES;
}
#Override
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException {
System.err.println(this.toBundleName(baseName, locale) + ": " + format + " - " + reload);
return super.newBundle(baseName, locale, format, loader, reload);
}
#Override
public long getTimeToLive(String baseName, Locale locale) {
long ttl = 1000;
System.err.println(this.toBundleName(baseName, locale) + " - " + ttl + "ms");
return ttl;
}
#Override
public boolean needsReload(String baseName, Locale locale, String format, ClassLoader loader, ResourceBundle bundle, long loadTime) {
System.err.println(baseName + "_" + locale + " - " + new Date(loadTime));
return true;
}
});
System.out.println(resourceBundle.getString("display.first_name") + ": John");
System.out.println(resourceBundle.getString("display.last_name") + ": Doe");
Thread.sleep(5000);
}

Custom class loader for loading dll in applet

To address the .dll file loading/unloading issue with applets. I am loading, following this tutorial, .dll files using a custom class loader as :
1- Class Loader (Copied from tutorial)
public class CustomClassLoader extends ClassLoader {
private static final String CLASS_NAME = CustomClassLoader.class.getName();
private Map<String, Class<?>> classes;
public CustomClassLoader() {
super(CustomClassLoader.class.getClassLoader());
classes = new HashMap<String, Class<?>>();
}
public String toString() {
return CustomClassLoader.class.getName();
}
#Override
public Class<?> findClass(String name) throws ClassNotFoundException {
if (classes.containsKey(name)) {
return classes.get(name);
}
String path = name.replace('.', File.separatorChar) + ".class";
byte[] b = null;
try {
b = loadClassData(path);
} catch (IOException e) {
throw new ClassNotFoundException("Class not found at path: " + new File(name).getAbsolutePath(), e); **
}
Class<?> c = defineClass(name, b, 0, b.length);
resolveClass(c);
classes.put(name, c);
return c;
}
private byte[] loadClassData(String name) throws IOException {
String methodName = CLASS_NAME + ".loadClassData";
byte buff[] = null;
try {
File file = new File(name);
int size = (int) file.length();
buff = new byte[size];
DataInputStream in = new DataInputStream(new FileInputStream(name));
in.readFully(buff);
in.close();
} catch (IOException e) {
throw new IOException(e.getMessage());
}
return buff;
}
}
2- A class to load .dll files
public class DllLoader {
private static final String CLASS_NAME = AppletReload.class.getName();
static String javaHome = System.getProperty("java.io.tmpdir");
private static String dllPath = javaHome + Constant.SMARTCARD_JACSPCSC_DLL_NAME;
private static String dllPath1 = javaHome + Constant.SMARTCARD_RXTXSERIAL_DLL_NAME;
public DllLoader() {
}
static {
try {
System.load(dllPath);
Logger.write(LoggerConstant.TRACE, "JACSPCSC Dll loaded from the path: " + dllPath, "Dll Loader");
System.load(dllPath1);
Logger.write(LoggerConstant.TRACE, "RXTXSERIAL Dll loaded from the path: " + dllPath1, "Dll Loader");
} catch (Exception e) {
// Log exception;
}
}
}
And this is how I am using this class loader:
cl = new CustomClassLoader();
ca = cl.findClass("com.DllLoader");
a = ca.newInstance();
The motivation behind loading .dll using custom class loader is that it would guarantee the unloading as well and this is what most of the answers to question on .dll loading/unloading suggest. But in my case, loadClassData() (a function in my classLoader) thows this exception:
loadClassData,Error: com\DllLoader.class (The system cannot find the path specified)
java.io.FileNotFoundException: com\DllLoader.class (The system cannot find the path specified)
and the absolute path of file is show logged as:
**C:\Program Files\Mozilla Firefox\com.DllLoader
I think this is where its searching for the file, and .jar file of applet isn't located here.
I would appreciate if anyone can point out mistake I am making or tell how can I guide browser to look for class file in correct folder.
P.S: Kindly note that a answer to seemingly duplicate question don't solve this problem.

Categories