blob: 1f48e7c20d92e8f3dca6ab20bd6b895c64ae9ad1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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 org.netbeans.core.osgi;
import java.beans.PropertyVetoException;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.filesystems.FileStatusListener;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.LocalFileSystem;
import org.openide.filesystems.MultiFileSystem;
import org.openide.filesystems.Repository;
import org.openide.filesystems.XMLFileSystem;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.NbBundle;
import org.openide.util.NbCollections;
import org.osgi.framework.Bundle;
import org.xml.sax.SAXException;
/**
* Default repository to which layers can be added or removed.
*/
class OSGiRepository extends Repository {
private static final Logger LOG = Logger.getLogger(OSGiRepository.class.getName());
public static final OSGiRepository DEFAULT = new OSGiRepository();
private final SFS fs;
private OSGiRepository() {
this(new SFS());
}
private OSGiRepository(SFS fs) {
super(fs);
this.fs = fs;
}
private static URL[] layersFor(List<Bundle> bundles) {
List<URL> layers = new ArrayList<URL>(2);
for (Bundle b : bundles) {
if (b.getSymbolicName().equals("org.netbeans.modules.autoupdate.ui")) { // NOI18N
// Won't work anyway, so don't even try.
continue;
}
String explicit = b.getHeaders().get("OpenIDE-Module-Layer");
if (explicit != null) {
String base, ext;
int idx = explicit.lastIndexOf('.'); // NOI18N
if (idx == -1) {
base = explicit;
ext = ""; // NOI18N
} else {
base = explicit.substring(0, idx);
ext = explicit.substring(idx);
}
for (String suffix : NbCollections.iterable(NbBundle.getLocalizingSuffixes())) {
URL layer = b.getResource(base + suffix + ext);
if (layer != null) {
layers.add(layer);
} else if (suffix.isEmpty()) {
LOG.log(Level.WARNING, "no such layer {0} in {1} of state {2}", new Object[] {explicit, b.getSymbolicName(), b.getState()});
}
}
}
URL generated = b.getResource("META-INF/generated-layer.xml");
if (generated != null) {
layers.add(generated);
}
}
return layers.toArray(new URL[layers.size()]);
}
public void addLayersFor(List<Bundle> bundles) {
URL[] resources = layersFor(bundles);
if (resources.length > 0) {
// System.err.println("adding layers: " + Arrays.toString(resources));
fs.addLayers(resources);
}
}
public void removeLayersFor(List<Bundle> bundles) {
URL[] resources = layersFor(bundles);
if (resources.length > 0) {
// System.err.println("removing layers: " + Arrays.toString(resources));
fs.removeLayers(resources);
}
}
private static final class SFS extends MultiFileSystem implements LookupListener {
static {
Object _3 = FileStatusListener.class;
Object _4 = LookupEvent.class; // FELIX-3477
}
private static final class Layers extends MultiFileSystem {
Layers() {
setPropagateMasks(true);
}
void _setDelegates(Collection<FileSystem> delegates) {
setDelegates(delegates.toArray(new FileSystem[delegates.size()]));
}
}
private final Map<String,FileSystem> fss = new HashMap<String,FileSystem>();
private final FileSystem userdir;
private final Layers layers;
private final Lookup.Result<FileSystem> dynamic = Lookup.getDefault().lookupResult(FileSystem.class);
@SuppressWarnings({"LeakingThisInConstructor", "deprecation"})
SFS() {
dynamic.addLookupListener(this);
layers = new Layers();
File config = null;
String netbeansUser = System.getProperty("netbeans.user");
if (netbeansUser != null) {
config = new File(netbeansUser, "config");
}
if (config != null && (config.isDirectory() || config.mkdirs())) {
LocalFileSystem lfs = new LocalFileSystem();
try {
lfs.setRootDirectory(config);
} catch (Exception x) {
LOG.log(Level.WARNING, "Could not set userdir: " + config, x);
}
userdir = lfs;
} else {
// no persisted configuration
userdir = FileUtil.createMemoryFileSystem();
}
resetAll();
try {
setSystemName("SystemFileSystem");
} catch (PropertyVetoException x) {
throw new AssertionError(x);
}
}
private synchronized void addLayers(URL... resources) {
LOG.log(Level.FINE, "addLayers: {0}", Arrays.asList(resources));
for (URL resource : resources) {
try {
fss.put(resource.toString(), new XMLFileSystem(resource));
} catch (SAXException x) {
LOG.log(Level.WARNING, "Could not parse layer: " + resource, x);
}
}
resetLayers();
}
private synchronized void removeLayers(URL... resources) {
for (URL resource : resources) {
fss.remove(resource.toString());
}
resetLayers();
}
private void resetLayers() {
layers._setDelegates(fss.values());
}
private void resetAll() {
List<FileSystem> delegates = new ArrayList<FileSystem>();
delegates.add(userdir);
Collection<? extends FileSystem> dyn = dynamic.allInstances();
LOG.log(Level.FINE, "dyn={0}", dyn);
for (FileSystem fs : dyn) {
if (Boolean.TRUE.equals(fs.getRoot().getAttribute("fallback"))) { // NOI18N
continue;
}
delegates.add(fs);
}
delegates.add(layers);
for (FileSystem fs : dyn) {
if (Boolean.TRUE.equals(fs.getRoot().getAttribute("fallback"))) { // NOI18N
delegates.add(fs);
}
}
setDelegates(delegates.toArray(new FileSystem[delegates.size()]));
assert getRoot().isValid();
assert !isReadOnly() : delegates;
}
public @Override void resultChanged(LookupEvent ev) {
resetAll();
}
}
}