/* * 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.plugins; import jetbrains.mps.module.ReloadableModule; import jetbrains.mps.plugins.applicationplugins.BaseApplicationPlugin; import jetbrains.mps.plugins.projectplugins.BaseProjectPlugin; import jetbrains.mps.project.AbstractModule; import jetbrains.mps.util.ModuleNameUtil; import jetbrains.mps.vfs.IFile; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.module.SModule; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class ModulePluginContributor extends PluginContributor { private static final Logger LOG = LogManager.getLogger(ModulePluginContributor.class); private static final String PLUGIN_STRING = ".plugin."; private static final String PROJECT_PLUGIN_SUFFIX = "_ProjectPlugin"; private static final String APP_PLUGIN_SUFFIX = "_ApplicationPlugin"; private static String getProjectPluginClassName(SModule module) { return String.format("%s%s%s%s", module.getModuleName(), PLUGIN_STRING, ModuleNameUtil.getModuleShortName(module), PROJECT_PLUGIN_SUFFIX); } private static String getApplicationPluginClassName(SModule module) { return String.format("%s%s%s%s", module.getModuleName(), PLUGIN_STRING, ModuleNameUtil.getModuleShortName(module), APP_PLUGIN_SUFFIX); } @NotNull public ReloadableModule getModule() { return myModule; } @NotNull private final ReloadableModule myModule; public ModulePluginContributor(@NotNull ReloadableModule module) { myModule = module; } @Override public BaseApplicationPlugin createApplicationPlugin() { String pluginClassName; Properties cfg = getComponentStartupConfiguration(); if (cfg == null || (pluginClassName = cfg.getProperty("init.application")) == null) { // fallback to legacy, name convention approach pluginClassName = getApplicationPluginClassName(myModule); } return pluginClassName == null ? null : createPlugin(BaseApplicationPlugin.class, pluginClassName); } @Override public BaseProjectPlugin createProjectPlugin() { String pluginClassName; Properties cfg = getComponentStartupConfiguration(); if (cfg == null || (pluginClassName = cfg.getProperty("init.project")) == null) { // fallback to legacy, name convention approach pluginClassName = getProjectPluginClassName(myModule); } return pluginClassName == null ? null : createPlugin(BaseProjectPlugin.class, pluginClassName); } @Nullable private <T> T createPlugin(Class<T> expectedClass, String className) { try { Class<?> pluginClass = myModule.getOwnClass(className); return pluginClass.asSubclass(expectedClass).newInstance(); } catch (ClassNotFoundException e) { return null; } catch (Throwable t) { LOG.error("Failed to instantiate plugin component activator", t); return null; } } @Nullable private Properties getComponentStartupConfiguration() { // although getResource does look into dependencies and chances are we read configuration // of another module, the fact we use loadOwnClass later prevents loading it second time. // However, shall update fallback solution (try to load from config name, then try to load from fallback name) // unless switch to files here IFile dir = ((AbstractModule) myModule).getModuleSourceDir(); if (dir == null) { return null; } IFile cfg = dir.getDescendant("startup.properties"); // Note, META-INF location won't work for groups of modules distributed as a single plugin, shall come up with better approach if (!cfg.exists()) { return null; } InputStream is = null; try { is = cfg.openInputStream(); Properties rv = new Properties(); rv.load(is); return rv; } catch (IOException ex) { LOG.warn("Failed to read startup.properties for module " + myModule.getModuleName(), ex); } finally { if (is != null) { try { is.close(); } catch (IOException ignore) {} } } return null; } @Override public int hashCode() { return myModule.hashCode(); } @Override public boolean equals(Object o) { return o instanceof ModulePluginContributor && (((ModulePluginContributor) o).myModule == myModule); } @Override public String toString() { return myModule + " plugin contributor"; } }