blob: 8c41a8e30ab65f1bbdc3e477e43150a442f39d78 [file] [log] [blame]
/*
* Copyright 2000-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 com.intellij.ide;
import com.intellij.concurrency.JobScheduler;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.notification.*;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.ui.HyperlinkAdapter;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.SystemProperties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.PropertyKey;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import java.awt.*;
import java.io.File;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class SystemHealthMonitor extends ApplicationComponent.Adapter {
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.SystemHealthMonitor");
private static final NotificationGroup GROUP = new NotificationGroup("System Health", NotificationDisplayType.STICKY_BALLOON, false);
private static final NotificationGroup LOG_GROUP = NotificationGroup.logOnlyGroup("System Health (minor)");
@NotNull private final PropertiesComponent myProperties;
public SystemHealthMonitor(@NotNull PropertiesComponent properties) {
myProperties = properties;
}
@Override
public void initComponent() {
checkJvm();
startDiskSpaceMonitoring();
}
private void checkJvm() {
if (StringUtil.containsIgnoreCase(System.getProperty("java.vm.name", ""), "OpenJDK")) {
notifyUnsupportedJvm("unsupported.jvm.openjdk.message");
}
else if (StringUtil.endsWithIgnoreCase(System.getProperty("java.version", ""), "-ea")) {
notifyUnsupportedJvm("unsupported.jvm.ea.message");
}
}
private void notifyUnsupportedJvm(@PropertyKey(resourceBundle = "messages.IdeBundle") final String key) {
final String ignoreKey = "ignore." + key;
final String message = IdeBundle.message(key) + IdeBundle.message("unsupported.jvm.link");
showNotification(ignoreKey, message, new HyperlinkAdapter() {
@Override
protected void hyperlinkActivated(HyperlinkEvent e) {
myProperties.setValue(ignoreKey, "true");
}
});
}
private void showNotification(final String ignoreKey, final String message, final HyperlinkAdapter hyperlinkAdapter) {
if (myProperties.isValueSet(ignoreKey)) {
return;
}
final Application app = ApplicationManager.getApplication();
app.getMessageBus().connect(app).subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() {
@Override
public void appFrameCreated(String[] commandLineArgs, @NotNull Ref<Boolean> willOpenProject) {
app.invokeLater(new Runnable() {
public void run() {
JComponent component = WindowManager.getInstance().findVisibleFrame().getRootPane();
if (component != null) {
Rectangle rect = component.getVisibleRect();
JBPopupFactory.getInstance()
.createHtmlTextBalloonBuilder(message, MessageType.WARNING, hyperlinkAdapter)
.setFadeoutTime(-1)
.setHideOnFrameResize(false)
.setHideOnLinkClick(true)
.setDisposable(app)
.createBalloon()
.show(new RelativePoint(component, new Point(rect.x + 30, rect.y + rect.height - 10)), Balloon.Position.above);
}
Notification notification = LOG_GROUP.createNotification(message, NotificationType.WARNING);
notification.setImportant(true);
Notifications.Bus.notify(notification);
}
});
}
});
}
private static void startDiskSpaceMonitoring() {
if (SystemProperties.getBooleanProperty("idea.no.system.path.space.monitoring", false)) {
return;
}
final File file = new File(PathManager.getSystemPath());
final AtomicBoolean reported = new AtomicBoolean();
final ThreadLocal<Future<Long>> ourFreeSpaceCalculation = new ThreadLocal<Future<Long>>();
JobScheduler.getScheduler().schedule(new Runnable() {
private static final long LOW_DISK_SPACE_THRESHOLD = 50 * 1024 * 1024;
private static final long MAX_WRITE_SPEED_IN_BPS = 500 * 1024 * 1024; // 500 MB/sec is near max SSD sequential write speed
@Override
public void run() {
if (!reported.get()) {
Future<Long> future = ourFreeSpaceCalculation.get();
if (future == null) {
ourFreeSpaceCalculation.set(future = ApplicationManager.getApplication().executeOnPooledThread(new Callable<Long>() {
@Override
public Long call() throws Exception {
return file.getUsableSpace();
}
}));
}
if (!future.isDone() || future.isCancelled()) {
JobScheduler.getScheduler().schedule(this, 1, TimeUnit.SECONDS);
return;
}
try {
final long fileUsableSpace = future.get();
final long timeout = Math.max(5, (fileUsableSpace - LOW_DISK_SPACE_THRESHOLD) / MAX_WRITE_SPEED_IN_BPS);
ourFreeSpaceCalculation.set(null);
if (fileUsableSpace < LOW_DISK_SPACE_THRESHOLD) {
if (!notificationsComponentIsLoaded()) {
ourFreeSpaceCalculation.set(future);
JobScheduler.getScheduler().schedule(this, 1, TimeUnit.SECONDS);
return;
}
reported.compareAndSet(false, true);
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
String productName = ApplicationNamesInfo.getInstance().getFullProductName();
String message = IdeBundle.message("low.disk.space.message", productName);
if (fileUsableSpace < 100 * 1024) {
LOG.warn(message);
Messages.showErrorDialog(message, "Fatal Configuration Problem");
reported.compareAndSet(true, false);
restart(timeout);
}
else {
GROUP.createNotification(message, file.getPath(), NotificationType.ERROR, null).whenExpired(new Runnable() {
@Override
public void run() {
reported.compareAndSet(true, false);
restart(timeout);
}
}).notify(null);
}
}
});
}
else {
restart(timeout);
}
}
catch (Exception ex) {
LOG.error(ex);
}
}
}
private boolean notificationsComponentIsLoaded() {
return ApplicationManager.getApplication().runReadAction(new Computable<NotificationsConfiguration>() {
@Override
public NotificationsConfiguration compute() {
return NotificationsConfiguration.getNotificationsConfiguration();
}
}) != null;
}
private void restart(long timeout) {
JobScheduler.getScheduler().schedule(this, timeout, TimeUnit.SECONDS);
}
}, 1, TimeUnit.SECONDS);
}
}