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

devonfw / IDEasy / 13656780447

04 Mar 2025 03:11PM UTC coverage: 68.471% (+0.2%) from 68.253%
13656780447

Pull #1085

github

web-flow
Merge 325976934 into ddb6d002d
Pull Request #1085: #654: improved plugin suppport

3065 of 4919 branches covered (62.31%)

Branch coverage included in aggregate %.

7926 of 11133 relevant lines covered (71.19%)

3.1 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
/**
24
 * {@link IdeToolCommandlet} for IDEA based commandlets like: {@link com.devonfw.tools.ide.tool.intellij.Intellij IntelliJ} and
25
 * {@link com.devonfw.tools.ide.tool.androidstudio.AndroidStudio Android Studio}.
26
 */
27
public class IdeaBasedIdeToolCommandlet extends IdeToolCommandlet {
28

29
  private static final String BUILD_FILE = "build.txt";
30

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

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

47
    String pluginId = plugin.id();
3✔
48

49
    Path tmpDir = null;
2✔
50

51
    try {
52
      Path installationPath = this.getPluginsInstallationPath();
3✔
53
      ensureInstallationPathExists(installationPath);
3✔
54

55
      FileAccess fileAccess = context.getFileAccess();
4✔
56
      tmpDir = fileAccess.createTempDir(pluginId);
4✔
57

58
      Path downloadedFile = downloadPlugin(fileAccess, downloadUrl, tmpDir, pluginId);
7✔
59
      extractDownloadedPlugin(fileAccess, downloadedFile, pluginId);
5✔
60

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

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

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

87
    String buildVersion = readBuildVersion();
3✔
88

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

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

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

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

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

139
  private String getFileExtensionFromUrl(String urlString) throws IOException {
140
    URL url = new URL(urlString);
5✔
141
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
4✔
142
    connection.setRequestMethod("HEAD");
3✔
143
    connection.connect();
2✔
144

145
    int responseCode = connection.getResponseCode();
3✔
146
    if (responseCode != HttpURLConnection.HTTP_OK) {
3!
147
      throw new IOException("Failed to fetch file headers: HTTP " + responseCode);
×
148
    }
149

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

161
}
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