Using reflections with onejar plugin - java

I am using reflections with onejar maven plugin, and I have a problem when running the resulting jar:
20204 [main] ERROR org.reflections.Reflections - given scan urls are
empty. set urls in the configuration
It seems to me that reflections doesn't support the class loader that onejar is using. I've been using the following configuration of Reflections in my code:
Reflections reflections = new Reflections("path.to.my.package");
Any ideas? Thanks.

Yep a known issue. There is a work around...
Use Custom UrlType and custom Vfs.Dir; Uses OneJarURLConnection to correctly read content from embedded JARs; uses 'standard' Vfs.File.
Issue and solution discussed here : http://code.google.com/p/reflections/issues/detail?id=128
Source if you need it:
OneJarDir
package com.sdl.ws.integration.profserv.shared.onejar;
import com.simontuffs.onejar.OneJarURLConnection;
import org.reflections.vfs.Vfs;
import org.reflections.vfs.ZipDir;
import org.reflections.vfs.ZipFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class OneJarDir implements Vfs.Dir {
private JarFile oneJarFile = null;
private List<Vfs.File> oneJarClassFiles = new ArrayList<Vfs.File>();
private OneJarURLConnection oneJarConnection;
public OneJarDir(OneJarURLConnection oneJarConnection) {
this.oneJarConnection = oneJarConnection;
try {
this.oneJarConnection.connect();
this.oneJarFile = this.oneJarConnection.getJarFile();
Enumeration<JarEntry> entries = oneJarFile.entries();
while (entries.hasMoreElements()) {
oneJarClassFiles.add(new ZipFile(new ZipDir(oneJarFile), entries.nextElement()));
}
} catch (IOException e) {
throw new RuntimeException("Can't create One-Jar VFS directory", e);
}
}
public String getPath() {
return oneJarConnection.getURL().getPath();
}
public Iterable<Vfs.File> getFiles() {
return oneJarClassFiles;
}
public void close() {
try {
if (oneJarConnection != null)
oneJarConnection.getInputStream().close();
} catch (IOException e) {
throw new RuntimeException("Can't close VFS JAR stream", e);
}
}
}
OneJarUrlType
package com.sdl.ws.integration.profserv.shared.onejar;
import com.simontuffs.onejar.OneJarURLConnection;
import org.reflections.vfs.Vfs;
import java.io.IOException;
import java.net.URL;
public class OneJarUrlType implements Vfs.UrlType {
private static final String _JAR_DIR = "jar!";
public boolean matches(URL url) {
// check if "double-jarred' by one-jar; this would appear to conflict with the standard JAR loader, so it either needs to be first (which it is)
// OR the standard needs to be removed. This match assumes a nested JAR, unlike the standard JAR type.
String externalForm = url.toExternalForm();
// ugly, but should be much faster than regex.
int idx1 = externalForm.indexOf(_JAR_DIR);
return (idx1 > 0 && externalForm.indexOf(_JAR_DIR, idx1 + _JAR_DIR.length()) > 0);
}
public Vfs.Dir createDir(URL url) {
try {
return new OneJarDir(new OneJarURLConnection(url));
} catch (IOException e) {
throw new RuntimeException("Can't open One-Jar embedded JAR", e);
}
}
}

Related

Using AccessController to limit permissions

I'm trying to prevent plugins that run "inside" the main Java application from accessing things they shouldn't. I've read about Policies, and AccessControllers, and ProtectionDomains, but they're very oriented around JARs.
I've tried this:
import java.nio.file.Files
import java.nio.file.Paths
import java.security.*
fun main(args: Array<String>) {
Policy.setPolicy(object : Policy() {})
System.setSecurityManager(SecurityManager())
val domain = ProtectionDomain(null, Permissions() /* no permissions */)
AccessController.doPrivileged(PrivilegedAction {
untrusted()
}, AccessControlContext(arrayOf(domain)))
}
fun untrusted() {
try {
// Works as expected
Files.readAllBytes(Paths.get("build.gradle"))
throw IllegalStateException("Was able to access file, but shouldn't have been able to")
} catch (e: AccessControlException) {
}
try {
// Should throw AccessControlException, but doesn't
AccessController.doPrivileged(PrivilegedAction {
Files.readAllBytes(Paths.get("build.gradle"))
})
throw IllegalStateException("Was able to access file, but shouldn't have been able to")
} catch (e: AccessControlException) {
}
}
Even though I'm invoking untrusted() via a custom limited ProtectionDomain, it seems it can trivially break out of it. I'm expecting the the doPrivileged call in untrusted to operate with the intersection of the permissions of the outermost ProtectionDomain (the main program, which has all permissions) and the caller's ProtectionDomain (which has no permissions), resulting in untrusted having essentially 0 permissions.
I've also tried with the domain set like this:
val domain = ProtectionDomain(CodeSource(URL("http://foo"), null as Array<CodeSigner>?), Permissions() /* no permissions */)
but this also doesn't work -- the Policy is queried with the main program's ProtectionDomain and not the one calling untrusted(). (Obviously I'd need to update the Policy to handle "http://foo" correctly, but it doesn't even check that ProtectionDomain anyway)
So where has my understanding gone wrong?
After doing some research on this, I think I have an answer. I could write a significantly longer answer, but I think I'll just cut to the chase here.
Each class loaded by a ClassLoader has a ProtectionDomain+CodeSource associated with it. These are somewhat coarse -- a CodeSource represents where a class came from, but it's not a pointer to an individual .class file or anything -- it's to a directory or a JAR. Thus two classes in the same JAR or directory will generally have identical permissions. Any class or script that has an identifiable ProtectionDomain+CodeSource can be whitelisted/blacklisted by your Policy.
The exception (kinda) to this is, of course, is AccessController.doPrivileged with Permission arguments. This lets you clamp down the permissions of a region of code. But that code could, in theory, call AccessController.doPrivileged with just the callback. That method signature means "don't check my entire call stack for permissions; just look up my ProtectionDomain+CodeSource in the Policy file and see what it says." So if you're running truly untrusted code, you better make sure that a. it has a ProtectionDomain+CodeSource different from your trusted application, and b. that your Policy is able to identify that code and grant it appropriately-limited permissions.
Here is one way for the example to run as intended, i.e., to effectively blacklist subsequent execution paths under the same domain. The core permission-intersection-based authorization model should still hold. The sample must be run with -Djava.system.class.loader=com.example.Test$AppClassLoader (this replacement system class loader is only needed in order to attain a working single-file example).
Obligatory disclaimer: While technically many things are possible, to the point of dynamically white-/blacklisting individual instances and beyond, they all involve additional context of some sort being introduced into the already non-trivial authorization process. Such approaches should be avoided whenever possible. The proper solution, sufficing in the vast majority of cases, as documented in the OP's answer's conclusion, is to package trusted code separately from untrusted (and, when manually managing class-to-domain mappings, ensuring that code bases of distinct trustworthiness are mapped to distinct domains), and assign appropriate permissions to the resulting domains.
package com.example;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test {
public static final class AppClassLoader extends URLClassLoader {
private static final URL[] CLASS_PATH;
private static final String SANDBOXABLE_DOMAIN_CLASS_NAME = "com.example.Test$SandboxableDomain";
static {
String[] paths = System.getProperty("java.class.path").split(File.pathSeparator);
List<URL> classPath = new ArrayList<>();
for (String path : paths) {
try {
classPath.add(new URL("file://" + path));
}
catch (MalformedURLException ex) {}
}
CLASS_PATH = classPath.toArray(new URL[0]);
}
private final Constructor<?> sandboxableDomainCtor;
{
try {
// ensure this loader defines SandboxableDomain so that normal code
// can safely / conveniently access it via class literal
Class<?> sandboxableDomainClass = loadClass(SANDBOXABLE_DOMAIN_CLASS_NAME, true);
sandboxableDomainCtor = sandboxableDomainClass.getConstructor(CodeSource.class,
PermissionCollection.class, ClassLoader.class);
}
catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
public AppClassLoader(ClassLoader parent) {
super(CLASS_PATH, parent);
}
#Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.startsWith("java") || name.startsWith("sun")) {
return super.loadClass(name, resolve);
}
Class<?> ret = findLoadedClass(name);
if (ret != null) {
return ret;
}
ProtectionDomain assignedDomain;
byte[] classData;
try {
URL classResource = getResource(name.replace(".", "/") + ".class");
CodeSource assignedCodeSource = new CodeSource(classResource, (Certificate[]) null);
classData = Files.readAllBytes(Paths.get(classResource.toURI()));
if (SANDBOXABLE_DOMAIN_CLASS_NAME.equals(name)) {
// loading the domain class itself; ensure _its own_ domain is fully privileged,
// so that it doesn't affect authorization
PermissionCollection perms = new Permissions();
perms.add(new AllPermission());
assignedDomain = new ProtectionDomain(assignedCodeSource, perms, this, null);
}
else {
// the per-class code source (URL) is unintentional; normally all classes under
// the same class path entry would share one
assignedDomain = (ProtectionDomain) sandboxableDomainCtor.newInstance(assignedCodeSource,
getPermissions(assignedCodeSource), this);
}
}
catch (NullPointerException | URISyntaxException | IOException | ReflectiveOperationException ex) {
throw new ClassNotFoundException(name);
}
ret = defineClass(name, classData, 0, classData.length, assignedDomain);
if (resolve) {
resolveClass(ret);
}
return ret;
}
}
public static final class SandboxableDomain extends ProtectionDomain {
private static final Permission DO_SANDBOXED_PERM = new RuntimePermission("com.example.doSandboxed");
private final ThreadLocal<Boolean> sandboxed = new InheritableThreadLocal<>();
public SandboxableDomain(CodeSource cs, PermissionCollection permissions, ClassLoader classLoader) {
super(cs, permissions, classLoader, null);
sandboxed.set(false);
}
// no equivalent doUnsandboxed here for escaping the sandbox on-demand;
// firstly because it's fishy; secondly because it would be impossible
// to distinguish a privileged caller based on permissions alone
public void doSandboxed(Runnable action) {
if (!sandboxed.get()) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(DO_SANDBOXED_PERM);
}
}
sandboxed.set(true);
try {
action.run();
}
finally {
sandboxed.set(false);
}
}
#Override
public boolean implies(Permission permission) {
if (sandboxed.get()) {
// static only (AppClassLoader only grants essentials like reading from own directory)
PermissionCollection perms = getPermissions();
return (perms == null) ? false : perms.implies(permission);
}
// static + policy
return super.implies(permission);
}
}
public static void main(String[] args) throws Exception {
initSecurity();
SandboxableDomain ownDomain = (SandboxableDomain) Test.class.getProtectionDomain();
System.out.println("Try unsandboxed"); // should succeed
untrusted();
System.out.println("---\n\nTry sandboxed"); // should fail
ownDomain.doSandboxed(Test::untrusted);
System.out.println("---\n\nTry unsandboxed from within a child thread"); // should succeed
new Thread(Test::untrusted).start();
Thread.sleep(1000);
System.out.println("---\n\nTry unsandboxed from within a sandboxed child thread"); // should fail
ownDomain.doSandboxed(() -> new Thread(Test::untrusted).start());
}
private static void initSecurity() throws Exception {
Path tempPolicyConfig = Files.createTempFile(null, null);
// self-grant AllPermission
Files.write(tempPolicyConfig,
Collections.singletonList(new StringBuilder("grant codebase \"")
.append(Test.class.getProtectionDomain().getCodeSource().getLocation()).append("\"{permission ")
.append(AllPermission.class.getName()).append(";};").toString()));
System.setProperty("java.security.policy", "=" + tempPolicyConfig.toString());
System.setSecurityManager(new SecurityManager());
Files.delete(tempPolicyConfig);
}
private static void untrusted() {
try {
untrusted0();
System.out.println("\tSucceeded");
}
catch (AccessControlException ex) {
System.out.println("\tFailed; try via doPrivileged");
try {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
untrusted0();
return null;
});
System.out.println("\t\tSucceeded");
}
catch (AccessControlException ex1) {
System.out.println("\t\tFailed anew");
}
}
}
private static void untrusted0() {
try {
Files.readAllBytes(Paths.get("build.gradle"));
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}

Java, how could we log and store it into local file, a stateless session bean?

I am trying to log the program's execution flow to better understand how servlets, EJBs and JSPs work together.
Currently the difficulty I am facing is to output the log to a local file.
I have first tried with the Java logger API, studying this example:
Using Java log API:
http://wiki4.caucho.com/Java_EE_Servlet/JSP_tutorial_:_Adding_an_error_page,_logging,_and_other_forms_of_debugging#Using_Java_log_API
And I have written:
package beans;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Stateless;
#Stateless
public class ComentarioNota {
private static final Logger log = Logger.getLogger(ComentarioNota.class.getName());
public String convierteComentarioNota(String evaluacion, String comentario) {
log.logp(Level.WARNING,
this.getClass().getName(),
this.getClass().getName(), this.getClass().getName() + "::convierteComentarioNota::el usuario introdujo: " + evaluacion + comentario);
if (evaluacion.trim().equals("Apto") && comentario != null && comentario.length() > 5) {
return "Apto";
} else {
return "No Apto";
}
}
And it indeed outputs the log with ClassName::ClassMethod::User input info.
However it outputs the log info to the console, I need it in a local file, how could we log into a local file?
I have tried to use the PrintWriter and creating a new file with its contents:
package beans;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Stateless;
#Stateless
public class ComentarioNota {
private static final Logger log = Logger.getLogger(ComentarioNota.class.getName());
public String convierteComentarioNota(String evaluacion, String comentario) {
try (PrintWriter out = new PrintWriter("log.txt")) {
out.println(this.getClass().getName() + "::convierteComentarioNota::el usuario introdujo: " + evaluacion + comentario);
if (evaluacion.trim().equals("Apto") && comentario != null && comentario.length() > 5) {
return "Apto";
} else {
return "No Apto";
}
} catch (FileNotFoundException ex) {
Logger.getLogger(ComentarioNota.class.getName()).log(Level.SEVERE, null, ex);
}
return "No Apto";
}
}
However this does not create a new file.
How could we create that new local file and send the log output to it?
Thank you for your help!.
I have also read:
How do I save a String to a text file using Java?
Where does the ServletContext.log messages go in tomcat 7?
EDIT: I have read the comment's tutorial: http://tutorials.jenkov.com/java-logging/handlers.html#streamhandler
ANd I have found that we can add handlers to the logger, and there is a built in handler which is FileHandler which is supposed to create one file with the log's contents. I have tried the following:
package beans;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Stateless;
#Stateless
public class ComentarioNota {
private static final Logger log = Logger.getLogger(ComentarioNota.class.getName());
public String convierteComentarioNota(String evaluacion, String comentario) {
try {
FileHandler handler = new FileHandler("comentarioNota.txt");
log.addHandler(handler);
log.logp(Level.WARNING,
this.getClass().getName(),
"convierteComentarioNota", this.getClass().getName() + "::convierteComentarioNota::el usuario introdujo: " + evaluacion + comentario);
if (evaluacion.trim().equals("Apto") && comentario != null && comentario.length() > 5) {
return "Apto";
} else {
return "No Apto";
}
} catch (IOException ex) {
Logger.getLogger(ComentarioNota.class.getName()).log(Level.SEVERE, null, ex);
} catch (SecurityException ex) {
Logger.getLogger(ComentarioNota.class.getName()).log(Level.SEVERE, null, ex);
}
return "No Apto";
}
}
However I still seeing the log being outputted to the console and no file is being created:
And no file is being creted because at least it does not show up in the IDE and I have also try to find it:
Could you help me figuring out how to log to a local file properly?
Thank you for your help.
you need to configure java.util.logging. what you're looking for is the FileHandler. I actually prefer other logging APIs but for your purposes please start here: Tutorial: Java Logging Configuration

error package org.apache.pig.FilterFunc not exist

May I ask a question please, I get Pig installed and configured, but it says "error package org.apache.pig.FilterFunc not exist" while I am trying to compile a very simple java source file by using javac command.
The CLASSPATH variable is set as listed below:
/usr/local/hadoop/share/hadoop/common/hadoop-common-2.7.0.jar:/usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-client-core-2.7.0.jar:/usr/local/hadoop/share/hadoop/common/lib/commons-cli-1.2.jar:/usr/local/hadoop/etc/hadoop/:/usr/local/pig/lib/:.:/usr/java/jdk1.8.0_45/jre/lib/rt.jar:/usr/java/jdk1.8.0_45/lib/dt.jar:/usr/java/jdk1.8.0_45/lib/tools.jar:/usr/share/ant/lib/ant-launcher.jar
and these two environment variables set as below:
export PIG_INSTALL=/usr/local/pig
export PIG_CLASSPATH=$HADOOP_INSTALL/etc/hadoop
The source code of file IsUseragentBot.java is listed as below:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.apache.pig.FilterFunc;
import org.apache.pig.data.Tuple;
public class IsUseragentBot extends FilterFunc {
private Set<String> blacklist = null;
private void loadBlacklist() throws IOException {
blacklist = new HashSet<String>();
BufferedReader in = new BufferedReader(new FileReader("blacklist"));
String userAgent = null;
while ((userAgent = in.readLine()) != null) {
blacklist.add(userAgent);
}
}
#Override
public Boolean exec(Tuple tuple) throws IOException {
if (blacklist == null) {
loadBlacklist();
}
if (tuple == null || tuple.size() == 0) {
return null;
}
String ua = (String)tuple.get(0);
if (blacklist.contains(ua)) {
return true;
}
return false;
}
}
While I am going to compile source file by executing javac IsUseragentBot.java,it always fails and says that "error package org.apache.pig not exist",could any buddy help me please,thanks a lot!
I have solved this problem, it is related to compatibility between pig and hadoop, besides the compilation of pig

Exception thrown when trying to load Pentaho report through java program

This code was mostly taken from Pentaho's website on sample programs. The goal is to use their API to generate HTML output with a Pentaho report.
The report was created with Pentaho 3.5.0-RC2
The version of jars I am using are the following:
pentaho-reporting-engine-classic-core-3.5.0-RC2.jar
libloader-1.1.1.jar
libbase-1.1.1.jar
The exception I am getting is the following:
org.pentaho.reporting.libraries.resourceloader.ContentNotRecognizedException: None of the selected factories was able to handle the given data: ResourceKey{schema=org.pentaho.reporting.libraries.resourceloader.loader.URLResourceLoader, identifier=file:/C:/Users/elias.kopsiaftis/workspace_experimental/TestPentahoReport/bin/Test.prpt, factoryParameters={}, parent=null}
at org.pentaho.reporting.libraries.resourceloader.DefaultResourceManagerBackend.create(DefaultResourceManagerBackend.java:295)
at org.pentaho.reporting.libraries.resourceloader.ResourceManager.create(ResourceManager.java:387)
at org.pentaho.reporting.libraries.resourceloader.ResourceManager.create(ResourceManager.java:342)
at org.pentaho.reporting.libraries.resourceloader.ResourceManager.createDirectly(ResourceManager.java:205)
at ReportTester.getReportDefinition(ReportTester.java:74)
at ReportTester.getReport(ReportTester.java:25)
at ReportTester.main(ReportTester.java:84)
java.lang.NullPointerException
at ReportTester.getReport(ReportTester.java:26)
at ReportTester.main(ReportTester.java:84)
And finally, the code snippet!
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Map;
import java.util.HashMap;
import java.io.*;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.modules.output.table.html.HtmlReportUtil;
import org.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.base.util.StackableException;
import org.pentaho.reporting.libraries.resourceloader.ResourceException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
public class ReportTester {
private String reportName = "FormCompleteUsagePerPartnerByMonth.prpt";
private String reportPath = "";
public void getReport() {
try {
final MasterReport report = getReportDefinition();
report.getParameterValues().put("partner", "");
report.getParameterValues().put("startingdate", "2015-03-01");
report.getParameterValues().put("endingdate", "2015-03-05");
HtmlReportUtil.createStreamHTML(report, System.out);
} catch(Exception e) {
e.printStackTrace();
}
}
private MasterReport getReportDefinition() {
try {
// Using the classloader, get the URL to the reportDefinition file
final ClassLoader classloader = this.getClass().getClassLoader();
final URL reportDefinitionURL = classloader.getResource(reportPath + reportName);
if(reportDefinitionURL == null) {
System.out.println("URL was null");
}
// Parse the report file
final ResourceManager resourceManager = new ResourceManager();
resourceManager.registerDefaults();
final Resource directly = resourceManager.createDirectly(reportDefinitionURL, MasterReport.class);
return (MasterReport) directly.getResource();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
ReportTester rt = new ReportTester();
rt.getReport();
}
}
Any advice on this would be much appreciated as I have been scratching my head on this all day. Thanks in advance!

Detecting remote files in Java

I'm using Watcher in JDK7 which relies on inotify events. If the file is on a NFS, I want my program to fallback and use polling instead. Is there a way to detect if a file is on a remote drive (other than using Runtime.exec and parsing the mount table)? I'm only concerned with Linux compatibility for now.
I suppose one option is to use both inotify and polling when the program starts, but then disable the polling thread if an inotify event for my file is created.
You should be able to get relatively reliable info about the underlying file system type with FileStore.type().
It will definitely tell you if it's an NFS, or CIFS, not sure about other network mount types.
However I have no info about how reliable it is, #hoaz's suggestion to check if events are coming through might be a good idea.
I had the same problem. I have solved it by creating a new thread in de main class and touching the files periodically so a new change event gets fired.
The sample polls the dir for every 10 seconds does a touch.
Here a sample of the code:
package com.ardevco.files;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.List;
public class Touch implements Runnable {
private Path touchPath;
public Touch(Path touchPath) {
this.touchPath = touchPath;
this.checkPath = checkPath;
}
public static void touch(Path file) throws IOException {
long timestamp = System.currentTimeMillis();
touch(file, timestamp);
}
public static void touch(Path file, long timestamp) throws IOException {
if (Files.exists(file)) {
FileTime ft = FileTime.fromMillis(timestamp);
Files.setLastModifiedTime(file, ft);
}
}
List<Path> listFiles(Path path) throws IOException {
final List<Path> files = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
for (Path entry : stream) {
if (Files.isDirectory(entry)) {
files.addAll(listFiles(entry));
}
files.add(entry);
}
}
return files;
}
#Override
public void run() {
while (true) {
try {
for (Path path : listFiles(touchPath)) {
touch(path);
}
} catch (IOException e) {
System.out.println("Exception: " + e);
}
try {
Thread.sleep(10000L);
} catch (InterruptedException e) {
System.out.println("Exception: " + e);
}
}
}
}

Categories

Resources