| /* ========================================================================== |
| * Copyright 2006 Mevenide Team |
| * |
| * 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 org.jetbrains.idea.maven.execution; |
| |
| import com.intellij.execution.ExecutionException; |
| import com.intellij.execution.RunManager; |
| import com.intellij.execution.configurations.JavaParameters; |
| import com.intellij.execution.configurations.ParametersList; |
| import com.intellij.execution.impl.EditConfigurationsDialog; |
| import com.intellij.execution.impl.RunManagerImpl; |
| import com.intellij.notification.Notification; |
| import com.intellij.notification.NotificationListener; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.PathManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleManager; |
| import com.intellij.openapi.options.ShowSettingsUtil; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.projectRoots.JavaSdk; |
| import com.intellij.openapi.projectRoots.JavaSdkType; |
| import com.intellij.openapi.projectRoots.ProjectJdkTable; |
| import com.intellij.openapi.projectRoots.Sdk; |
| import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; |
| import com.intellij.openapi.roots.ModuleRootManager; |
| import com.intellij.openapi.roots.ProjectRootManager; |
| import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.encoding.EncodingManager; |
| import com.intellij.openapi.vfs.encoding.EncodingProjectManager; |
| import com.intellij.util.PathUtil; |
| import com.intellij.util.io.ZipUtil; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.idea.maven.artifactResolver.MavenArtifactResolvedM2RtMarker; |
| import org.jetbrains.idea.maven.artifactResolver.MavenArtifactResolvedM31RtMarker; |
| import org.jetbrains.idea.maven.artifactResolver.MavenArtifactResolvedM3RtMarker; |
| import org.jetbrains.idea.maven.artifactResolver.common.MavenModuleMap; |
| import org.jetbrains.idea.maven.project.MavenGeneralSettings; |
| import org.jetbrains.idea.maven.project.MavenProject; |
| import org.jetbrains.idea.maven.project.MavenProjectsManager; |
| import org.jetbrains.idea.maven.utils.MavenSettings; |
| import org.jetbrains.idea.maven.utils.MavenUtil; |
| |
| import javax.swing.event.HyperlinkEvent; |
| import javax.swing.event.HyperlinkListener; |
| import java.io.*; |
| import java.util.*; |
| import java.util.zip.ZipOutputStream; |
| |
| /** |
| * @author Ralf Quebbemann |
| */ |
| public class MavenExternalParameters { |
| |
| private static final Logger LOG = Logger.getInstance(MavenExternalParameters.class); |
| |
| public static final String MAVEN_LAUNCHER_CLASS = "org.codehaus.classworlds.Launcher"; |
| |
| @NonNls private static final String MAVEN_OPTS = "MAVEN_OPTS"; |
| |
| @Deprecated // Use createJavaParameters(Project,MavenRunnerParameters, MavenGeneralSettings,MavenRunnerSettings,MavenRunConfiguration) |
| public static JavaParameters createJavaParameters(@Nullable final Project project, |
| @NotNull final MavenRunnerParameters parameters, |
| @Nullable MavenGeneralSettings coreSettings, |
| @Nullable MavenRunnerSettings runnerSettings) throws ExecutionException { |
| return createJavaParameters(project, parameters, coreSettings, runnerSettings, null); |
| } |
| |
| public static JavaParameters createJavaParameters(@Nullable final Project project, |
| @NotNull final MavenRunnerParameters parameters) throws ExecutionException { |
| return createJavaParameters(project, parameters, null, null, null); |
| } |
| |
| /** |
| * |
| * @param project |
| * @param parameters |
| * @param coreSettings |
| * @param runnerSettings |
| * @param runConfiguration used to creation fix if maven home not found |
| * @return |
| * @throws ExecutionException |
| */ |
| public static JavaParameters createJavaParameters(@Nullable final Project project, |
| @NotNull final MavenRunnerParameters parameters, |
| @Nullable MavenGeneralSettings coreSettings, |
| @Nullable MavenRunnerSettings runnerSettings, |
| @Nullable MavenRunConfiguration runConfiguration) throws ExecutionException { |
| final JavaParameters params = new JavaParameters(); |
| |
| ApplicationManager.getApplication().assertReadAccessAllowed(); |
| |
| if (coreSettings == null) { |
| coreSettings = project == null ? new MavenGeneralSettings() : MavenProjectsManager.getInstance(project).getGeneralSettings(); |
| } |
| if (runnerSettings == null) { |
| runnerSettings = project == null ? new MavenRunnerSettings() : MavenRunner.getInstance(project).getState(); |
| } |
| |
| params.setWorkingDirectory(parameters.getWorkingDirFile()); |
| |
| params.setJdk(getJdk(project, runnerSettings, project != null && MavenRunner.getInstance(project).getState() == runnerSettings)); |
| |
| final String mavenHome = resolveMavenHome(coreSettings, project, runConfiguration); |
| |
| params.getProgramParametersList().add("-Didea.version=" + MavenUtil.getIdeaVersionToPassToMavenProcess()); |
| |
| addVMParameters(params.getVMParametersList(), mavenHome, runnerSettings); |
| |
| File confFile = MavenUtil.getMavenConfFile(new File(mavenHome)); |
| if (!confFile.isFile()) { |
| throw new ExecutionException("Configuration file is not exists in maven home: " + confFile.getAbsolutePath()); |
| } |
| |
| if (project != null && parameters.isResolveToWorkspace()) { |
| try { |
| String resolverJar = getArtifactResolverJar(MavenUtil.getMavenVersion(mavenHome)); |
| confFile = patchConfFile(confFile, resolverJar); |
| |
| File modulesPathsFile = dumpModulesPaths(project); |
| params.getVMParametersList().addProperty(MavenModuleMap.PATHS_FILE_PROPERTY, modulesPathsFile.getAbsolutePath()); |
| } |
| catch (IOException e) { |
| LOG.error(e); |
| throw new ExecutionException("Failed to run maven configuration", e); |
| } |
| } |
| |
| params.getVMParametersList().addProperty("classworlds.conf", confFile.getPath()); |
| |
| for (String path : getMavenClasspathEntries(mavenHome)) { |
| params.getClassPath().add(path); |
| } |
| |
| params.setEnv(new HashMap<String, String>(runnerSettings.getEnvironmentProperties())); |
| params.setPassParentEnvs(runnerSettings.isPassParentEnv()); |
| |
| params.setMainClass(MAVEN_LAUNCHER_CLASS); |
| EncodingManager encodingManager = project == null |
| ? EncodingProjectManager.getInstance() |
| : EncodingProjectManager.getInstance(project); |
| params.setCharset(encodingManager.getDefaultCharset()); |
| |
| addMavenParameters(params.getProgramParametersList(), mavenHome, coreSettings, runnerSettings, parameters); |
| |
| return params; |
| } |
| |
| private static File patchConfFile(File conf, String library) throws IOException { |
| File tmpConf = File.createTempFile("idea-", "-mvn.conf"); |
| tmpConf.deleteOnExit(); |
| patchConfFile(conf, tmpConf, library); |
| |
| return tmpConf; |
| } |
| |
| private static void patchConfFile(File originalConf, File dest, String library) throws IOException { |
| Scanner sc = new Scanner(originalConf); |
| |
| try { |
| BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dest))); |
| |
| try { |
| boolean patched = false; |
| |
| while (sc.hasNextLine()) { |
| String line = sc.nextLine(); |
| |
| out.append(line); |
| out.newLine(); |
| |
| if (!patched && "[plexus.core]".equals(line)) { |
| out.append("load ").append(library); |
| out.newLine(); |
| |
| patched = true; |
| } |
| } |
| } |
| finally { |
| out.close(); |
| } |
| } |
| finally { |
| sc.close(); |
| } |
| } |
| |
| private static String getArtifactResolverJar(@Nullable String mavenVersion) throws IOException { |
| boolean isMaven3; |
| Class marker; |
| |
| if (mavenVersion != null && mavenVersion.compareTo("3.1.0") >= 0) { |
| isMaven3 = true; |
| marker = MavenArtifactResolvedM31RtMarker.class; |
| } |
| else if (mavenVersion != null && mavenVersion.compareTo("3.0.0") >= 0) { |
| isMaven3 = true; |
| marker = MavenArtifactResolvedM3RtMarker.class; |
| } |
| else { |
| isMaven3 = false; |
| marker = MavenArtifactResolvedM2RtMarker.class; |
| } |
| |
| File classDirOrJar = new File(PathUtil.getJarPathForClass(marker)); |
| |
| if (!classDirOrJar.isDirectory()) { |
| return classDirOrJar.getAbsolutePath(); // it's a jar in IDEA installation. |
| } |
| |
| // it's a classes directory, we are in development mode. |
| File tempFile = FileUtil.createTempFile("idea-", "-artifactResolver.jar"); |
| tempFile.deleteOnExit(); |
| |
| ZipOutputStream zipOutput = new ZipOutputStream(new FileOutputStream(tempFile)); |
| try { |
| ZipUtil.addDirToZipRecursively(zipOutput, null, classDirOrJar, "", null, null); |
| |
| if (isMaven3) { |
| File m2Module = new File(PathUtil.getJarPathForClass(MavenModuleMap.class)); |
| |
| String commonClassesPath = MavenModuleMap.class.getPackage().getName().replace('.', '/'); |
| ZipUtil.addDirToZipRecursively(zipOutput, null, new File(m2Module, commonClassesPath), commonClassesPath, null, null); |
| } |
| } finally { |
| zipOutput.close(); |
| } |
| |
| return tempFile.getAbsolutePath(); |
| } |
| |
| private static File dumpModulesPaths(@NotNull Project project) throws IOException { |
| ApplicationManager.getApplication().assertReadAccessAllowed(); |
| |
| Properties res = new Properties(); |
| |
| MavenProjectsManager manager = MavenProjectsManager.getInstance(project); |
| |
| for (Module module : ModuleManager.getInstance(project).getModules()) { |
| if (manager.isMavenizedModule(module)) { |
| MavenProject mavenProject = manager.findProject(module); |
| if (mavenProject != null && !manager.isIgnored(mavenProject)) { |
| res.setProperty(mavenProject.getMavenId().getGroupId() |
| + ':' + mavenProject.getMavenId().getArtifactId() |
| + ":pom" |
| + ':' + mavenProject.getMavenId().getVersion(), |
| mavenProject.getFile().getPath()); |
| |
| res.setProperty(mavenProject.getMavenId().getGroupId() |
| + ':' + mavenProject.getMavenId().getArtifactId() |
| + ":test-jar" |
| + ':' + mavenProject.getMavenId().getVersion(), |
| mavenProject.getTestOutputDirectory()); |
| |
| res.setProperty(mavenProject.getMavenId().getGroupId() |
| + ':' + mavenProject.getMavenId().getArtifactId() |
| + ':' + mavenProject.getPackaging() |
| + ':' + mavenProject.getMavenId().getVersion(), |
| mavenProject.getOutputDirectory()); |
| |
| } |
| } |
| } |
| |
| File file = new File(PathManager.getSystemPath(), "Maven/idea-projects-state-" + project.getLocationHash() + ".properties"); |
| file.getParentFile().mkdirs(); |
| |
| OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); |
| try { |
| res.store(out, null); |
| } |
| finally { |
| out.close(); |
| } |
| |
| return file; |
| } |
| |
| @NotNull |
| private static Sdk getJdk(@Nullable Project project, MavenRunnerSettings runnerSettings, boolean isGlobalRunnerSettings) throws ExecutionException { |
| String name = runnerSettings.getJreName(); |
| if (name.equals(MavenRunnerSettings.USE_INTERNAL_JAVA)) { |
| return JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk(); |
| } |
| |
| if (name.equals(MavenRunnerSettings.USE_PROJECT_JDK)) { |
| if (project != null) { |
| Sdk res = ProjectRootManager.getInstance(project).getProjectSdk(); |
| if (res != null) { |
| return res; |
| } |
| Module[] modules = ModuleManager.getInstance(project).getModules(); |
| for (Module module : modules) { |
| Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); |
| if (sdk != null && sdk.getSdkType() instanceof JavaSdkType) { |
| return sdk; |
| } |
| } |
| } |
| |
| if (project == null) { |
| Sdk recent = ProjectJdkTable.getInstance().findMostRecentSdkOfType(JavaSdk.getInstance()); |
| if (recent != null) return recent; |
| return JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk(); |
| } |
| |
| throw new ProjectJdkSettingsOpenerExecutionException("Project JDK is not specified. <a href=''>Configure</a>", project); |
| } |
| |
| if (name.equals(MavenRunnerSettings.USE_JAVA_HOME)) { |
| final String javaHome = System.getenv("JAVA_HOME"); |
| if (StringUtil.isEmptyOrSpaces(javaHome)) { |
| throw new ExecutionException(RunnerBundle.message("maven.java.home.undefined")); |
| } |
| final Sdk jdk = JavaSdk.getInstance().createJdk("", javaHome); |
| if (jdk == null) { |
| throw new ExecutionException(RunnerBundle.message("maven.java.home.invalid", javaHome)); |
| } |
| return jdk; |
| } |
| |
| for (Sdk projectJdk : ProjectJdkTable.getInstance().getAllJdks()) { |
| if (projectJdk.getName().equals(name)) { |
| return projectJdk; |
| } |
| } |
| |
| if (isGlobalRunnerSettings) { |
| throw new ExecutionException(RunnerBundle.message("maven.java.not.found.default.config", name)); |
| } |
| else { |
| throw new ExecutionException(RunnerBundle.message("maven.java.not.found", name)); |
| } |
| } |
| |
| public static void addVMParameters(ParametersList parametersList, String mavenHome, MavenRunnerSettings runnerSettings) { |
| parametersList.addParametersString(System.getenv(MAVEN_OPTS)); |
| |
| parametersList.addParametersString(runnerSettings.getVmOptions()); |
| |
| parametersList.addProperty("maven.home", mavenHome); |
| } |
| |
| private static void addMavenParameters(ParametersList parametersList, |
| String mavenHome, |
| MavenGeneralSettings coreSettings, |
| MavenRunnerSettings runnerSettings, |
| MavenRunnerParameters parameters) { |
| encodeCoreAndRunnerSettings(coreSettings, mavenHome, parametersList); |
| |
| if (runnerSettings.isSkipTests()) { |
| parametersList.addProperty("skipTests", "true"); |
| } |
| |
| for (Map.Entry<String, String> entry : runnerSettings.getMavenProperties().entrySet()) { |
| if (entry.getKey().length() > 0) { |
| parametersList.addProperty(entry.getKey(), entry.getValue()); |
| } |
| } |
| |
| for (String goal : parameters.getGoals()) { |
| parametersList.add(goal); |
| } |
| |
| addOption(parametersList, "P", encodeProfiles(parameters.getProfilesMap())); |
| } |
| |
| private static void addOption(ParametersList cmdList, @NonNls String key, @NonNls String value) { |
| if (!StringUtil.isEmptyOrSpaces(value)) { |
| cmdList.add("-" + key); |
| cmdList.add(value); |
| } |
| } |
| |
| public static String resolveMavenHome(@NotNull MavenGeneralSettings coreSettings) throws ExecutionException { |
| return resolveMavenHome(coreSettings, null, null); |
| } |
| |
| /** |
| * |
| * @param coreSettings |
| * @param project used to creation fix if maven home not found |
| * @param runConfiguration used to creation fix if maven home not found |
| * @return |
| * @throws ExecutionException |
| */ |
| public static String resolveMavenHome(@NotNull MavenGeneralSettings coreSettings, |
| @Nullable Project project, |
| @Nullable MavenRunConfiguration runConfiguration) throws ExecutionException { |
| final File file = MavenUtil.resolveMavenHomeDirectory(coreSettings.getMavenHome()); |
| |
| if (file == null) { |
| throw createExecutionException(RunnerBundle.message("external.maven.home.no.default"), |
| RunnerBundle.message("external.maven.home.no.default.with.fix"), |
| coreSettings, project, runConfiguration); |
| } |
| |
| if (!file.exists()) { |
| throw createExecutionException(RunnerBundle.message("external.maven.home.does.not.exist", file.getPath()), |
| RunnerBundle.message("external.maven.home.does.not.exist.with.fix", file.getPath()), |
| coreSettings, project, runConfiguration); |
| } |
| |
| if (!MavenUtil.isValidMavenHome(file)) { |
| throw createExecutionException(RunnerBundle.message("external.maven.home.invalid", file.getPath()), |
| RunnerBundle.message("external.maven.home.invalid.with.fix", file.getPath()), |
| coreSettings, project, runConfiguration); |
| } |
| |
| try { |
| return file.getCanonicalPath(); |
| } |
| catch (IOException e) { |
| throw new ExecutionException(e.getMessage(), e); |
| } |
| } |
| |
| private static ExecutionException createExecutionException(String text, |
| String textWithFix, |
| @NotNull MavenGeneralSettings coreSettings, |
| @Nullable Project project, |
| @Nullable MavenRunConfiguration runConfiguration) { |
| Project notNullProject = project; |
| if (notNullProject == null) { |
| if (runConfiguration == null) return new ExecutionException(text); |
| notNullProject = runConfiguration.getProject(); |
| if (notNullProject == null) return new ExecutionException(text); |
| } |
| |
| if (coreSettings == MavenProjectsManager.getInstance(notNullProject).getGeneralSettings()) { |
| return new ProjectSettingsOpenerExecutionException(textWithFix, notNullProject); |
| } |
| |
| if (runConfiguration != null) { |
| Project runCfgProject = runConfiguration.getProject(); |
| if (runCfgProject != null) { |
| if (((RunManagerImpl)RunManager.getInstance(runCfgProject)).getSettings(runConfiguration) != null) { |
| return new RunConfigurationOpenerExecutionException(textWithFix, runConfiguration); |
| } |
| } |
| } |
| |
| return new ExecutionException(text); |
| } |
| |
| @SuppressWarnings({"HardCodedStringLiteral"}) |
| private static List<String> getMavenClasspathEntries(final String mavenHome) { |
| File mavenHomeBootAsFile = new File(new File(mavenHome, "core"), "boot"); |
| // if the dir "core/boot" does not exist we are using a Maven version > 2.0.5 |
| // in this case the classpath must be constructed from the dir "boot" |
| if (!mavenHomeBootAsFile.exists()) { |
| mavenHomeBootAsFile = new File(mavenHome, "boot"); |
| } |
| |
| List<String> classpathEntries = new ArrayList<String>(); |
| |
| File[] files = mavenHomeBootAsFile.listFiles(); |
| if (files != null) { |
| for (File file : files) { |
| if (file.getName().contains("classworlds")) { |
| classpathEntries.add(file.getAbsolutePath()); |
| } |
| } |
| } |
| |
| return classpathEntries; |
| } |
| |
| private static void encodeCoreAndRunnerSettings(MavenGeneralSettings coreSettings, String mavenHome, |
| ParametersList cmdList) { |
| if (coreSettings.isWorkOffline()) { |
| cmdList.add("--offline"); |
| } |
| |
| boolean atLeastMaven3 = MavenUtil.isMaven3(mavenHome); |
| |
| if (!atLeastMaven3) { |
| addIfNotEmpty(cmdList, coreSettings.getPluginUpdatePolicy().getCommandLineOption()); |
| |
| if (!coreSettings.isUsePluginRegistry()) { |
| cmdList.add("--no-plugin-registry"); |
| } |
| } |
| |
| if (coreSettings.getOutputLevel() == MavenExecutionOptions.LoggingLevel.DEBUG) { |
| cmdList.add("--debug"); |
| } |
| if (coreSettings.isNonRecursive()) { |
| cmdList.add("--non-recursive"); |
| } |
| if (coreSettings.isPrintErrorStackTraces()) { |
| cmdList.add("--errors"); |
| } |
| |
| if (coreSettings.isAlwaysUpdateSnapshots()) { |
| cmdList.add("--update-snapshots"); |
| } |
| |
| if (StringUtil.isNotEmpty(coreSettings.getThreads())) { |
| cmdList.add("-T", coreSettings.getThreads()); |
| } |
| |
| addIfNotEmpty(cmdList, coreSettings.getFailureBehavior().getCommandLineOption()); |
| addIfNotEmpty(cmdList, coreSettings.getChecksumPolicy().getCommandLineOption()); |
| |
| addOption(cmdList, "s", coreSettings.getUserSettingsFile()); |
| if (!StringUtil.isEmptyOrSpaces(coreSettings.getLocalRepository())) { |
| cmdList.addProperty("maven.repo.local", coreSettings.getLocalRepository()); |
| } |
| } |
| |
| private static void addIfNotEmpty(ParametersList parametersList, @Nullable String value) { |
| if (!StringUtil.isEmptyOrSpaces(value)) { |
| parametersList.add(value); |
| } |
| } |
| |
| private static String encodeProfiles(Map<String, Boolean> profiles) { |
| StringBuilder stringBuilder = new StringBuilder(); |
| for (Map.Entry<String, Boolean> entry : profiles.entrySet()) { |
| if (stringBuilder.length() != 0) { |
| stringBuilder.append(","); |
| } |
| if (!entry.getValue()) { |
| stringBuilder.append("!"); |
| } |
| stringBuilder.append(entry.getKey()); |
| } |
| return stringBuilder.toString(); |
| } |
| |
| private static class ProjectSettingsOpenerExecutionException extends ExecutionExceptionWithHyperlink { |
| |
| private final Project myProject; |
| |
| public ProjectSettingsOpenerExecutionException(final String s, Project project) { |
| super(s); |
| myProject = project; |
| } |
| |
| @Override |
| protected void hyperlinkClicked() { |
| ShowSettingsUtil.getInstance().showSettingsDialog(myProject, MavenSettings.DISPLAY_NAME); |
| } |
| } |
| |
| private static class ProjectJdkSettingsOpenerExecutionException extends ExecutionExceptionWithHyperlink { |
| |
| private final Project myProject; |
| |
| public ProjectJdkSettingsOpenerExecutionException(final String s, Project project) { |
| super(s); |
| myProject = project; |
| } |
| |
| @Override |
| protected void hyperlinkClicked() { |
| ProjectSettingsService.getInstance(myProject).openProjectSettings(); |
| } |
| } |
| |
| private static class RunConfigurationOpenerExecutionException extends ExecutionExceptionWithHyperlink { |
| |
| private final MavenRunConfiguration myRunConfiguration; |
| |
| public RunConfigurationOpenerExecutionException(final String s, MavenRunConfiguration runConfiguration) { |
| super(s); |
| myRunConfiguration = runConfiguration; |
| } |
| |
| @Override |
| protected void hyperlinkClicked() { |
| Project project = myRunConfiguration.getProject(); |
| EditConfigurationsDialog dialog = new EditConfigurationsDialog(project); |
| dialog.show(); |
| } |
| } |
| |
| private static abstract class ExecutionExceptionWithHyperlink extends ExecutionException implements HyperlinkListener, NotificationListener { |
| |
| public ExecutionExceptionWithHyperlink(String s) { |
| super(s); |
| } |
| |
| protected abstract void hyperlinkClicked(); |
| |
| @Override |
| public final void hyperlinkUpdate(HyperlinkEvent e) { |
| if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { |
| hyperlinkClicked(); |
| } |
| } |
| |
| @Override |
| public final void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { |
| hyperlinkUpdate(event); |
| } |
| } |
| } |