/**
* Copyright 2005-2014 Red Hat, Inc.
*
* Red Hat licenses this file to you under the Apache License, version
* 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package io.fabric8.watcher.spring.context;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Path;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import io.fabric8.watcher.Paths;
import io.fabric8.watcher.Processor;
import io.fabric8.watcher.file.FileWatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import static io.fabric8.watcher.PathHelper.toUrlString;
/**
* A {@link io.fabric8.watcher.file.FileWatcher} which finds Spring Application Context XML files
* using the given file patterns and loads them up and starts them automatically
*/
public class WatcherSpringContext extends FileWatcher {
private static final transient Logger LOG = LoggerFactory.getLogger(WatcherSpringContext.class);
public static final String SPRING_BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
private ConcurrentHashMap<String, FileSystemXmlApplicationContext> contextMap
= new ConcurrentHashMap<String, FileSystemXmlApplicationContext>();
private ApplicationContext parentApplicationContext;
private AtomicBoolean closing = new AtomicBoolean(false);
public WatcherSpringContext() {
setFileMatchPattern("glob:**.xml");
setProcessor(new Processor() {
public void process(Path path) {
if (!closing.get()) {
addPath(path);
}
}
public void onRemove(Path path) {
if (!closing.get()) {
removePath(path);
}
}
});
}
public void init() throws IOException {
super.init();
LOG.info("Watching directory " + getRoot() + " for Spring XML files to load");
}
public void destroy() {
if (closing.compareAndSet(false, true)) {
Set<Map.Entry<String, FileSystemXmlApplicationContext>> entries = contextMap.entrySet();
for (Map.Entry<String, FileSystemXmlApplicationContext> entry : entries) {
String url = entry.getKey();
FileSystemXmlApplicationContext context = entry.getValue();
closeContext(url, context);
}
}
super.destroy();
}
// Properties
//-------------------------------------------------------------------------
public SortedSet<String> getApplicationContextPaths() {
return new TreeSet<String>(contextMap.keySet());
}
public FileSystemXmlApplicationContext getApplicationContext(String path) {
return contextMap.get(path);
}
public ApplicationContext getParentApplicationContext() {
return parentApplicationContext;
}
public void setParentApplicationContext(ApplicationContext parentApplicationContext) {
this.parentApplicationContext = parentApplicationContext;
}
// Implementation
//-------------------------------------------------------------------------
protected void addPath(Path path) {
String url = null;
try {
url = toUrlString(path);
} catch (MalformedURLException e) {
LOG.warn("Ignored path " + path + " due to: " + e, e);
}
if (url != null) {
FileSystemXmlApplicationContext context = contextMap.get(url);
if (context != null) {
try {
LOG.info("Refreshing context at path " + path + " context " + context);
context.refresh();
} catch (Exception e) {
LOG.warn("Failed to refresh context at " + path + " context " + context + ". " + e, e);
}
} else {
context = createContext(path, url);
if (context != null) {
contextMap.put(url, context);
try {
LOG.info("Starting context at path " + path + " context " + context);
context.start();
} catch (Exception e) {
LOG.warn("Failed to start context at " + path + " context " + context + ". " + e, e);
}
}
}
}
}
protected void removePath(Path path) {
try {
String url = toUrlString(path);
FileSystemXmlApplicationContext context = contextMap.remove(url);
closeContext(url, context);
} catch (MalformedURLException e) {
LOG.warn("Ignored path " + path + " due to: " + e, e);
}
}
protected void closeContext(String url, FileSystemXmlApplicationContext context) {
if (context != null) {
try {
LOG.info("Closing context at path " + url + " context " + context);
context.close();
} catch (Exception e) {
LOG.info("Failed to close at " + url + " context " + context + ". " + e, e);
}
}
}
protected FileSystemXmlApplicationContext createContext(Path path, String url) {
if (!Paths.hasNamespace(path, SPRING_BEANS_NAMESPACE_URI)) {
LOG.info("Ignoring XML file " + path + " which is not a spring XML");
return null;
}
String[] locations = {url};
if (parentApplicationContext != null) {
return new FileSystemXmlApplicationContext(locations, true, parentApplicationContext);
} else {
return new FileSystemXmlApplicationContext(locations, true);
}
}
}