/* * Copyright 2003-2015 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.module; import jetbrains.mps.classloading.ClassLoaderManager; import jetbrains.mps.classloading.ModuleClassLoader; import jetbrains.mps.classloading.ModuleClassNotFoundException; import jetbrains.mps.classloading.ModuleIsNotLoadableException; import jetbrains.mps.library.SLibrary; import jetbrains.mps.project.AbstractModule; import jetbrains.mps.smodel.MPSModuleOwner; import jetbrains.mps.smodel.ModuleRepositoryFacade; import jetbrains.mps.util.InternUtil; import jetbrains.mps.vfs.IFile; import jetbrains.mps.vfs.openapi.FileSystem; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; public class ReloadableModuleBase extends AbstractModule implements ReloadableModule { private final static Logger LOG = LogManager.getLogger(ReloadableModuleBase.class); private final ClassLoaderManager myManager = ClassLoaderManager.getInstance(); private final List<SModuleDependenciesListener> myListeners = new CopyOnWriteArrayList<SModuleDependenciesListener>(); protected ReloadableModuleBase() { super(); } protected ReloadableModuleBase(@Nullable IFile file) { super(file); } protected ReloadableModuleBase(@NotNull FileSystem fileSystem) { super(fileSystem); } @NotNull @Override public Class<?> getClass(String classFqName) throws ClassNotFoundException, ModuleIsNotLoadableException { return getClass(classFqName, false); } @NotNull @Override public Class<?> getOwnClass(String classFqName) throws ClassNotFoundException, ModuleIsNotLoadableException { return getClass(classFqName, true); } @NotNull protected Class<?> getClass(String classFqName, boolean ownClassOnly) throws ClassNotFoundException, ModuleClassNotFoundException, ModuleIsNotLoadableException { ClassLoader classLoader = getClassLoader(); if (classLoader == null) { throw new ModuleClassLoaderIsNullException(this); } String internClassName = InternUtil.intern(classFqName); if (ownClassOnly && classLoader instanceof ModuleClassLoader) { return ((ModuleClassLoader) classLoader).loadOwnClass(internClassName); } Class<?> aClass = classLoader.loadClass(internClassName); if (aClass == null) { throw new LoadedClassIsNullException(classLoader, internClassName); } return aClass; } @Nullable @Override public ClassLoader getClassLoader() { return myManager.getClassLoader(this); } @Override public ClassLoader getRootClassLoader() { getRepository().getModelAccess().checkReadAccess(); Set<MPSModuleOwner> moduleOwners = ModuleRepositoryFacade.getInstance().getModuleOwners(this); for (MPSModuleOwner owner : moduleOwners) { if (owner instanceof SLibrary) { ClassLoader classLoader = ((SLibrary) owner).getPluginClassLoader(); if (classLoader != null) { return classLoader; } } } return ReloadableModule.class.getClassLoader(); } @Override public void reload() { if (!willLoad()) return; LOG.info("Reloading module " + this); myManager.reloadModule(this); } @Override public boolean willLoad() { return true; } @Override protected void dependenciesChanged() { super.dependenciesChanged(); if (willLoad()) { fireDependenciesChanged(); } } protected final void fireDependenciesChanged() { assertCanChange(); for (SModuleDependenciesListener listener : myListeners) { listener.dependenciesChanged(this); } } // NOTE: for internal use public final void addDependenciesListener(SModuleDependenciesListener listener) { myListeners.add(listener); } // NOTE: for internal use public final void removeDependenciesListener(SModuleDependenciesListener listener) { myListeners.remove(listener); } // NOTE: for internal use // notifies about ANY changes in deps, used languages, etc. // designed specifically for the class loading client public interface SModuleDependenciesListener { void dependenciesChanged(@NotNull ReloadableModuleBase module); } }