/*
* Copyright 2013-2016 consulo.io
*
* 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 consulo.compiler.server.application;
import com.intellij.core.CoreFileTypeRegistry;
import com.intellij.ide.StartupProgress;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.*;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.components.StateStorageException;
import com.intellij.openapi.components.impl.ApplicationPathMacroManager;
import com.intellij.openapi.components.impl.ComponentManagerImpl;
import com.intellij.openapi.components.impl.stores.ApplicationStoreImpl;
import com.intellij.openapi.components.impl.stores.IApplicationStore;
import com.intellij.openapi.components.impl.stores.IComponentStore;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.project.impl.ProjectManagerImpl;
import com.intellij.openapi.util.*;
import com.intellij.util.io.storage.HeavyProcessLatch;
import consulo.annotations.RequiredDispatchThread;
import consulo.annotations.RequiredReadAction;
import consulo.annotations.RequiredWriteAction;
import consulo.application.ex.ApplicationEx2;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.picocontainer.MutablePicoContainer;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.util.concurrent.*;
/**
* @author VISTALL
* @since 11:26/12.08.13
*/
public class CompilerServerApplication extends ComponentManagerImpl implements ApplicationEx2 {
public static final Logger LOGGER = Logger.getInstance(CompilerServerApplication.class);
private static class ExecutorServiceHolder {
private static final ExecutorService ourThreadExecutorsService = createServiceImpl();
private static ThreadPoolExecutor createServiceImpl() {
return new ThreadPoolExecutor(10, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() {
@NotNull
@Override
@SuppressWarnings({"HardCodedStringLiteral"})
public Thread newThread(@NotNull Runnable r) {
return new Thread(r, "CompilerServerApplication pooled thread");
}
});
}
}
public static CompilerServerApplication createApplication() {
final CompilerServerApplication app = new CompilerServerApplication();
ApplicationManager.setApplication(app, new Getter<FileTypeRegistry>() {
@Override
public FileTypeRegistry get() {
return new CoreFileTypeRegistry();
}
}, app
);
return app;
}
private boolean myDisposeInProgress;
public CompilerServerApplication() {
super(null);
ApplicationManager.setApplication(this, Disposer.newDisposable());
getPicoContainer().registerComponentInstance(Application.class, this);
loadApplicationComponents();
registerShutdownHook();
}
private void loadApplicationComponents() {
PluginManagerCore.initPlugins(new StartupProgress() {
@Override
public void showProgress(String message, float progress) {
}
});
final IdeaPluginDescriptor[] plugins = PluginManagerCore.getPlugins();
for (IdeaPluginDescriptor plugin : plugins) {
if (PluginManagerCore.shouldSkipPlugin(plugin)) continue;
loadComponentsConfiguration(plugin.getAppComponents(), plugin, false);
}
}
@Override
protected void bootstrapPicoContainer(@NotNull String name) {
super.bootstrapPicoContainer(name);
getPicoContainer().registerComponentImplementation(IComponentStore.class, ApplicationStoreImpl.class);
getPicoContainer().registerComponentImplementation(ApplicationPathMacroManager.class);
}
@Override
public void initializeComponent(Object component, boolean service) {
getStateStore().initComponent(component);
}
@NotNull
@Override
public IApplicationStore getStateStore() {
return (IApplicationStore)getPicoContainer().getComponentInstance(IComponentStore.class);
}
@NotNull
@Override
protected MutablePicoContainer createPicoContainer() {
return Extensions.getRootArea().getPicoContainer();
}
@Override
public synchronized void dispose() {
ShutDownTracker.getInstance().ensureStopperThreadsFinished();
disposeComponents();
ExecutorServiceHolder.ourThreadExecutorsService.shutdownNow();
super.dispose();
}
private void registerShutdownHook() {
ShutDownTracker.getInstance(); // Necessary to avoid creating an instance while already shutting down.
ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
@Override
public void run() {
if (isDisposed() || isDisposeInProgress()) {
return;
}
ShutDownTracker.invokeAndWait(isUnitTestMode(), true, new Runnable() {
@Override
public void run() {
if (ApplicationManager.getApplication() != CompilerServerApplication.this) return;
myDisposeInProgress = true;
if (!disposeSelf(true)) {
myDisposeInProgress = false;
}
}
});
}
});
}
private boolean disposeSelf(final boolean checkCanCloseProject) {
final CommandProcessor commandProcessor = CommandProcessor.getInstance();
final boolean[] canClose = {true};
for (final Project project : ProjectManagerEx.getInstanceEx().getOpenProjects()) {
try {
commandProcessor.executeCommand(project, new Runnable() {
@Override
public void run() {
final ProjectManagerImpl manager = (ProjectManagerImpl)ProjectManagerEx.getInstanceEx();
if (!manager.closeProject(project, true, true, checkCanCloseProject)) {
canClose[0] = false;
}
}
}, ApplicationBundle.message("command.exit"), null);
}
catch (Throwable e) {
CompilerServerApplication.LOGGER.error(e);
}
if (!canClose[0]) {
return false;
}
}
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
Disposer.dispose(CompilerServerApplication.this);
}
});
Disposer.assertIsEmpty();
return true;
}
@Override
public boolean isInternal() {
return false;
}
@Override
public boolean isEAP() {
return false;
}
@Override
public void runReadAction(@NotNull Runnable action) {
try {
action.run();
}
catch (Exception e) {
CompilerServerApplication.LOGGER.error(e);
}
}
@Override
public <T> T runReadAction(@NotNull Computable<T> computation) {
try {
return computation.compute();
}
catch (Exception e) {
CompilerServerApplication.LOGGER.error(e);
return null;
}
}
@Override
public <T, E extends Throwable> T runReadAction(@NotNull ThrowableComputable<T, E> computation) throws E {
return computation.compute();
}
@RequiredDispatchThread
@Override
public void runWriteAction(@NotNull Runnable action) {
try {
action.run();
}
catch (Exception e) {
CompilerServerApplication.LOGGER.error(e);
}
}
@RequiredDispatchThread
@Override
public <T> T runWriteAction(@NotNull Computable<T> computation) {
try {
return computation.compute();
}
catch (Exception e) {
CompilerServerApplication.LOGGER.error(e);
return null;
}
}
@RequiredDispatchThread
@Override
public <T, E extends Throwable> T runWriteAction(@NotNull ThrowableComputable<T, E> computation) throws E {
return computation.compute();
}
@RequiredDispatchThread
@Override
public boolean hasWriteAction(@Nullable Class<?> actionClass) {
return true;
}
@RequiredReadAction
@Override
public void assertReadAccessAllowed() {
}
@RequiredWriteAction
@Override
public void assertWriteAccessAllowed() {
}
@RequiredDispatchThread
@Override
public void assertIsDispatchThread() {
}
@Override
public void addApplicationListener(@NotNull ApplicationListener listener) {
}
@Override
public void addApplicationListener(@NotNull ApplicationListener listener, @NotNull Disposable parent) {
}
@Override
public void removeApplicationListener(@NotNull ApplicationListener listener) {
}
@Override
public void saveAll() {
}
@Override
public void saveSettings() {
}
@Override
public void exit() {
}
@Override
public boolean isWriteAccessAllowed() {
return true;
}
@Override
public boolean isReadAccessAllowed() {
return true;
}
@Override
public boolean isDispatchThread() {
return true;
}
@NotNull
@Override
public ModalityInvokator getInvokator() {
return null;
}
@Override
public void invokeLater(@NotNull Runnable runnable) {
try {
runnable.run();
}
catch (Exception e) {
CompilerServerApplication.LOGGER.error(e);
}
}
@Override
public void invokeLater(@NotNull Runnable runnable, @NotNull Condition expired) {
try {
runnable.run();
}
catch (Exception e) {
CompilerServerApplication.LOGGER.error(e);
}
}
@Override
public void invokeLater(@NotNull Runnable runnable, @NotNull ModalityState state) {
try {
runnable.run();
}
catch (Exception e) {
CompilerServerApplication.LOGGER.error(e);
}
}
@Override
public void invokeLater(@NotNull Runnable runnable, @NotNull ModalityState state, @NotNull Condition expired) {
try {
runnable.run();
}
catch (Exception e) {
CompilerServerApplication.LOGGER.error(e);
}
}
@Override
public void invokeAndWait(@NotNull Runnable runnable, @NotNull ModalityState modalityState) {
try {
runnable.run();
}
catch (Exception e) {
CompilerServerApplication.LOGGER.error(e);
}
}
@NotNull
@Override
public ModalityState getCurrentModalityState() {
return ModalityState.NON_MODAL;
}
@NotNull
@Override
public ModalityState getModalityStateForComponent(@NotNull Component c) {
return ModalityState.NON_MODAL;
}
@NotNull
@Override
public ModalityState getDefaultModalityState() {
return ModalityState.NON_MODAL;
}
@NotNull
@Override
public ModalityState getNoneModalityState() {
return ModalityState.NON_MODAL;
}
@NotNull
@Override
public ModalityState getAnyModalityState() {
return ModalityState.NON_MODAL;
}
@Override
public long getStartTime() {
return 0;
}
@RequiredDispatchThread
@Override
public long getIdleTime() {
return 0;
}
@Override
public boolean isUnitTestMode() {
return false;
}
@Override
public boolean isHeadlessEnvironment() {
return true;
}
@Override
public boolean isCompilerServerMode() {
return true;
}
@Override
public boolean isCommandLine() {
return false;
}
@NotNull
@Override
public Future<?> executeOnPooledThread(@NotNull Runnable action) {
return ExecutorServiceHolder.ourThreadExecutorsService.submit(action);
}
@NotNull
@Override
public <T> Future<T> executeOnPooledThread(@NotNull Callable<T> action) {
return ExecutorServiceHolder.ourThreadExecutorsService.submit(action);
}
@Override
public boolean isDisposeInProgress() {
return myDisposeInProgress || ShutDownTracker.isShutdownHookRunning();
}
@Override
public boolean isRestartCapable() {
return false;
}
@Override
public void restart() {
}
@Override
public boolean isActive() {
return true;
}
@NotNull
@Override
public AccessToken acquireReadActionLock() {
return AccessToken.EMPTY_ACCESS_TOKEN;
}
@RequiredDispatchThread
@NotNull
@Override
public AccessToken acquireWriteActionLock(@NotNull Class marker) {
return AccessToken.EMPTY_ACCESS_TOKEN;
}
@Override
public void load(String path) throws IOException {
getStateStore().setOptionsPath(path);
getStateStore().setConfigPath(PathManager.getConfigPath());
AccessToken accessToken = HeavyProcessLatch.INSTANCE.processStarted("app store load");
try {
getStateStore().load();
}
catch (StateStorageException e) {
throw new IOException(e.getMessage());
}
finally {
accessToken.finish();
}
}
@Override
public boolean isLoaded() {
return true;
}
@Override
public boolean holdsReadLock() {
return false;
}
@Override
public boolean isWriteActionInProgress() {
return false;
}
@Override
public boolean isWriteActionPending() {
return false;
}
@Override
public void doNotSave() {
}
@Override
public void doNotSave(boolean value) {
}
@Override
public boolean isDoNotSave() {
return true;
}
@Override
public void exit(boolean force, boolean exitConfirmed) {
}
@Override
public void restart(boolean force) {
}
@RequiredDispatchThread
@Override
public boolean runProcessWithProgressSynchronously(@NotNull Runnable process,
@NotNull String progressTitle,
boolean canBeCanceled,
Project project) {
process.run();
return true;
}
@RequiredDispatchThread
@Override
public boolean runProcessWithProgressSynchronously(@NotNull Runnable process,
@NotNull String progressTitle,
boolean canBeCanceled,
@Nullable Project project,
JComponent parentComponent) {
process.run();
return true;
}
@RequiredDispatchThread
@Override
public boolean runProcessWithProgressSynchronously(@NotNull Runnable process,
@NotNull String progressTitle,
boolean canBeCanceled,
@Nullable Project project,
JComponent parentComponent,
String cancelText) {
process.run();
return true;
}
@RequiredDispatchThread
@Override
public void assertIsDispatchThread(@Nullable JComponent component) {
}
@Override
public void assertTimeConsuming() {
}
@Override
public boolean tryRunReadAction(@NotNull Runnable action) {
action.run();
return true;
}
@NotNull
@Override
public <T> T[] getExtensions(final ExtensionPointName<T> extensionPointName) {
return Extensions.getRootArea().getExtensionPoint(extensionPointName).getExtensions();
}
}