/*
* Copyright 2003-2014 JetBrains s.r.o.
*
* Licensed 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 jetbrains.mps.classloading;
import jetbrains.mps.module.ReloadableModule;
import jetbrains.mps.module.ReloadableModuleBase;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.module.ModelAccess;
import org.jetbrains.mps.openapi.module.SModuleReference;
import org.jetbrains.mps.openapi.util.ProgressMonitor;
import org.jetbrains.mps.openapi.util.SubProgressKind;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Broadcasting class loading load/unload events.
* Guarantees that the listeners are invoked in write action
*/
public class ClassLoadingBroadCaster {
private static final Logger LOG = LogManager.getLogger(ClassLoadingBroadCaster.class);
private final LinkedHashSet<ReloadableModule> myLoadedModules = new LinkedHashSet<ReloadableModule>();
private final ModelAccess myModelAccess;
// reload handlers
private final List<MPSClassesListener> myClassesHandlers = new CopyOnWriteArrayList<MPSClassesListener>();
private final List<ModuleReloadListener> myReloadListeners = new CopyOnWriteArrayList<ModuleReloadListener>();
private final List<DeployListener> myDeployListeners = new CopyOnWriteArrayList<>();
public ClassLoadingBroadCaster(ModelAccess modelAccess) {
myModelAccess = modelAccess;
}
public void addClassesHandler(MPSClassesListener handler) {
myClassesHandlers.add(handler);
}
public void removeClassesHandler(MPSClassesListener handler) {
myClassesHandlers.remove(handler);
}
public void addReloadListener(ModuleReloadListener listener) {
myReloadListeners.add(listener);
}
public void removeReloadListener(ModuleReloadListener listener) {
myReloadListeners.remove(listener);
}
public Set<ReloadableModule> onUnload(Collection<? extends SModuleReference> refsToUnload, @NotNull ProgressMonitor monitor) {
if (refsToUnload.isEmpty()) return Collections.emptySet();
myModelAccess.checkWriteAccess();
final Set<ReloadableModule> modulesToUnload = new LinkedHashSet<>();
for (ReloadableModule loadedModule : myLoadedModules) {
SModuleReference mRef = loadedModule.getModuleReference();
if (refsToUnload.contains(mRef)) {
modulesToUnload.add(loadedModule);
}
}
if (modulesToUnload.size() < refsToUnload.size()) {
LOG.error("", new IllegalArgumentException("Broken contract : some of the passed module references have not been loaded"));
}
myLoadedModules.removeAll(modulesToUnload);
try {
monitor.start("Broadcasting Events", myClassesHandlers.size() + myDeployListeners.size());
for (MPSClassesListener listener : myClassesHandlers) {
try {
listener.onUnloaded(modulesToUnload, monitor.subTask(1));
} catch (VirtualMachineError e) {
throw e;
} catch (Throwable e) {
LOG.error("Caught exception from the listener " + listener + ". Will continue.", e);
}
}
for (DeployListener listener : myDeployListeners) {
try {
listener.onUnloaded(modulesToUnload, monitor.subTask(1));
} catch (VirtualMachineError e) {
throw e;
} catch (Throwable e) {
LOG.error("Caught exception from the listener " + listener + ". Will continue.", e);
}
}
} finally {
monitor.done();
}
final Set<ReloadableModule> resultingUnload = new LinkedHashSet<ReloadableModule>();
for (ReloadableModule module : modulesToUnload) resultingUnload.add(module);
return resultingUnload;
}
public void onLoad(Set<ReloadableModule> toLoad, @NotNull ProgressMonitor monitor) {
if (toLoad.isEmpty()) return;
myModelAccess.checkWriteAccess();
final Set<ReloadableModuleBase> modulesToLoad = new LinkedHashSet<>(toLoad.size());
for (ReloadableModule module : toLoad) {
modulesToLoad.add((ReloadableModuleBase) module);
}
myLoadedModules.addAll(modulesToLoad);
try {
monitor.start("Broadcasting Events", myClassesHandlers.size() + myDeployListeners.size());
for (MPSClassesListener listener : myClassesHandlers) {
try {
listener.onLoaded(toLoad, monitor.subTask(1));
} catch (VirtualMachineError e) {
throw e;
} catch (Throwable e) {
LOG.error("Caught exception from the listener " + listener + ". Will continue.", e);
}
}
for (DeployListener listener : myDeployListeners) {
try {
listener.onLoaded(toLoad, monitor.subTask(1));
} catch (VirtualMachineError e) {
throw e;
} catch (Throwable e) {
LOG.error("Caught exception from the listener " + listener + ". Will continue.", e);
}
}
} finally {
monitor.done();
}
}
public void onReload(Collection<ReloadableModule> reloadedModules) {
if (reloadedModules.isEmpty()) return;
myModelAccess.checkWriteAccess();
final Set<ReloadableModule> modulesToReload = new LinkedHashSet<ReloadableModule>(reloadedModules.size());
for (ReloadableModule module : reloadedModules) modulesToReload.add(module);
for (ModuleReloadListener listener : myReloadListeners) {
listener.modulesReloaded(modulesToReload);
}
}
public void addListener(@NotNull DeployListener listener) {
myDeployListeners.add(listener);
}
public void removeListener(@NotNull DeployListener listener) {
myDeployListeners.remove(listener);
}
}