• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devonfw / IDEasy / 13631164443

03 Mar 2025 12:42PM UTC coverage: 68.505% (+0.3%) from 68.251%
13631164443

Pull #1085

github

web-flow
Merge 9829c710e into 991139853
Pull Request #1085: #654: improved plugin suppport

3066 of 4919 branches covered (62.33%)

Branch coverage included in aggregate %.

7927 of 11128 relevant lines covered (71.23%)

3.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.79
cli/src/main/java/com/devonfw/tools/ide/tool/ide/IdeaBasedIdeToolCommandlet.java
1
package com.devonfw.tools.ide.tool.ide;
2

3
import java.io.IOException;
4
import java.net.HttpURLConnection;
5
import java.net.URL;
6
import java.net.URLEncoder;
7
import java.nio.charset.StandardCharsets;
8
import java.nio.file.Files;
9
import java.nio.file.Path;
10
import java.util.ArrayList;
11
import java.util.Arrays;
12
import java.util.List;
13
import java.util.Set;
14

15
import com.devonfw.tools.ide.common.Tag;
16
import com.devonfw.tools.ide.context.IdeContext;
17
import com.devonfw.tools.ide.io.FileAccess;
18
import com.devonfw.tools.ide.os.MacOsHelper;
19
import com.devonfw.tools.ide.process.ProcessContext;
20
import com.devonfw.tools.ide.step.Step;
21
import com.devonfw.tools.ide.tool.plugin.ToolPluginDescriptor;
22

23
public class IdeaBasedIdeToolCommandlet extends IdeToolCommandlet {
24

25
  private static final String BUILD_FILE = "build.txt";
26

27
  /**
28
   * The constructor.
29
   *
30
   * @param context the {@link IdeContext}.
31
   * @param tool the {@link #getName() tool name}.
32
   * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} method.
33
   */
34
  public IdeaBasedIdeToolCommandlet(IdeContext context, String tool, Set<Tag> tags) {
35
    super(context, tool, tags);
5✔
36
  }
1✔
37

38
  @Override
39
  // TODO: Check if this is still needed, because Intellij is overriding this already and using a different approach
40
  public void installPlugin(ToolPluginDescriptor plugin, Step step, ProcessContext pc) {
41
    String downloadUrl = getDownloadUrl(plugin);
4✔
42

43
    String pluginId = plugin.id();
3✔
44

45
    Path tmpDir = null;
2✔
46

47
    try {
48
      Path installationPath = this.getPluginsInstallationPath();
3✔
49
      ensureInstallationPathExists(installationPath);
3✔
50

51
      FileAccess fileAccess = context.getFileAccess();
4✔
52
      tmpDir = fileAccess.createTempDir(pluginId);
4✔
53

54
      Path downloadedFile = downloadPlugin(fileAccess, downloadUrl, tmpDir, pluginId);
7✔
55
      extractDownloadedPlugin(fileAccess, downloadedFile, pluginId);
5✔
56

57
      step.success();
2✔
58
    } catch (IOException e) {
×
59
      step.error(e);
×
60
      throw new IllegalStateException("Failed to process installation of plugin: " + pluginId, e);
×
61
    } finally {
62
      if (tmpDir != null) {
2!
63
        context.getFileAccess().delete(tmpDir);
5✔
64
      }
65
    }
66
  }
1✔
67

68
  @Override
69
  public void runTool(String... args) {
70
    List<String> extendedArgs = new ArrayList<>(Arrays.asList(args));
6✔
71
    extendedArgs.add(this.context.getWorkspacePath().toString());
7✔
72
    super.runTool(extendedArgs.toArray(new String[0]));
7✔
73
  }
1✔
74

75
  /**
76
   * @param plugin the {@link ToolPluginDescriptor} to be installer
77
   * @return a {@link String} representing the download URL.
78
   */
79
  private String getDownloadUrl(ToolPluginDescriptor plugin) {
80
    String downloadUrl = plugin.url();
3✔
81
    String pluginId = URLEncoder.encode(plugin.id(), StandardCharsets.UTF_8).replaceAll("\\+", "%20");
8✔
82

83
    String buildVersion = readBuildVersion();
3✔
84

85
    if (downloadUrl == null || downloadUrl.isEmpty()) {
5!
86
      downloadUrl = String.format("https://plugins.jetbrains.com/pluginManager?action=download&id=%s&build=%s", pluginId, buildVersion);
×
87
    }
88
    return downloadUrl;
2✔
89
  }
90

91
  private String readBuildVersion() {
92
    Path buildFile = this.getToolPath().resolve(BUILD_FILE);
5✔
93
    if (context.getSystemInfo().isMac()) {
5✔
94
      MacOsHelper macOsHelper = new MacOsHelper(context);
6✔
95
      Path appPath = macOsHelper.findAppDir(macOsHelper.findRootToolPath(this, context));
8✔
96
      buildFile = appPath.resolve("Contents/Resources").resolve(BUILD_FILE);
6✔
97
    }
98
    try {
99
      return Files.readString(buildFile);
3✔
100
    } catch (IOException e) {
×
101
      throw new IllegalStateException("Failed to read " + this.getName() + " build version: " + buildFile, e);
×
102
    }
103
  }
104

105
  private void ensureInstallationPathExists(Path installationPath) throws IOException {
106
    if (!Files.exists(installationPath)) {
5!
107
      try {
108
        Files.createDirectories(installationPath);
×
109
      } catch (IOException e) {
×
110
        throw new IllegalStateException("Failed to create directory " + installationPath, e);
×
111
      }
×
112
    }
113
  }
1✔
114

115
  private Path downloadPlugin(FileAccess fileAccess, String downloadUrl, Path tmpDir, String pluginId) throws IOException {
116
    String extension = getFileExtensionFromUrl(downloadUrl);
4✔
117
    if (extension.isEmpty()) {
3!
118
      throw new IllegalStateException("Unknown file type for URL: " + downloadUrl);
×
119
    }
120
    String fileName = String.format("%s-plugin-%s%s", this.getName(), pluginId, extension);
18✔
121
    Path downloadedFile = tmpDir.resolve(fileName);
4✔
122
    fileAccess.download(downloadUrl, downloadedFile);
4✔
123
    return downloadedFile;
2✔
124
  }
125

126
  private void extractDownloadedPlugin(FileAccess fileAccess, Path downloadedFile, String pluginId) throws IOException {
127
    Path targetDir = this.getPluginsInstallationPath().resolve(pluginId);
5✔
128
    if (Files.exists(targetDir)) {
5!
129
      context.info("Plugin already installed, target directory already existing: {}", targetDir);
×
130
    } else {
131
      fileAccess.extract(downloadedFile, targetDir);
4✔
132
    }
133
  }
1✔
134

135
  private String getFileExtensionFromUrl(String urlString) throws IOException {
136
    URL url = new URL(urlString);
5✔
137
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
4✔
138
    connection.setRequestMethod("HEAD");
3✔
139
    connection.connect();
2✔
140

141
    int responseCode = connection.getResponseCode();
3✔
142
    if (responseCode != HttpURLConnection.HTTP_OK) {
3!
143
      throw new IOException("Failed to fetch file headers: HTTP " + responseCode);
×
144
    }
145

146
    String contentType = connection.getContentType();
3✔
147
    if (contentType == null) {
2!
148
      return "";
×
149
    }
150
    return switch (contentType) {
9!
151
      case "application/zip" -> ".zip";
×
152
      case "application/java-archive" -> ".jar";
2✔
153
      default -> "";
×
154
    };
155
  }
156

157
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc