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

devonfw / IDEasy / 13772747296

10 Mar 2025 07:05PM UTC coverage: 68.619% (+0.1%) from 68.471%
13772747296

Pull #1085

github

web-flow
Merge 390a610b0 into 9ba4d3cb7
Pull Request #1085: #654: improved plugin suppport

3067 of 4915 branches covered (62.4%)

Branch coverage included in aggregate %.

7934 of 11117 relevant lines covered (71.37%)

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
/**
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 boolean 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
      return true;
4✔
63
    } catch (IOException e) {
×
64
      step.error(e);
×
65
      throw new IllegalStateException("Failed to process installation of plugin: " + pluginId, e);
×
66
    } finally {
67
      if (tmpDir != null) {
2!
68
        context.getFileAccess().delete(tmpDir);
5✔
69
      }
70
    }
71
  }
72

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

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

88
    String buildVersion = readBuildVersion();
3✔
89

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

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

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

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

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

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

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

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

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

© 2025 Coveralls, Inc