Scan File System

How do I call Scan File System in Gateway Hook via code?

If you have a GatewayContext, then:

context.getProjectManager().requestScan();

In version 8.3, this code only scans projects and does not scan resources such as theme and font.

Ah, also:

context.getConfigurationManager().requestScan();
4 Likes

I tried this too, it didn't work.

You’ll have to better define what you’ve changed and what your expectations are.

I have added a few custom themes and fonts to my module. After installing the module and the initial Gateway restart, I noticed that the themes and fonts are not automatically detected. I have to manually trigger a file system scan to make them available.

Expand on this. What resources are you providing? How are you providing them? When and how do you call requestScan(). What are you doing to determine it "isn't working".

Share some code.

I can't read your mind.

1 Like

I'm adding these custom fonts and themes to Ignition 8.3 programmatically using Java code — copying them into the folders under
data/config/resources/core/com.inductiveautomation.perspective/.

After installing the module and performing the initial required restart, the new fonts and themes do not appear automatically.
I have to either restart the Gateway again or run “Scan File System” manually for them to be detected.

I also tried calling the following APIs after copying the files:

context.getProjectManager().requestScan();
context.getConfigurationManager().requestScan();

But neither of them caused the fonts or themes to be recognized automatically.

You probably should be constructing resources instead of copying files. (And I would expect no rescan required.)

1 Like

Try a test first, call the API to Get Themes Names to check whether your custom theme actually registered or not.

I ran into this issue where Ignition did not register the themes if I created the theme in the file system.

If you create the theme using the Create Themes API and then push the files for that theme using Update Themes Data File calls, it should work without having to rescan the file system or the configuration, you’ll just need to close the designer and launch it again to see the theme in effect.

At what point in your module lifecycle do you copy the files and call requestScan()?

The "scan file system" routes use the same API.

I call gatewayContext.getProjectManager().requestScan() inside the startup() method of my GatewayHook, and file-copying code inside the setup() method.

Here’s what I’m trying to do programmatically:

  1. Copy the myTheme-dark and myTheme-light folders.
  2. If the override-dark and override-light folders do not exist, create them and copy the same files there.
  3. If those folders already exist, open their index.css files and check if my custom @import lines are missing then append them.

Is it possible to perform these steps using the Ignition API instead of manually manipulating the filesystem?

Yes, absolutely. Look at the API for ConfigurationManager, including methods from the parent class:

Take a look at this thread for an extremely similar task:

1 Like

In my module, I currently use the following two utility functions:

:one: Append a CSS import line if it doesn’t already exist:

private void appendToFile(String filePath, String lineToAppend) {
    try {
        List<String> lines = Files.readAllLines(Paths.get(filePath));
        if (!lines.contains(lineToAppend)) {
            try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath, true))) {
                writer.write(lineToAppend);
                writer.newLine();
            }
        } else {
            System.out.println("Line already exists in the file.");
        }
    } catch (IOException e) {
        System.out.println("IOException: " + e.getMessage());
    }
}

:two: Copy a directory (like theme folders) from inside the JAR to Ignition’s filesystem:

private void copyDirectoryFromJar(String sourceDir, File destDir) {
    try {
        URL resourceURL = getClass().getResource(sourceDir);
        if (resourceURL != null) {
            log.error("resourceURL = " + resourceURL);
        }

        if (resourceURL == null) {
            log.error("Resource not found: " + sourceDir);
            return;
        }

        if (resourceURL.getProtocol().equals("jar")) {
            String string = getJarPath(resourceURL);
            try (JarFile jar = new JarFile(string)) {
                Enumeration<JarEntry> entries = jar.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    if (entry.getName().startsWith(sourceDir.substring(1))) {
                        String fileName = entry.getName().substring(sourceDir.length() - 1);
                        File targetFile = new File(destDir, fileName);
                        if (entry.isDirectory()) {
                            targetFile.mkdirs();
                        } else {
                            try (InputStream in = jar.getInputStream(entry);
                                 OutputStream out = new FileOutputStream(targetFile)) {
                                byte[] buffer = new byte[1024];
                                int bytesRead;
                                while ((bytesRead = in.read(buffer)) != -1) {
                                    out.write(buffer, 0, bytesRead);
                                }
                            }
                        }
                    }
                }
            }
        } else {
            log.error("Unsupported protocol: " + resourceURL.getProtocol());
        }
    } catch (IOException e) {
        throw new RuntimeException("Error copying resources from JAR", e);
    }
}

I use these two functions to automatically:

  • copy my theme folders and fonts into Ignition’s Perspective resource directories, and
  • append my custom CSS import lines into existing index.css files if they’re missing.

:red_question_mark:My question:

Now that Ignition 8.3 introduces the new ConfigurationManager API,
what is the proper or recommended way to perform these operations (copying, appending, or updating Perspective resources) using the official API, instead of manipulating the filesystem directly?

Also,
is there any way to programmatically trigger a file system scan (for example, something like requestScan()),
so that the Gateway detects newly copied or modified resource files immediately?

I’ve tested several requestScan() variants and similar methods mentioned in older posts,
but none of them seem to work in 8.3.

Since every rebuild requires reinstalling the module and restarting the Gateway,
I want to ensure I’m using the most correct and efficient approach to achieve this.

Any guidance or example code using the new API would be greatly appreciated.

Thanks!

Yes. Pushing changes via the configuration manager API will take effect instantly, with no need to scan for changes.

requestScan is that API. requestScan is literally what the button on the web interface calls. Which is what a couple people have told you already.
There is a "configuration" manager, and a "project" manager. Both are ResourceCollectionManagers. The only one relevant to Perspective themes and icons in the configuration manager.

You are doing something wrong, then, either in where you are placing your files, how your files are constructed as resources, or something else. But it's all moot if you properly use the configuration manager API directly.

Did you try the forum thread I linked above, where there's literally code written out to do exactly what you're trying to do, except for icons instead of themes?

1 Like

Thanks @ Kevin.Herron and @ paul-griffith for your help.

I realized that I made a mistake during my testing — I was running the context.getConfigurationManager().requestScan(); code inside the setup() method instead of the startup() method.

After moving it to startup(), the code worked exactly as Kevin mentioned.

Also, the Java copy and append code I shared earlier works perfectly as well.

Marking Kevin’s post as the solution since his answer was correct, and Paul’s explanation helped me understand the root cause.

Appreciate both of you for the clear guidance!