diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBConnectionProvider.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBConnectionProvider.java index d761d2044a04..22d366619353 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBConnectionProvider.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/db/DBConnectionProvider.java @@ -18,10 +18,27 @@ */ package org.netbeans.modules.java.lsp.server.db; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.AclEntry; +import static java.nio.file.attribute.AclEntryPermission.*; +import java.nio.file.attribute.AclFileAttributeView; +import java.nio.file.attribute.DosFileAttributeView; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFileAttributeView; +import static java.nio.file.attribute.PosixFilePermission.*; +import java.nio.file.attribute.PosixFilePermissions; import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.Set; import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.CodeAction; @@ -31,6 +48,7 @@ import org.netbeans.modules.java.lsp.server.protocol.CodeActionsProvider; import org.netbeans.modules.java.lsp.server.protocol.NbCodeLanguageClient; import org.netbeans.modules.parsing.api.ResultIterator; +import org.openide.util.Exceptions; import org.openide.util.lookup.ServiceProvider; /** @@ -41,6 +59,8 @@ public class DBConnectionProvider extends CodeActionsProvider{ private static final String GET_DB_CONNECTION = "java.db.connection"; //NOI18N + private static final boolean POSIX = FileSystems.getDefault().supportedFileAttributeViews().contains("posix"); // NOI18N + @Override public List getCodeActions(ResultIterator resultIterator, CodeActionParams params) throws Exception { return Collections.emptyList(); @@ -49,20 +69,100 @@ public List getCodeActions(ResultIterator resultIterator, CodeAction @Override public CompletableFuture processCommand(NbCodeLanguageClient client, String command, List arguments) { Map result = new HashMap<> (); + CompletableFuture ret = new CompletableFuture(); + Properties dbProps = new Properties(); DatabaseConnection conn = ConnectionManager.getDefault().getPreferredConnection(true); if (conn != null) { - result.put("DATASOURCES_DEFAULT_URL", conn.getDatabaseURL()); //NOI18N - result.put("DATASOURCES_DEFAULT_USERNAME", conn.getUser()); //NOI18N - result.put("DATASOURCES_DEFAULT_PASSWORD", conn.getPassword()); //NOI18N - result.put("DATASOURCES_DEFAULT_DRIVER_CLASS_NAME", conn.getDriverClass()); //NOI18N - String ocid = (String) conn.getConnectionProperties().get("OCID"); //NOI18N - if (ocid != null && !ocid.isEmpty()) { - result.put("DATASOURCES_DEFAULT_OCID", ocid); //NOI18N + Path temp = null; + + try { + if (POSIX) { + FileAttribute readWritePosix = PosixFilePermissions.asFileAttribute(EnumSet.of(OWNER_READ, OWNER_WRITE)); +// FileAttribute readWritePosix = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rw-------")); + temp = Files.createTempFile("db-", ".properties", readWritePosix); + } else { + temp = Files.createTempFile("db-", ".properties"); + AclFileAttributeView acl = Files.getFileAttributeView(temp, AclFileAttributeView.class); + AclEntry ownerEntry = null; + for(AclEntry e : acl.getAcl()) { + if (e.principal().equals(acl.getOwner())) { + ownerEntry = e; + break; + } + } + if (ownerEntry != null) { + acl.setAcl(Collections.singletonList(ownerEntry)); + } else { + deleteTempFile(temp); + ret.completeExceptionally(new IOException("Owner missing, file:"+temp.toString())); // NOI18N + return ret; + } + } + } catch (IOException ex) { + deleteTempFile(temp); + ret.completeExceptionally(ex); + return ret; + } + + try (Writer writer = new FileWriter(temp.toFile(), Charset.defaultCharset());) { + dbProps.put("datasources.default.url", conn.getDatabaseURL()); //NOI18N + dbProps.put("datasources.default.username", conn.getUser()); //NOI18N + dbProps.put("datasources.default.password", conn.getPassword()); //NOI18N + dbProps.put("datasources.default.driverClassName", conn.getDriverClass()); //NOI18N + String ocid = (String) conn.getConnectionProperties().get("OCID"); //NOI18N + if (ocid != null && !ocid.isEmpty()) { + dbProps.put("datasources.default.ocid", ocid); //NOI18N + } + dbProps.store(writer, ""); + if (POSIX) { + PosixFileAttributeView attribs = Files.getFileAttributeView(temp, PosixFileAttributeView.class); + attribs.setPermissions(EnumSet.of(OWNER_READ)); + } else { + DosFileAttributeView attribs = Files.getFileAttributeView(temp, DosFileAttributeView.class); + attribs.setReadOnly(true); + AclFileAttributeView acl = Files.getFileAttributeView(temp, AclFileAttributeView.class); + AclEntry ownerEntry = null; + if (acl.getAcl().size() != 1) { + ret.completeExceptionally(new IOException("Too many Acls, file:"+temp.toString())); // NOI18N + return ret; + } + for(AclEntry e : acl.getAcl()) { + if (e.principal().equals(acl.getOwner())) { + ownerEntry = e; + break; + } + } + if (ownerEntry != null) { + AclEntry readOnly = AclEntry.newBuilder(ownerEntry).setPermissions(READ_ACL, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_DATA, READ_NAMED_ATTRS, DELETE, SYNCHRONIZE).build(); + acl.setAcl(Collections.singletonList(readOnly)); + } else { + deleteTempFile(temp); + ret.completeExceptionally(new IOException("Owner missing, file:"+temp.toString())); // NOI18N + return ret; + } + } + temp.toFile().deleteOnExit(); + result.put("MICRONAUT_CONFIG_FILES", temp.toAbsolutePath().toString()); + } catch (IOException ex) { + deleteTempFile(temp); + ret.completeExceptionally(ex); + return ret; } } - return CompletableFuture.completedFuture(result); + ret.complete(result); + return ret; + } + + private void deleteTempFile(Path temp) { + if (temp != null) { + try { + Files.deleteIfExists(temp); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } } @Override diff --git a/java/java.lsp.server/vscode/src/dbConfigurationProvider.ts b/java/java.lsp.server/vscode/src/dbConfigurationProvider.ts index 361a6c795474..74fcca86c076 100644 --- a/java/java.lsp.server/vscode/src/dbConfigurationProvider.ts +++ b/java/java.lsp.server/vscode/src/dbConfigurationProvider.ts @@ -60,4 +60,14 @@ class DBConfigurationProvider implements vscode.DebugConfigurationProvider { } } +export function onDidTerminateSession(session: vscode.DebugSession): any { + const config = session.configuration; + if (config.env) { + const file = config.env["MICRONAUT_CONFIG_FILES"]; + if (file) { + vscode.workspace.fs.delete(vscode.Uri.file(file)); + } + } +} + export const dBConfigurationProvider = new DBConfigurationProvider(); \ No newline at end of file diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts index 9f7960efb154..4733e20e1dbf 100644 --- a/java/java.lsp.server/vscode/src/extension.ts +++ b/java/java.lsp.server/vscode/src/extension.ts @@ -56,7 +56,7 @@ import { asRanges, StatusMessageRequest, ShowStatusMessageParams, QuickPickReque import * as launchConfigurations from './launchConfigurations'; import { createTreeViewService, TreeViewService, TreeItemDecorator, Visualizer, CustomizableTreeDataProvider } from './explorer'; import { initializeRunConfiguration, runConfigurationProvider, runConfigurationNodeProvider, configureRunSettings, runConfigurationUpdateAll } from './runConfiguration'; -import { dBConfigurationProvider } from './dbConfigurationProvider'; +import { dBConfigurationProvider, onDidTerminateSession } from './dbConfigurationProvider'; import { TLSSocket } from 'tls'; import { InputStep, MultiStepInput } from './utils'; import { env } from 'process'; @@ -419,6 +419,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('java+', configResolver)); let configNativeResolver = new NetBeansConfigurationNativeResolver(); context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('nativeimage', configNativeResolver)); + context.subscriptions.push(vscode.debug.onDidTerminateDebugSession(((session) => onDidTerminateSession(session)))); let debugDescriptionFactory = new NetBeansDebugAdapterDescriptionFactory(); context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('java+', debugDescriptionFactory));