package
com.bluelotussoftware.service;
import
com.bluelotussoftware.service.spi.Log;
import
java.io.IOException;
import
java.net.MalformedURLException;
import
java.net.URL;
import
java.net.URLClassLoader;
import
java.nio.charset.Charset;
import
java.nio.file.DirectoryStream;
import
java.nio.file.FileSystem;
import
java.nio.file.Files;
import
java.nio.file.LinkOption;
import
java.nio.file.Path;
import
java.nio.file.Paths;
import
java.nio.file.spi.FileSystemProvider;
import
java.text.MessageFormat;
import
java.util.ArrayList;
import
java.util.Arrays;
import
java.util.HashMap;
import
java.util.Iterator;
import
java.util.List;
import
java.util.ServiceLoader;
import
javax.faces.context.FacesContext;
import
javax.servlet.ServletContext;
/**
* Singleton {@link Log} service with {@link ClassLoader} and
* {@link ServiceLoader} reloading capabilities for use in a web application.
*
* @author John Yeary
* @version 1.0
*/
public
class
LogService {
private
static
LogService service;
private
ServiceLoader serviceLoader;
private
LogService() {
serviceLoader = ServiceLoader.load(Log.
class
);
}
public
static
synchronized
LogService getInstance() {
if
(service ==
null
) {
service =
new
LogService();
}
return
service;
}
public
List<log> getLoggers() {
List<log> loggers =
new
ArrayList<>();
Iterator<log> it = serviceLoader.iterator();
while
(it.hasNext()) {
loggers.add(it.next());
}
return
loggers;
}
public
Log getFirstAvailableLogger() {
Log log =
null
;
Iterator<log> it = serviceLoader.iterator();
while
(it.hasNext()) {
log = it.next();
break
;
}
return
log;
}
public
void
reload()
throws
MalformedURLException, IOException, ClassNotFoundException {
ServletContext context = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
Path webInfLibDirectory = Paths.get(context.getRealPath(
"WEB-INF/lib"
));
URLClassLoader urlcl;
List<url> jarURLs =
new
ArrayList<>();
FileSystemProvider provider = getZipFileSystemProvider();
List<string> implementationsToLoad =
new
ArrayList<>();
if
(Files.exists(webInfLibDirectory, LinkOption.NOFOLLOW_LINKS)) {
List<path> files = listJars(webInfLibDirectory);
for
(Path p : files) {
info(
"LOCATED JAR "
+ p.toFile().getName());
jarURLs.add(p.toUri().toURL());
FileSystem fs = provider.newFileSystem(p,
new
HashMap<String, Object>());
Path serviceDirectory = fs.getPath(
"/META-INF"
,
"services"
);
info(
"SCANNING SERVICES"
);
if
(Files.exists(serviceDirectory)) {
DirectoryStream<path> serviceListings = Files.newDirectoryStream(serviceDirectory);
for
(Path px : serviceListings) {
List<string> services = Files.readAllLines(px, Charset.forName(
"UTF-8"
));
info(MessageFormat.format(
"SERVICES FOUND: {0}"
, Arrays.toString(services.toArray())));
implementationsToLoad.addAll(services);
}
}
}
urlcl =
new
URLClassLoader(jarURLs.toArray(
new
URL[jarURLs.size()]), context.getClassLoader());
load(implementationsToLoad, urlcl);
serviceLoader = ServiceLoader.load(Log.
class
, urlcl);
Iterator<log> it = serviceLoader.iterator();
while
(it.hasNext()) {
info(it.next().getClass().getName());
}
}
}
private
List<path> listJars(Path path)
throws
IOException {
List<path> jars =
new
ArrayList<>();
DirectoryStream<path> ds = Files.newDirectoryStream(path,
"*.jar"
);
for
(Path child : ds) {
if
(!Files.isDirectory(child)) {
jars.add(child);
}
}
return
jars;
}
private
void
load(
final
List<string> FQCN,
final
ClassLoader classLoader)
throws
ClassNotFoundException {
for
(String s : FQCN) {
info(MessageFormat.format(
"LOAD CLASS {0}"
, s));
Class<?> clazz = classLoader.loadClass(s);
info(MessageFormat.format(
"CLASS {0} LOADED"
, clazz.getName()));
}
}
private
static
FileSystemProvider getZipFileSystemProvider() {
for
(FileSystemProvider provider : FileSystemProvider.installedProviders()) {
if
(
"jar"
.equals(provider.getScheme())) {
return
provider;
}
}
return
null
;
}
private
void
info(
final
String message) {
getFirstAvailableLogger().info(message);
}
}