001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     bstefanescu
018 *
019 * $Id$
020 */
021
022package org.nuxeo.runtime.deploy;
023
024import java.io.File;
025import java.io.IOException;
026import java.net.URI;
027import java.net.URISyntaxException;
028import java.net.URL;
029import java.util.HashMap;
030import java.util.Map;
031
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.nuxeo.common.collections.ListenerList;
035import org.nuxeo.runtime.model.RuntimeContext;
036
037/**
038 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
039 */
040public class ConfigurationDeployer implements FileChangeListener {
041
042    private static final Log log = LogFactory.getLog(ConfigurationDeployer.class);
043
044    protected final Map<String, Entry> urls;
045
046    protected final FileChangeNotifier notifier;
047
048    protected final ListenerList listeners = new ListenerList();
049
050    public ConfigurationDeployer() {
051        this(null);
052    }
053
054    public ConfigurationDeployer(FileChangeNotifier notifier) {
055        this.notifier = notifier;
056        urls = new HashMap<String, Entry>();
057        if (notifier != null) {
058            notifier.addListener(this);
059        }
060    }
061
062    public void deploy(RuntimeContext ctx, URL url, boolean trackChanges) throws IOException {
063        File watchFile = null;
064        if (trackChanges) {
065            try {
066                String str = url.toExternalForm();
067                if (str.startsWith("file:")) {
068                    watchFile = new File(url.toURI());
069                } else if (str.startsWith("jar:file:")) {
070                    int p = str.lastIndexOf('!');
071                    if (p > -1) {
072                        str = str.substring(4, p);
073                    } else {
074                        str = str.substring(4);
075                    }
076                    watchFile = new File(new URI(str));
077                }
078            } catch (URISyntaxException e) {
079                log.error(e);
080                watchFile = null;
081            }
082        }
083        _deploy(ctx, url, watchFile, trackChanges);
084    }
085
086    public void undeploy(URL url) throws IOException {
087        Entry entry = urls.remove(url.toExternalForm());
088        if (entry != null) {
089            _undeploy(entry);
090        }
091    }
092
093    public synchronized void _undeploy(Entry entry) throws IOException {
094        try {
095            entry.ctx.undeploy(entry.url);
096        } finally {
097            if (notifier != null && entry.watchFile != null) {
098                notifier.unwatch(entry.url.toExternalForm(), entry.watchFile);
099            }
100        }
101    }
102
103    public void deploy(RuntimeContext ctx, File file, boolean trackChanges) throws IOException {
104        _deploy(ctx, file.getCanonicalFile().toURI().toURL(), file, trackChanges);
105    }
106
107    public void undeploy(File file) throws IOException {
108        undeploy(file.getCanonicalFile().toURI().toURL());
109    }
110
111    protected synchronized void _deploy(RuntimeContext ctx, URL url, File watchFile, boolean trackChanges)
112            throws IOException {
113        String id = url.toExternalForm();
114        Entry entry = new Entry(ctx, url, watchFile);
115        ctx.deploy(url);
116        urls.put(id, entry);
117        if (trackChanges && notifier != null && watchFile != null) {
118            notifier.watch(id, watchFile);
119        }
120    }
121
122    @Override
123    public void fileChanged(FileChangeNotifier.FileEntry entry, long now) {
124        Entry e = urls.get(entry.id);
125        if (e != null) {
126            try {
127                e.ctx.undeploy(e.url);
128                e.ctx.deploy(e.url);
129            } catch (IOException ex) {
130                throw new RuntimeException(ex);
131            } finally {
132                fireConfigurationChanged(e);
133            }
134        }
135    }
136
137    public void addConfigurationChangedListener(ConfigurationChangedListener listener) {
138        listeners.add(listener);
139    }
140
141    public void removeConfigurationChangedListener(ConfigurationChangedListener listener) {
142        listeners.remove(listener);
143    }
144
145    public void fireConfigurationChanged(Entry entry) {
146        for (Object obj : listeners.getListenersCopy()) {
147            ((ConfigurationChangedListener) obj).configurationChanged(entry);
148        }
149    }
150
151    public static class Entry {
152        final RuntimeContext ctx;
153
154        final URL url;
155
156        File watchFile;
157
158        Entry(RuntimeContext ctx, URL config, File watchFile) throws IOException {
159            url = config;
160            this.ctx = ctx;
161            if (watchFile != null) {
162                this.watchFile = watchFile.getCanonicalFile();
163            }
164        }
165
166        public RuntimeContext getContext() {
167            return ctx;
168        }
169
170        public File getWatchFile() {
171            return watchFile;
172        }
173
174        public URL getUrl() {
175            return url;
176        }
177
178        @Override
179        public String toString() {
180            return url.toExternalForm();
181        }
182    }
183
184}