Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions java/j2ee.persistence/nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,7 @@
<friend>io.github.jeddict.rest.generator</friend>
<friend>io.github.jeddict.mvc.generator</friend>
<friend>io.github.jeddict.infra</friend>
<friend>org.netbeans.modules.java.lsp.server</friend>
<package>org.netbeans.modules.j2ee.persistence.action</package>
<package>org.netbeans.modules.j2ee.persistence.api.entity.generator</package>
<package>org.netbeans.modules.j2ee.persistence.dd</package>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,6 @@
<folder name="XML">
<file name="XMLCatalog.xml_hidden"/>
</folder>
<folder name="Persistence_hidden"/>
</folder>
</filesystem>
8 changes: 8 additions & 0 deletions java/java.lsp.server/nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,14 @@
<specification-version>9.14</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.j2ee.persistence</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>1.69</specification-version>
</run-dependency>
</dependency>
</module-dependencies>
<test-dependencies>
<test-type>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.netbeans.modules.java.lsp.server.db;

import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.MessageParams;
import org.eclipse.lsp4j.MessageType;
import org.netbeans.api.db.explorer.ConnectionManager;
import org.netbeans.api.db.explorer.DatabaseConnection;
import org.netbeans.api.db.explorer.DatabaseException;
import org.netbeans.api.java.project.JavaProjectConstants;
import org.netbeans.api.progress.aggregate.BasicAggregateProgressFactory;
import org.netbeans.api.progress.aggregate.ProgressContributor;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;

import org.netbeans.modules.j2ee.persistence.api.entity.generator.EntitiesFromDBGenerator;
import org.netbeans.modules.java.lsp.server.LspServerState;
import org.netbeans.modules.java.lsp.server.protocol.CodeActionsProvider;
import org.netbeans.modules.java.lsp.server.protocol.NbCodeLanguageClient;
import org.netbeans.modules.java.lsp.server.protocol.QuickPickItem;
import org.netbeans.modules.java.lsp.server.protocol.ShowInputBoxParams;
import org.netbeans.modules.java.lsp.server.protocol.ShowQuickPickParams;
import org.netbeans.modules.parsing.api.ResultIterator;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;

/**
*
* @author Jan Horvath
*/
@NbBundle.Messages({
"MSG_NoProject=No Project Open",
"MSG_NoDbConn=No DB Connection",
"MSG_NoSourceRoot=No source root found",
"MSG_SelectTables=Select Database Tables",
"MSG_EnterPackageName=Enter package name"
})
@ServiceProvider(service = CodeActionsProvider.class)
public class DBEntityFromTables extends CodeActionsProvider {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not like reusing CodeActionsProvider for registering simple Command providers. Consider creating a new CommandProvider interface/abstract class for such purpose. The original CodeActionsProvider should extend/implement it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we talked about that, but I didn't get to that refactoring. So far it is not a public API ... I'd suggest to fire a JIRA issue for the refactoring & refactor all such usages (there are more of them) in a single refactoring PR. I volunteer :)


private static final String COMMAND_ENTITY_FROM_TABLES = "db.entity.from.tables"; //NOI18N

@Override
public CompletableFuture<Object> processCommand(NbCodeLanguageClient client, String command, List<Object> arguments) {
if (!COMMAND_ENTITY_FROM_TABLES.equals(command)) {
return null;
}
Lookup.getDefault().lookup(LspServerState.class).openedProjects().thenAccept((projects) -> {
if (projects.length > 0) {
createEntityClassesInProject(client, projects[0]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: if the command is invoked with an active editor focused: shouldn't we create the classes in the editor's project instead in the 1st opened one ? The order of projects in openedProjects may be rather random.
Of course provided that the LSP command arguments contain some URI etc to begin with.

} else {
client.showMessage(new MessageParams(MessageType.Error, Bundle.MSG_NoProject()));
}

});
return null;
}

private CompletableFuture createEntityClassesInProject(NbCodeLanguageClient client, Project prj) {
try {
DatabaseConnection connection = ConnectionManager.getDefault().getPreferredConnection(true);
if (connection == null) {
client.showMessage(new MessageParams(MessageType.Error, Bundle.MSG_NoDbConn()));
return null;
}
ConnectionManager.getDefault().connect(connection);
SourceGroup[] sr = ProjectUtils.getSources(prj).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
if (sr.length < 1) {
client.showMessage(new MessageParams(MessageType.Error, Bundle.MSG_NoSourceRoot()));
return null;
}
Connection conn = connection.getJDBCConnection();
ResultSet rs = conn.getMetaData().getTables(conn.getCatalog(), conn.getSchema(), "%", new String[]{"TABLE", "VIEW"}); //NOI18N
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This call eventually waits for the data from the JDBC connection; I'd suggest to run the DB query in a RequesProcessor so the LSP communication is not blocked. The behaviour depends on the DB location / network conditions.

List<QuickPickItem> dbItems = new ArrayList<>();
while (rs.next()) {
dbItems.add(new QuickPickItem(rs.getString("TABLE_NAME"))); //NOI18N
}
return client.showQuickPick(new ShowQuickPickParams(Bundle.MSG_SelectTables(), true, dbItems))
.thenApply(items -> items.stream().map(item -> item.getLabel()).collect(Collectors.toList()))
.thenAccept(tables -> {
client.showInputBox(new ShowInputBoxParams(Bundle.MSG_EnterPackageName(), "")) //NOI18N
.thenAccept(packageName -> {
EntitiesFromDBGenerator generator = new EntitiesFromDBGenerator(tables, true, packageName, sr[0], connection, prj, null);
ProgressContributor pc = BasicAggregateProgressFactory.createProgressContributor("entity"); //NOI18N
try {
generator.generate(pc);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also potentially run inside LSP event loop (input box response is sent as a protocol response to the server). Please check and if so, run asynchronously. The ProgressContributor indicates that the code called runs for some time and reports progress - that won't work when LSP connection is blocked.

} catch (SQLException | IOException ex) {
client.showMessage(new MessageParams(MessageType.Error, ex.getMessage()));
}
});
return;
});
} catch (SQLException | IllegalArgumentException | DatabaseException ex) {
System.out.println(ex.getMessage());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this client.showMessage instead, or use NotifyDescriptor + DialogDisplayer.getDefault().notify() that will be automagically remoted to the client.

}
return null;
}

@Override
public List<CodeAction> getCodeActions(ResultIterator resultIterator, CodeActionParams params) throws Exception {
return Collections.emptyList();
}

@Override
public Set<String> getCommands() {
return Collections.singleton(COMMAND_ENTITY_FROM_TABLES);
}

}
5 changes: 5 additions & 0 deletions java/java.lsp.server/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,11 @@
"title": "Add Database Connection",
"category": "Database"
},
{
"command": "db.entity.from.tables",
"title": "Generate Entity Classes from DB",
"category": "Database"
},
{
"command": "nbls:Database:netbeans.db.explorer.action.Connect",
"title": "Connect to Database"
Expand Down