From 8d76e960bd812d9a8933a4567636b7be4de0dcf9 Mon Sep 17 00:00:00 2001 From: Sergio De Lorenzis Date: Wed, 15 Jan 2025 17:43:32 +0100 Subject: [PATCH 1/6] Load functions and proceduress asynchronously --- .../tableinput/TableInputDialog.java | 101 +++++++++++++++--- .../transforms/tableinput/TableInputMeta.java | 18 ++++ .../hop/ui/core/widget/SQLStyledTextComp.java | 5 + .../hop/ui/core/widget/TextComposite.java | 9 ++ 4 files changed, 116 insertions(+), 17 deletions(-) diff --git a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java index a1c3dabd29..b39a2cf0bd 100644 --- a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java +++ b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java @@ -23,6 +23,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; import org.apache.hop.core.Const; import org.apache.hop.core.Props; import org.apache.hop.core.database.Database; @@ -50,6 +54,7 @@ import org.apache.hop.ui.core.widget.StyledTextComp; import org.apache.hop.ui.core.widget.TextComposite; import org.apache.hop.ui.core.widget.TextVar; +import org.apache.hop.ui.core.widget.highlight.SQLValuesHighlight; import org.apache.hop.ui.pipeline.dialog.PipelinePreviewProgressDialog; import org.apache.hop.ui.pipeline.transform.BaseTransformDialog; import org.apache.hop.ui.util.EnvironmentUtils; @@ -68,6 +73,7 @@ import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; @@ -92,10 +98,19 @@ public class TableInputDialog extends BaseTransformDialog { private Label wlPosition; + private AtomicReference sqlHighlightListener = + new AtomicReference<>(new SQLValuesHighlight(List.of())); + public TableInputDialog( Shell parent, IVariables variables, TableInputMeta transformMeta, PipelineMeta pipelineMeta) { super(parent, variables, transformMeta, pipelineMeta); input = transformMeta; + final ExecutorService executorService = Executors.newFixedThreadPool(5); + pipelineMeta.getDatabases().parallelStream() + .forEach( + db -> { + executorService.submit(() -> fetchKeywords(db)); + }); } @Override @@ -139,7 +154,13 @@ public String open() { wTransformName.setLayoutData(fdTransformName); wConnection = addConnectionLine(shell, wTransformName, input.getConnection(), lsMod); - wConnection.addListener(SWT.Selection, e -> getSqlReservedWords()); + wConnection.addListener( + SWT.Selection, + e -> { + final List k = + input.getKeywordsByConnectionName(wConnection.getText()).orElse(List.of()); + refreshLineStyleListener(k); + }); // Some buttons wOk = new Button(shell, SWT.PUSH); @@ -343,10 +364,9 @@ public void mouseUp(MouseEvent e) { wDataFrom.addListener(SWT.Selection, e -> setFlags()); wDataFrom.addListener(SWT.FocusOut, e -> setFlags()); - final List sqlKeywords = getSqlReservedWords(); - - wSql.addLineStyleListener(sqlKeywords); getData(); + initSQLFormat(); + input.setChanged(changed); BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel()); @@ -354,21 +374,65 @@ public void mouseUp(MouseEvent e) { return transformName; } - private List getSqlReservedWords() { - // Do not search keywords when connection is empty - if (input.getConnection() == null || input.getConnection().isEmpty()) { - return List.of(); + private void initSQLFormat() { + String connectionName = input.getConnection(); + final Optional> keywordsByConnectionName = + input.getKeywordsByConnectionName(connectionName); + if (keywordsByConnectionName.isPresent()) { + refreshLineStyleListener(keywordsByConnectionName.get()); + } else { + refreshLineStyleListener(List.of()); + Executors.newSingleThreadExecutor() + .submit( + () -> { + fetchKeywords(input.getConnection()); + final Optional> optKeywords = + input.getKeywordsByConnectionName(connectionName); + if (optKeywords.isPresent()) { + Display.getDefault().asyncExec(() -> refreshLineStyleListener(optKeywords.get())); + } + }); } + } - // If connection is a variable that can't be resolved - if (variables.resolve(input.getConnection()).startsWith("${")) { - return List.of(); + private synchronized void refreshLineStyleListener(List keywords) { + wSql.removeLineStyleListener(sqlHighlightListener.get()); + sqlHighlightListener.set(new SQLValuesHighlight(keywords)); + wSql.addLineStyleListener(sqlHighlightListener.get()); + wSql.setText(wSql.getText()); + // wSql.setRedraw(true); + } + + // private List getAsyncSqlReservedWords(String connectionName) { + // if (connectionName == null || connectionName.isEmpty()) { + // return new ArrayList<>(); + // } + // if (variables.resolve(connectionName).startsWith("${")) { // COULDN'T resolve variable: skip + // return new ArrayList<>(); + // } + // return input + // .getKeywordsByConnectionName(connectionName) + // .orElseGet( + // () -> { + // Executors.newSingleThreadExecutor().submit(() -> fetchKeywords(connectionName)); + // return List.of(); + // }); + // } + + private void fetchKeywords(String connectionName) { + DatabaseMeta databaseMeta = pipelineMeta.findDatabase(connectionName, variables); + if (databaseMeta != null) { + fetchKeywords(databaseMeta); + } else { + logError("Unable to find database '" + connectionName + "'"); } + } + + private void fetchKeywords(DatabaseMeta databaseMeta) { - DatabaseMeta databaseMeta = pipelineMeta.findDatabase(input.getConnection(), variables); if (databaseMeta == null) { logError("Database connection not found. Proceding without keywords."); - return new ArrayList<>(); + return; } try (Database db = new Database(loggingObject, variables, databaseMeta)) { @@ -376,22 +440,25 @@ private List getSqlReservedWords() { DatabaseMetaData databaseMetaData = db.getDatabaseMetaData(); if (databaseMetaData == null) { logError("Couldn't get database metadata"); - return new ArrayList<>(); + return; } List sqlKeywords = new ArrayList<>(); try { final ResultSet functionsResultSet = databaseMetaData.getFunctions(null, null, null); while (functionsResultSet.next()) { - sqlKeywords.add(functionsResultSet.getString("FUNCTION_NAME")); + String functionName = functionsResultSet.getString("FUNCTION_NAME"); + if (functionName.contains(";")) { + functionName = functionName.substring(0, functionName.indexOf(";")); + } + sqlKeywords.add(functionName); } sqlKeywords.addAll(Arrays.asList(databaseMetaData.getSQLKeywords().split(","))); } catch (SQLException e) { logError("Couldn't extract keywords from database metadata. Proceding without them."); } - return sqlKeywords; + input.putKeywords(databaseMeta.getName(), sqlKeywords); } catch (HopDatabaseException e) { logError("Couldn't extract keywords from database metadata. Proceding without them."); - return List.of(); } } diff --git a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputMeta.java b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputMeta.java index e29588832a..afec669525 100644 --- a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputMeta.java +++ b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputMeta.java @@ -18,6 +18,9 @@ package org.apache.hop.pipeline.transforms.tableinput; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import org.apache.hop.core.CheckResult; import org.apache.hop.core.Const; import org.apache.hop.core.ICheckResult; @@ -88,6 +91,8 @@ public class TableInputMeta extends BaseTransformMeta> keywordsCache = new ConcurrentHashMap<>(); + public TableInputMeta() { super(); } @@ -528,4 +533,17 @@ public ITransformIOMeta getTransformIOMeta() { return ioMeta; } + + public void putKeywords(String name, List sqlKeywords) { + keywordsCache.put(name, sqlKeywords); + } + + public Map> getKeywordsCache() { + return keywordsCache; + } + + public Optional> getKeywordsByConnectionName(String connection) { + final List keywordsList = keywordsCache.get(connection); + return Optional.ofNullable(keywordsList); + } } diff --git a/ui/src/main/java/org/apache/hop/ui/core/widget/SQLStyledTextComp.java b/ui/src/main/java/org/apache/hop/ui/core/widget/SQLStyledTextComp.java index b87c80706c..f37ad7afbd 100644 --- a/ui/src/main/java/org/apache/hop/ui/core/widget/SQLStyledTextComp.java +++ b/ui/src/main/java/org/apache/hop/ui/core/widget/SQLStyledTextComp.java @@ -51,4 +51,9 @@ public void addLineStyleListener() { public void addLineStyleListener(List keywords) { addLineStyleListener(new SqlHighlight(keywords)); } + + @Override + public void removeLineStyleListener(LineStyleListener listener) { + textWidget.removeLineStyleListener(listener); + } } diff --git a/ui/src/main/java/org/apache/hop/ui/core/widget/TextComposite.java b/ui/src/main/java/org/apache/hop/ui/core/widget/TextComposite.java index eff5ea95da..db262bc413 100644 --- a/ui/src/main/java/org/apache/hop/ui/core/widget/TextComposite.java +++ b/ui/src/main/java/org/apache/hop/ui/core/widget/TextComposite.java @@ -26,6 +26,7 @@ import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.events.MenuDetectListener; +import org.eclipse.swt.custom.LineStyleListener; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; @@ -58,6 +59,10 @@ public TextComposite(Composite parent, int style) { public abstract void addLineStyleListener(List keywords); + public void addLineStyleListener(LineStyleListener listener) { + throw new UnsupportedOperationException("Cannot add the line style listener"); + } + public void addLineStyleListener(String scriptEngine) { throw new UnsupportedOperationException("Cannot specify a script engine"); } @@ -342,4 +347,8 @@ protected void buildingStyledTextMenu(Menu popupMenu) { setMenu(popupMenu); } + + public void removeLineStyleListener(LineStyleListener listener) { + throw new UnsupportedOperationException("Cannot remove the listener"); + } } From 5bd132af7ba17d40ecef9dde83e0ac958bfad607 Mon Sep 17 00:00:00 2001 From: Sergio De Lorenzis Date: Wed, 22 Jan 2025 16:56:10 +0100 Subject: [PATCH 2/6] Rebase broke everything --- go.sh | 95 +++++++++++++++++++ .../tableinput/TableInputDialog.java | 8 +- .../hop/ui/core/widget/SQLStyledTextComp.java | 3 +- .../hop/ui/core/widget/StyledTextVar.java | 4 + .../hop/ui/core/widget/TextComposite.java | 8 +- 5 files changed, 109 insertions(+), 9 deletions(-) create mode 100755 go.sh diff --git a/go.sh b/go.sh new file mode 100755 index 0000000000..9456dbe4cb --- /dev/null +++ b/go.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# +# 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. +# + +export HOP_PATH=/Users/sergio/Serasoft/code/xdelox/hop +# export HOP_DEPLOY=/Users/sergio/Serasoft/hops/2.12.0-SNAPSHOT/hop +export HOP_DEPLOY=/Users/sergio/Serasoft/code/xdelox/hop/assemblies/client/target/hop + +# Check if an argument was provided +if [ -n "$1" ] && [ "$1" = "rebuild" ]; then + echo "Building..." + mvn clean install -pl org.apache.hop:hop-core -DskipTests + mvn clean install -pl org.apache.hop:hop-ui -DskipTests + mvn clean install -pl org.apache.hop:hop-engine -DskipTests +# mvn clean install -o -pl org.apache.hop:hop-transform-textfile -DskipTests +# mvn clean install -o -pl org.apache.hop:hop-transform-excel -DskipTests +# mvn clean install -o -pl org.apache.hop:hop-transform-selectvalues -DskipTests + # mvn package -o -pl org.apache.hop:hop-transform-tableinput -DskipTests + mvn clean install -o -pl org.apache.hop:hop-transform-tableinput -DskipTests +# mvn clean install -o -pl org.apache.hop:hop-misc-static-schema -DskipTests +# mvn clean install -o -pl org.apache.hop:hop-transform-javascript -DskipTests +# mvn clean install -o -pl org.apache.hop:hop-transform-script -DskipTests +# mvn clean install -o -pl org.apache.hop:hop-transform-databasejoin -DskipTests +# mvn clean install -o -pl org.apache.hop:hop-transform-sql -DskipTests +# mvn clean install -o -pl org.apache.hop:hop-action-sql -DskipTests +# mvn clean install -o -pl org.apache.hop:hop-action-waitforsql -DskipTests + echo $HOP_PATH + echo $HOP_DEPLOY + + mv $HOP_PATH/core/target/hop-core-2.12.0-SNAPSHOT.zip $HOP_DEPLOY + mv $HOP_PATH/ui/target/hop-ui-2.12.0-SNAPSHOT.zip $HOP_DEPLOY + mv $HOP_PATH/engine/target/hop-engine-2.12.0-SNAPSHOT.zip $HOP_DEPLOY +# mv $HOP_PATH/plugins/transforms/textfile/target/hop-transform-textfile-2.12.0-SNAPSHOT.zip $HOP_DEPLOY +# mv $HOP_PATH/plugins/transforms/textfile/target/hop-transform-excel-2.12.0-SNAPSHOT.zip $HOP_DEPLOY + # mv $HOP_PATH/plugins/misc/static-schema/target/hop-misc-static-schema-2.12.0-SNAPSHOT.zip $HOP_DEPLOY +# mv $HOP_PATH/plugins/transforms/selectvalues/target/hop-transform-selectvalues-2.12.0-SNAPSHOT.zip $HOP_DEPLOY +mv $HOP_PATH/plugins/transforms/tableinput/target/hop-transform-tableinput-2.12.0-SNAPSHOT.zip $HOP_DEPLOY +# mv $HOP_PATH/plugins/transforms/janino/target/hop-transform-janino-2.12.0-SNAPSHOT.zip $HOP_DEPLOY +# mv $HOP_PATH/plugins/transforms/javascript/target/hop-transform-javascript-2.12.0-SNAPSHOT.zip $HOP_DEPLOY +# mv $HOP_PATH/plugins/transforms/sql/target/hop-transform-sql-2.12.0-SNAPSHOT.zip $HOP_DEPLOY +# mv $HOP_PATH/plugins/actions/sql/target/hop-action-sql-2.12.0-SNAPSHOT.zip $HOP_DEPLOY +# mv $HOP_PATH/plugins/actions/sql/target/hop-action-waitforsql-2.12.0-SNAPSHOT.zip $HOP_DEPLOY + cd $HOP_DEPLOY + unzip -o hop-core-2.12.0-SNAPSHOT.zip + unzip -o hop-ui-2.12.0-SNAPSHOT.zip + unzip -o hop-engine-2.12.0-SNAPSHOT.zip +# unzip -o hop-transform-textfile-2.12.0-SNAPSHOT.zip +# unzip -o hop-transform-excel-2.12.0-SNAPSHOT.zip +# unzip -o hop-transform-selectvalues-2.12.0-SNAPSHOT.zip + unzip -o hop-transform-tableinput-2.12.0-SNAPSHOT.zip +# unzip -o hop-misc-static-schema-2.12.0-SNAPSHOT.zip +# unzip -o hop-transform-janino-2.12.0-SNAPSHOT.zip +# unzip -o hop-transform-javascript-2.12.0-SNAPSHOT.zip +# unzip -o hop-transform-script-2.12.0-SNAPSHOT.zip +# unzip -o hop-transform-databasejoin-2.12.0-SNAPSHOT.zip +# unzip -o hop-transform-sql-2.12.0-SNAPSHOT.zip +# unzip -o hop-action-sql-2.12.0-SNAPSHOT.zip +# unzip -o hop-action-waitforsql-2.12.0-SNAPSHOT.zip + +# rm hop-transform-selectvalues-2.12.0-SNAPSHOT.zip + rm hop-transform-tableinput-2.12.0-SNAPSHOT.zip + rm hop-core-2.12.0-SNAPSHOT.zip + rm hop-ui-2.12.0-SNAPSHOT.zip + rm hop-engine-2.12.0-SNAPSHOT.zip +# rm hop-transform-textfile-2.12.0-SNAPSHOT.zip +# rm hop-transform-excel-2.12.0-SNAPSHOT.zip +# rm hop-misc-static-schema-2.12.0-SNAPSHOT.zip +# rm hop-transform-janino-2.12.0-SNAPSHOT.zip +# rm hop-transform-javascript-2.12.0-SNAPSHOT.zip +# rm hop-transform-script-2.12.0-SNAPSHOT.zip +# rm hop-transform-databasejoin-2.12.0-SNAPSHOT.zip +# rm hop-transform-sql-2.12.0-SNAPSHOT.zip +# rm hop-action-sql-2.12.0-SNAPSHOT.zip +# rm hop-action-waitforsql-2.12.0-SNAPSHOT.zip +else + echo "Do nothing for now" +fi + + +$HOP_DEPLOY/hop-gui.sh debug diff --git a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java index b39a2cf0bd..e1786a0d9b 100644 --- a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java +++ b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java @@ -54,7 +54,7 @@ import org.apache.hop.ui.core.widget.StyledTextComp; import org.apache.hop.ui.core.widget.TextComposite; import org.apache.hop.ui.core.widget.TextVar; -import org.apache.hop.ui.core.widget.highlight.SQLValuesHighlight; +import org.apache.hop.ui.core.widget.highlight.SqlHighlight; import org.apache.hop.ui.pipeline.dialog.PipelinePreviewProgressDialog; import org.apache.hop.ui.pipeline.transform.BaseTransformDialog; import org.apache.hop.ui.util.EnvironmentUtils; @@ -98,8 +98,8 @@ public class TableInputDialog extends BaseTransformDialog { private Label wlPosition; - private AtomicReference sqlHighlightListener = - new AtomicReference<>(new SQLValuesHighlight(List.of())); + private AtomicReference sqlHighlightListener = + new AtomicReference<>(new SqlHighlight(List.of())); public TableInputDialog( Shell parent, IVariables variables, TableInputMeta transformMeta, PipelineMeta pipelineMeta) { @@ -397,7 +397,7 @@ private void initSQLFormat() { private synchronized void refreshLineStyleListener(List keywords) { wSql.removeLineStyleListener(sqlHighlightListener.get()); - sqlHighlightListener.set(new SQLValuesHighlight(keywords)); + sqlHighlightListener.set(new SqlHighlight(keywords)); wSql.addLineStyleListener(sqlHighlightListener.get()); wSql.setText(wSql.getText()); // wSql.setRedraw(true); diff --git a/ui/src/main/java/org/apache/hop/ui/core/widget/SQLStyledTextComp.java b/ui/src/main/java/org/apache/hop/ui/core/widget/SQLStyledTextComp.java index f37ad7afbd..1830085a66 100644 --- a/ui/src/main/java/org/apache/hop/ui/core/widget/SQLStyledTextComp.java +++ b/ui/src/main/java/org/apache/hop/ui/core/widget/SQLStyledTextComp.java @@ -20,6 +20,7 @@ import java.util.List; import org.apache.hop.core.variables.IVariables; import org.apache.hop.ui.core.widget.highlight.SqlHighlight; +import org.eclipse.swt.custom.LineStyleListener; import org.eclipse.swt.widgets.Composite; public class SQLStyledTextComp extends StyledTextVar { @@ -54,6 +55,6 @@ public void addLineStyleListener(List keywords) { @Override public void removeLineStyleListener(LineStyleListener listener) { - textWidget.removeLineStyleListener(listener); + super.removeLineStyleListener(listener); } } diff --git a/ui/src/main/java/org/apache/hop/ui/core/widget/StyledTextVar.java b/ui/src/main/java/org/apache/hop/ui/core/widget/StyledTextVar.java index fb956426c9..21a68965f1 100644 --- a/ui/src/main/java/org/apache/hop/ui/core/widget/StyledTextVar.java +++ b/ui/src/main/java/org/apache/hop/ui/core/widget/StyledTextVar.java @@ -181,6 +181,10 @@ public void addLineStyleListener(LineStyleListener lineStyler) { wText.addLineStyleListener(lineStyler); } + public void removeLineStyleListener(LineStyleListener lineStyler) { + wText.removeLineStyleListener(lineStyler); + } + public void addKeyListener(KeyAdapter keyAdapter) { wText.addKeyListener(keyAdapter); } diff --git a/ui/src/main/java/org/apache/hop/ui/core/widget/TextComposite.java b/ui/src/main/java/org/apache/hop/ui/core/widget/TextComposite.java index db262bc413..f62cb40962 100644 --- a/ui/src/main/java/org/apache/hop/ui/core/widget/TextComposite.java +++ b/ui/src/main/java/org/apache/hop/ui/core/widget/TextComposite.java @@ -23,10 +23,10 @@ import org.apache.hop.ui.core.ConstUi; import org.apache.hop.ui.core.gui.GuiResource; import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.LineStyleListener; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.events.MenuDetectListener; -import org.eclipse.swt.custom.LineStyleListener; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; @@ -348,7 +348,7 @@ protected void buildingStyledTextMenu(Menu popupMenu) { setMenu(popupMenu); } - public void removeLineStyleListener(LineStyleListener listener) { - throw new UnsupportedOperationException("Cannot remove the listener"); - } + public void removeLineStyleListener(LineStyleListener listener) { + // Do nothing, there's no LineStyleListener to remove by default, unless a subclass requires it + } } From a04a1129166ad4c1c7ca9431db13288f5a84dcf0 Mon Sep 17 00:00:00 2001 From: Sergio De Lorenzis Date: Thu, 23 Jan 2025 18:05:21 +0100 Subject: [PATCH 3/6] made funciton names italic --- .../org/apache/hop/ui/core/widget/highlight/SqlHighlight.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/main/java/org/apache/hop/ui/core/widget/highlight/SqlHighlight.java b/ui/src/main/java/org/apache/hop/ui/core/widget/highlight/SqlHighlight.java index a8251f4192..ba23751668 100644 --- a/ui/src/main/java/org/apache/hop/ui/core/widget/highlight/SqlHighlight.java +++ b/ui/src/main/java/org/apache/hop/ui/core/widget/highlight/SqlHighlight.java @@ -104,7 +104,7 @@ void initializeStyles() { styleAttributes[COMMENT] = new StyleAttribute(resource.getColorGray(), SWT.ITALIC); styleAttributes[KEY] = new StyleAttribute(resource.getColor(30, 144, 255), SWT.NORMAL); styleAttributes[SYMBOL] = new StyleAttribute(resource.getColor(243, 126, 131), SWT.NORMAL); - styleAttributes[FUNCTION] = new StyleAttribute(resource.getColor(177, 102, 218), SWT.NORMAL); + styleAttributes[FUNCTION] = new StyleAttribute(resource.getColor(177, 102, 218), SWT.ITALIC); } else { styleAttributes[COMMENT] = new StyleAttribute(resource.getColorDarkGray(), SWT.ITALIC); styleAttributes[KEY] = new StyleAttribute(resource.getColorBlue(), SWT.NORMAL); From ca8b2c239c6051cc14886d98cbb520423bc22782 Mon Sep 17 00:00:00 2001 From: Sergio De Lorenzis Date: Mon, 27 Jan 2025 13:55:55 +0100 Subject: [PATCH 4/6] Some optimisations (it works) --- .../tableinput/TableInputDialog.java | 56 ++++++++++--------- .../transforms/tableinput/TableInputMeta.java | 4 ++ 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java index e1786a0d9b..8678212344 100644 --- a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java +++ b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputDialog.java @@ -105,12 +105,7 @@ public TableInputDialog( Shell parent, IVariables variables, TableInputMeta transformMeta, PipelineMeta pipelineMeta) { super(parent, variables, transformMeta, pipelineMeta); input = transformMeta; - final ExecutorService executorService = Executors.newFixedThreadPool(5); - pipelineMeta.getDatabases().parallelStream() - .forEach( - db -> { - executorService.submit(() -> fetchKeywords(db)); - }); + initReservedWordsCache(pipelineMeta); } @Override @@ -157,9 +152,7 @@ public String open() { wConnection.addListener( SWT.Selection, e -> { - final List k = - input.getKeywordsByConnectionName(wConnection.getText()).orElse(List.of()); - refreshLineStyleListener(k); + onConnectionSelected(); }); // Some buttons @@ -379,7 +372,8 @@ private void initSQLFormat() { final Optional> keywordsByConnectionName = input.getKeywordsByConnectionName(connectionName); if (keywordsByConnectionName.isPresent()) { - refreshLineStyleListener(keywordsByConnectionName.get()); + Display.getDefault() + .asyncExec(() -> refreshLineStyleListener(keywordsByConnectionName.get())); } else { refreshLineStyleListener(List.of()); Executors.newSingleThreadExecutor() @@ -395,6 +389,25 @@ private void initSQLFormat() { } } + private void onConnectionSelected() { + if (input.containsKeywordsByConnectionName(wConnection.getText())) { + final List k = + input.getKeywordsByConnectionName(wConnection.getText()).orElse(List.of()); + refreshLineStyleListener(k); + } else { + final String currentConnection = wConnection.getText(); + refreshLineStyleListener(List.of()); + Executors.newSingleThreadExecutor() + .submit( + () -> { + fetchKeywords(currentConnection); + final List k = + input.getKeywordsByConnectionName(currentConnection).orElse(List.of()); + Display.getDefault().asyncExec(() -> refreshLineStyleListener(k)); + }); + } + } + private synchronized void refreshLineStyleListener(List keywords) { wSql.removeLineStyleListener(sqlHighlightListener.get()); sqlHighlightListener.set(new SqlHighlight(keywords)); @@ -403,21 +416,14 @@ private synchronized void refreshLineStyleListener(List keywords) { // wSql.setRedraw(true); } - // private List getAsyncSqlReservedWords(String connectionName) { - // if (connectionName == null || connectionName.isEmpty()) { - // return new ArrayList<>(); - // } - // if (variables.resolve(connectionName).startsWith("${")) { // COULDN'T resolve variable: skip - // return new ArrayList<>(); - // } - // return input - // .getKeywordsByConnectionName(connectionName) - // .orElseGet( - // () -> { - // Executors.newSingleThreadExecutor().submit(() -> fetchKeywords(connectionName)); - // return List.of(); - // }); - // } + private void initReservedWordsCache(PipelineMeta pipelineMeta) { + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + pipelineMeta.getDatabases().stream() + .forEach( + db -> { + executorService.submit(() -> fetchKeywords(db)); + }); + } private void fetchKeywords(String connectionName) { DatabaseMeta databaseMeta = pipelineMeta.findDatabase(connectionName, variables); diff --git a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputMeta.java b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputMeta.java index afec669525..dc5d4f8fa2 100644 --- a/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputMeta.java +++ b/plugins/transforms/tableinput/src/main/java/org/apache/hop/pipeline/transforms/tableinput/TableInputMeta.java @@ -546,4 +546,8 @@ public Optional> getKeywordsByConnectionName(String connection) { final List keywordsList = keywordsCache.get(connection); return Optional.ofNullable(keywordsList); } + + public boolean containsKeywordsByConnectionName(String connection) { + return keywordsCache.containsKey(connection); + } } From 2750515df3d5b6cdb714aaafd227f99dcd337c13 Mon Sep 17 00:00:00 2001 From: Sergio De Lorenzis Date: Mon, 27 Jan 2025 14:06:03 +0100 Subject: [PATCH 5/6] Dataabase join working --- .../databasejoin/DatabaseJoinDialog.java | 119 +++++++++++++++++- .../databasejoin/DatabaseJoinMeta.java | 22 ++++ 2 files changed, 139 insertions(+), 2 deletions(-) diff --git a/plugins/transforms/databasejoin/src/main/java/org/apache/hop/pipeline/transforms/databasejoin/DatabaseJoinDialog.java b/plugins/transforms/databasejoin/src/main/java/org/apache/hop/pipeline/transforms/databasejoin/DatabaseJoinDialog.java index fde4804adb..3309c89a18 100644 --- a/plugins/transforms/databasejoin/src/main/java/org/apache/hop/pipeline/transforms/databasejoin/DatabaseJoinDialog.java +++ b/plugins/transforms/databasejoin/src/main/java/org/apache/hop/pipeline/transforms/databasejoin/DatabaseJoinDialog.java @@ -23,6 +23,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; import org.apache.hop.core.Const; import org.apache.hop.core.Props; import org.apache.hop.core.database.Database; @@ -47,6 +51,7 @@ import org.apache.hop.ui.core.widget.StyledTextComp; import org.apache.hop.ui.core.widget.TableView; import org.apache.hop.ui.core.widget.TextComposite; +import org.apache.hop.ui.core.widget.highlight.SqlHighlight; import org.apache.hop.ui.pipeline.transform.BaseTransformDialog; import org.apache.hop.ui.util.EnvironmentUtils; import org.eclipse.swt.SWT; @@ -63,6 +68,7 @@ import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TableItem; @@ -96,6 +102,9 @@ public class DatabaseJoinDialog extends BaseTransformDialog { private Label wlCacheSize; private Text wCacheSize; + private AtomicReference sqlHighlightListener = + new AtomicReference<>(new SqlHighlight(List.of())); + public DatabaseJoinDialog( Shell parent, IVariables variables, @@ -103,6 +112,7 @@ public DatabaseJoinDialog( PipelineMeta pipelineMeta) { super(parent, variables, transformMeta, pipelineMeta); input = transformMeta; + initReservedWordsCache(pipelineMeta); } @Override @@ -147,7 +157,11 @@ public String open() { // Connection line wConnection = addConnectionLine(shell, wTransformName, input.getConnection(), lsMod); - wConnection.addListener(SWT.Selection, e -> getSqlReservedWords()); + wConnection.addListener( + SWT.Selection, + e -> { + onConnectionSelected(); + }); // ICache? Label wlCache = new Label(shell, SWT.RIGHT); @@ -208,7 +222,6 @@ public void widgetSelected(SelectionEvent e) { variables, shell, SWT.MULTI | SWT.LEFT | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL) : new SQLStyledTextComp( variables, shell, SWT.MULTI | SWT.LEFT | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL); - wSql.addLineStyleListener(getSqlReservedWords()); PropsUi.setLook(wSql, Props.WIDGET_STYLE_FIXED); wSql.addModifyListener(lsMod); FormData fdSql = new FormData(); @@ -418,6 +431,7 @@ public void widgetSelected(SelectionEvent e) { wCancel.addListener(SWT.Selection, e -> cancel()); getData(); + initSQLFormat(); BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel()); @@ -586,4 +600,105 @@ private void get() { ke); } } + + private void initSQLFormat() { + String connectionName = input.getConnection(); + final Optional> keywordsByConnectionName = + input.getKeywordsByConnectionName(connectionName); + if (keywordsByConnectionName.isPresent()) { + Display.getDefault() + .asyncExec(() -> refreshLineStyleListener(keywordsByConnectionName.get())); + } else { + refreshLineStyleListener(List.of()); + Executors.newSingleThreadExecutor() + .submit( + () -> { + fetchKeywords(input.getConnection()); + final Optional> optKeywords = + input.getKeywordsByConnectionName(connectionName); + if (optKeywords.isPresent()) { + Display.getDefault().asyncExec(() -> refreshLineStyleListener(optKeywords.get())); + } + }); + } + } + + private void onConnectionSelected() { + if (input.containsKeywordsByConnectionName(wConnection.getText())) { + final List k = + input.getKeywordsByConnectionName(wConnection.getText()).orElse(List.of()); + refreshLineStyleListener(k); + } else { + final String currentConnection = wConnection.getText(); + refreshLineStyleListener(List.of()); + Executors.newSingleThreadExecutor() + .submit( + () -> { + fetchKeywords(currentConnection); + final List k = + input.getKeywordsByConnectionName(currentConnection).orElse(List.of()); + Display.getDefault().asyncExec(() -> refreshLineStyleListener(k)); + }); + } + } + + private synchronized void refreshLineStyleListener(List keywords) { + wSql.removeLineStyleListener(sqlHighlightListener.get()); + sqlHighlightListener.set(new SqlHighlight(keywords)); + wSql.addLineStyleListener(sqlHighlightListener.get()); + wSql.setText(wSql.getText()); + // wSql.setRedraw(true); + } + + private void initReservedWordsCache(PipelineMeta pipelineMeta) { + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + pipelineMeta.getDatabases().stream() + .forEach( + db -> { + executorService.submit(() -> fetchKeywords(db)); + }); + } + + private void fetchKeywords(String connectionName) { + DatabaseMeta databaseMeta = pipelineMeta.findDatabase(connectionName, variables); + if (databaseMeta != null) { + fetchKeywords(databaseMeta); + } else { + logError("Unable to find database '" + connectionName + "'"); + } + } + + private void fetchKeywords(DatabaseMeta databaseMeta) { + + if (databaseMeta == null) { + logError("Database connection not found. Proceding without keywords."); + return; + } + + try (Database db = new Database(loggingObject, variables, databaseMeta)) { + db.connect(); + DatabaseMetaData databaseMetaData = db.getDatabaseMetaData(); + if (databaseMetaData == null) { + logError("Couldn't get database metadata"); + return; + } + List sqlKeywords = new ArrayList<>(); + try { + final ResultSet functionsResultSet = databaseMetaData.getFunctions(null, null, null); + while (functionsResultSet.next()) { + String functionName = functionsResultSet.getString("FUNCTION_NAME"); + if (functionName.contains(";")) { + functionName = functionName.substring(0, functionName.indexOf(";")); + } + sqlKeywords.add(functionName); + } + sqlKeywords.addAll(Arrays.asList(databaseMetaData.getSQLKeywords().split(","))); + } catch (SQLException e) { + logError("Couldn't extract keywords from database metadata. Proceding without them."); + } + input.putKeywords(databaseMeta.getName(), sqlKeywords); + } catch (HopDatabaseException e) { + logError("Couldn't extract keywords from database metadata. Proceding without them."); + } + } } diff --git a/plugins/transforms/databasejoin/src/main/java/org/apache/hop/pipeline/transforms/databasejoin/DatabaseJoinMeta.java b/plugins/transforms/databasejoin/src/main/java/org/apache/hop/pipeline/transforms/databasejoin/DatabaseJoinMeta.java index c380d89997..1e7896bc38 100644 --- a/plugins/transforms/databasejoin/src/main/java/org/apache/hop/pipeline/transforms/databasejoin/DatabaseJoinMeta.java +++ b/plugins/transforms/databasejoin/src/main/java/org/apache/hop/pipeline/transforms/databasejoin/DatabaseJoinMeta.java @@ -19,6 +19,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import org.apache.hop.core.CheckResult; import org.apache.hop.core.Const; import org.apache.hop.core.ICheckResult; @@ -113,6 +116,8 @@ public class DatabaseJoinMeta extends BaseTransformMeta> keywordsCache = new ConcurrentHashMap<>(); + public DatabaseJoinMeta() { super(); // allocate BaseTransformMeta } @@ -592,4 +597,21 @@ public List getParameters() { public void setParameters(List parameters) { this.parameters = parameters; } + + public void putKeywords(String name, List sqlKeywords) { + keywordsCache.put(name, sqlKeywords); + } + + public Map> getKeywordsCache() { + return keywordsCache; + } + + public Optional> getKeywordsByConnectionName(String connection) { + final List keywordsList = keywordsCache.get(connection); + return Optional.ofNullable(keywordsList); + } + + public boolean containsKeywordsByConnectionName(String connection) { + return keywordsCache.containsKey(connection); + } } From 58e544b3c36cbab741fa3e1964cb13967952d813 Mon Sep 17 00:00:00 2001 From: Sergio De Lorenzis Date: Mon, 27 Jan 2025 16:58:22 +0100 Subject: [PATCH 6/6] DynamicSqlRow working --- .../dynamicsqlrow/DynamicSqlRowDialog.java | 120 +++++++++++++++++- .../dynamicsqlrow/DynamicSqlRowMeta.java | 22 ++++ 2 files changed, 140 insertions(+), 2 deletions(-) diff --git a/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowDialog.java b/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowDialog.java index f5ada1e854..73a001ce7e 100644 --- a/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowDialog.java +++ b/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowDialog.java @@ -23,6 +23,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; import org.apache.hop.core.Const; import org.apache.hop.core.Props; import org.apache.hop.core.database.Database; @@ -42,6 +46,7 @@ import org.apache.hop.ui.core.widget.SQLStyledTextComp; import org.apache.hop.ui.core.widget.StyledTextComp; import org.apache.hop.ui.core.widget.TextComposite; +import org.apache.hop.ui.core.widget.highlight.SqlHighlight; import org.apache.hop.ui.pipeline.transform.BaseTransformDialog; import org.apache.hop.ui.util.EnvironmentUtils; import org.eclipse.swt.SWT; @@ -61,6 +66,7 @@ import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; @@ -88,6 +94,9 @@ public class DynamicSqlRowDialog extends BaseTransformDialog { private final DynamicSqlRowMeta input; + private AtomicReference sqlHighlightListener = + new AtomicReference<>(new SqlHighlight(List.of())); + public DynamicSqlRowDialog( Shell parent, IVariables variables, @@ -95,6 +104,7 @@ public DynamicSqlRowDialog( PipelineMeta pipelineMeta) { super(parent, variables, transformMeta, pipelineMeta); input = transformMeta; + initReservedWordsCache(pipelineMeta); } @Override @@ -143,7 +153,11 @@ public String open() { wConnection.select(0); } wConnection.addModifyListener(lsMod); - wConnection.addListener(SWT.Selection, e -> getSqlReservedWords()); + wConnection.addListener( + SWT.Selection, + e -> { + onConnectionSelected(); + }); // SQLFieldName field Label wlSqlFieldName = new Label(shell, SWT.RIGHT); @@ -308,7 +322,7 @@ public void widgetSelected(SelectionEvent e) { variables, shell, SWT.MULTI | SWT.LEFT | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL) : new SQLStyledTextComp( variables, shell, SWT.MULTI | SWT.LEFT | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL); - wSql.addLineStyleListener(getSqlReservedWords()); + PropsUi.setLook(wSql, Props.WIDGET_STYLE_FIXED); FormData fdSql = new FormData(); fdSql.left = new FormAttachment(0, 0); @@ -364,6 +378,7 @@ public void mouseUp(MouseEvent e) { wSql.addModifyListener(lsMod); getData(); + initSQLFormat(); BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel()); @@ -502,4 +517,105 @@ private void get() { } } } + + private void initSQLFormat() { + String connectionName = input.getConnection(); + final Optional> keywordsByConnectionName = + input.getKeywordsByConnectionName(connectionName); + if (keywordsByConnectionName.isPresent()) { + Display.getDefault() + .asyncExec(() -> refreshLineStyleListener(keywordsByConnectionName.get())); + } else { + refreshLineStyleListener(List.of()); + Executors.newSingleThreadExecutor() + .submit( + () -> { + fetchKeywords(input.getConnection()); + final Optional> optKeywords = + input.getKeywordsByConnectionName(connectionName); + if (optKeywords.isPresent()) { + Display.getDefault().asyncExec(() -> refreshLineStyleListener(optKeywords.get())); + } + }); + } + } + + private void onConnectionSelected() { + if (input.containsKeywordsByConnectionName(wConnection.getText())) { + final List k = + input.getKeywordsByConnectionName(wConnection.getText()).orElse(List.of()); + refreshLineStyleListener(k); + } else { + final String currentConnection = wConnection.getText(); + refreshLineStyleListener(List.of()); + Executors.newSingleThreadExecutor() + .submit( + () -> { + fetchKeywords(currentConnection); + final List k = + input.getKeywordsByConnectionName(currentConnection).orElse(List.of()); + Display.getDefault().asyncExec(() -> refreshLineStyleListener(k)); + }); + } + } + + private synchronized void refreshLineStyleListener(List keywords) { + wSql.removeLineStyleListener(sqlHighlightListener.get()); + sqlHighlightListener.set(new SqlHighlight(keywords)); + wSql.addLineStyleListener(sqlHighlightListener.get()); + wSql.setText(wSql.getText()); + // wSql.setRedraw(true); + } + + private void initReservedWordsCache(PipelineMeta pipelineMeta) { + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + pipelineMeta.getDatabases().stream() + .forEach( + db -> { + executorService.submit(() -> fetchKeywords(db)); + }); + } + + private void fetchKeywords(String connectionName) { + DatabaseMeta databaseMeta = pipelineMeta.findDatabase(connectionName, variables); + if (databaseMeta != null) { + fetchKeywords(databaseMeta); + } else { + logError("Unable to find database '" + connectionName + "'"); + } + } + + private void fetchKeywords(DatabaseMeta databaseMeta) { + + if (databaseMeta == null) { + logError("Database connection not found. Proceding without keywords."); + return; + } + + try (Database db = new Database(loggingObject, variables, databaseMeta)) { + db.connect(); + DatabaseMetaData databaseMetaData = db.getDatabaseMetaData(); + if (databaseMetaData == null) { + logError("Couldn't get database metadata"); + return; + } + List sqlKeywords = new ArrayList<>(); + try { + final ResultSet functionsResultSet = databaseMetaData.getFunctions(null, null, null); + while (functionsResultSet.next()) { + String functionName = functionsResultSet.getString("FUNCTION_NAME"); + if (functionName.contains(";")) { + functionName = functionName.substring(0, functionName.indexOf(";")); + } + sqlKeywords.add(functionName); + } + sqlKeywords.addAll(Arrays.asList(databaseMetaData.getSQLKeywords().split(","))); + } catch (SQLException e) { + logError("Couldn't extract keywords from database metadata. Proceding without them."); + } + input.putKeywords(databaseMeta.getName(), sqlKeywords); + } catch (HopDatabaseException e) { + logError("Couldn't extract keywords from database metadata. Proceding without them."); + } + } } diff --git a/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowMeta.java b/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowMeta.java index 1c0cd97500..5ae46bbcef 100644 --- a/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowMeta.java +++ b/plugins/transforms/dynamicsqlrow/src/main/java/org/apache/hop/pipeline/transforms/dynamicsqlrow/DynamicSqlRowMeta.java @@ -18,6 +18,9 @@ package org.apache.hop.pipeline.transforms.dynamicsqlrow; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import org.apache.hop.core.CheckResult; import org.apache.hop.core.Const; import org.apache.hop.core.ICheckResult; @@ -100,6 +103,8 @@ public class DynamicSqlRowMeta extends BaseTransformMeta> keywordsCache = new ConcurrentHashMap<>(); + public DynamicSqlRowMeta() { super(); // allocate BaseTransformMeta } @@ -444,4 +449,21 @@ public void analyseImpact( public boolean supportsErrorHandling() { return true; } + + public void putKeywords(String name, List sqlKeywords) { + keywordsCache.put(name, sqlKeywords); + } + + public Map> getKeywordsCache() { + return keywordsCache; + } + + public Optional> getKeywordsByConnectionName(String connection) { + final List keywordsList = keywordsCache.get(connection); + return Optional.ofNullable(keywordsList); + } + + public boolean containsKeywordsByConnectionName(String connection) { + return keywordsCache.containsKey(connection); + } }