Search code examples
javamavenosgifilesystemwatcher

OSGi noClassDefFound for java.nio.files.FileSystems$DefaultFileSystemHolder


in our application, we're using ini files for the configuration of our OSGi apps. Now I thought about adding a WatchService to detect changes and reload the modified file if necessary.

I found multiple tutorials so far. Most of them are similar and result in something like the following: http://java.dzone.com/news/how-watch-file-system-changes. I adopted it like this:

36: public Ini(final File file) throws IOException {
       if (!file.exists()) {
           throw new IOException("Ini-File does not exist.");
       } else if (!file.canRead()) {
40:         throw new IOException("Ini-File is not readable.");
       }
       iniFile = file;

        FileSystem fileSystem = FileSystems.getFileSystem(file.toURI());
45:     Path test = fileSystem.getPath(file.getAbsolutePath());
        Path ini = FileSystems.getDefault().getPath(file.getParent(), file.getName());
        Logger.debug(ini.toString());
        readIni(ini);

50:     service = FileSystems.getDefault().newWatchService();
        file.getParentFile().toPath().register(service, ENTRY_MODIFY);

        Thread watcher = new Thread(() -> {
            // noinspection InfiniteLoopStatement
55:             while (true) {
                WatchKey key = null;
                try {
                    key = service.take();
                    for (WatchEvent<?> event : key.pollEvents()) {
60:                     Logger.debug("event received");
                        if (event.kind() != ENTRY_MODIFY) {
                            continue;
                        }
                        WatchEvent<Path> ev = (WatchEvent<Path>) event;
65:
                        File filename = ev.context().toFile();
                        if (filename.getAbsolutePath().equals(iniFile.getAbsolutePath())) {
                            Logger.debug(filename.getName() + " has changed");

70:                         config = new HashMap<>();
                            try {
                                readIni(filename.toPath());
                            } catch(IOException e) {
                                Logger.warning(e);
75:                         }
                        }
                    }
                } catch (Exception e) {
                    Logger.warning(e);
80:             }
                if (key != null)
                    key.reset();
            }
        });
85: 
        watcher.start();
    }

When creating a new instance, I first check whether the file exist or not. After this, I want to create a WatchService profiling this config file (at first only this one. Later I will change to 1 WatchService for all loaded files).

There are several lines (44 - 46) doing the same in different ways. None of them worked.

Now when I create an Ini file like this, felix tells me the following:

[ERROR]  : java.lang.NoClassDefFoundError: Could not initialize class java.nio.file.FileSystems$DefaultFileSystemHolder
java.lang.IllegalStateException: java.lang.NoClassDefFoundError: Could not initialize class java.nio.file.FileSystems$DefaultFileSystemHolder
    at org.apache.felix.ipojo.handlers.lifecycle.callback.LifecycleCallbackHandler.__M_stateChanged(LifecycleCallbackHandler.java:171)
    at org.apache.felix.ipojo.handlers.lifecycle.callback.LifecycleCallbackHandler.stateChanged(LifecycleCallbackHandler.java)
    at org.apache.felix.ipojo.InstanceManager.setState(InstanceManager.java:560)
    at org.apache.felix.ipojo.InstanceManager.start(InstanceManager.java:440)
    at org.apache.felix.ipojo.ComponentFactory.createInstance(ComponentFactory.java:179)
    at org.apache.felix.ipojo.IPojoFactory.createComponentInstance(IPojoFactory.java:319)
    at org.apache.felix.ipojo.IPojoFactory.createComponentInstance(IPojoFactory.java:240)
    at org.apache.felix.ipojo.extender.internal.linker.ManagedType$InstanceSupport$1.call(ManagedType.java:312)
    at org.apache.felix.ipojo.extender.internal.linker.ManagedType$InstanceSupport$1.call(ManagedType.java:306)
    at org.apache.felix.ipojo.extender.internal.queue.JobInfoCallable.call(JobInfoCallable.java:114)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NoClassDefFoundError: Could not initialize class java.nio.file.FileSystems$DefaultFileSystemHolder
    at java.nio.file.FileSystems.getDefault(FileSystems.java:176)
    at java.nio.file.spi.FileSystemProvider.installedProviders(FileSystemProvider.java:156)
    at java.nio.file.FileSystems.getFileSystem(FileSystems.java:219)
    at df.core.config.internal.Ini.<init>(Ini.java:44)
    at df.core.config.internal.ConfigImpl.__M_addConfig(ConfigImpl.java:180)
    at df.core.config.internal.ConfigImpl.addConfig(ConfigImpl.java)
    at df.core.config.api.Config$$Proxy.addConfig(Unknown Source)
    at df.playground.IniTest.__M_validate(IniTest.java:24)
    at df.playground.IniTest.validate(IniTest.java)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.apache.felix.ipojo.util.Callback.call(Callback.java:237)
    at org.apache.felix.ipojo.util.Callback.call(Callback.java:193)
    at org.apache.felix.ipojo.handlers.lifecycle.callback.LifecycleCallback.call(LifecycleCallback.java:86)
    at org.apache.felix.ipojo.handlers.lifecycle.callback.LifecycleCallbackHandler.__M_stateChanged(LifecycleCallbackHandler.java:162)
    ... 13 more

This states me to line 44 which searches for the fileSystem object for the file. Am I doing something wrong because my Code does not find the DefaultFileSystemHolder? It's a java internal class so I shouldn't need an import statement in my MANIFEST.MF, right?

It's running on Ubuntu 14.04 using Apache Felix 4.4.1 and Oracle Java 8 and build with Maven.

Thanks in advance


Solution

  • tl;dr

    Add the following configuration to Felix:

    org.osgi.framework.bootdelegation=sun.nio.*
    

    Explanation:

    The FileSystems implementation in the Oracle JDK uses an internal class sun.nio.fs.DefaultFileSystemProvider. This is a curiosity of the JDK implementation, as it expects some internal classes such as this one to be visible from any classloader. In OSGi, the only classes that are always visible to all bundles are those beginning with java., such as java.lang.String etc.

    The bootdelegation config allows us to extend the set of always-visible packages. It is intended for exactly this scenario, i.e. for JVM internal classes. Sometimes it is also useful for running code that has been instrumented, e.g. for coverage or profiling. It is NOT a general purpose setting for exposing additional API from the JDK.