From 2d59f4e2aabeefad6eed5e103ffd28ce5c74626b Mon Sep 17 00:00:00 2001 From: Matt Casters Date: Tue, 7 Jan 2025 13:13:21 +0100 Subject: [PATCH] A first stab at issue #3454 --- assemblies/plugins/pom.xml | 7 + .../apache/hop/core/HopClientEnvironment.java | 2 + .../org/apache/hop/core/util/StringUtil.java | 4 + .../apache/hop/core/variables/Variables.java | 82 ++++- .../variables/resolver/IVariableResolver.java | 47 +++ .../variables/resolver/VariableResolver.java | 53 +++ .../VariableResolverObjectFactory.java | 43 +++ .../resolver/VariableResolverPlugin.java | 54 +++ .../resolver/VariableResolverPluginType.java | 82 +++++ .../multi/MultiMetadataProvider.java | 53 +-- .../metadata/util/HopMetadataInstance.java | 28 ++ .../messages/messages_en_US.properties | 20 ++ .../main/java/org/apache/hop/run/HopRun.java | 2 + .../java/org/apache/hop/www/HopServer.java | 2 + .../hop/projects/util/ProjectsUtil.java | 2 + plugins/pom.xml | 1 + plugins/resolvers/pipeline/pom.xml | 32 ++ .../pipeline/src/assembly/assembly.xml | 42 +++ .../pipeline/VariableResolverPipeline.java | 154 +++++++++ .../messages/messages_en_US.properties | 20 ++ .../pipeline/src/main/resources/version.xml | 20 ++ plugins/resolvers/pom.xml | 36 ++ .../main/java/org/apache/hop/rest/Hop.java | 2 + .../hop/ui/core/gui/GuiCompositeWidgets.java | 9 +- .../resolver/VariableResolverEditor.java | 324 ++++++++++++++++++ .../java/org/apache/hop/ui/hopgui/HopGui.java | 2 + .../messages/messages_en_US.properties | 21 ++ 27 files changed, 1094 insertions(+), 50 deletions(-) create mode 100644 core/src/main/java/org/apache/hop/core/variables/resolver/IVariableResolver.java create mode 100644 core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolver.java create mode 100644 core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolverObjectFactory.java create mode 100644 core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolverPlugin.java create mode 100644 core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolverPluginType.java create mode 100644 core/src/main/java/org/apache/hop/metadata/util/HopMetadataInstance.java create mode 100644 core/src/main/resources/org/apache/hop/core/variables/resolver/messages/messages_en_US.properties create mode 100644 plugins/resolvers/pipeline/pom.xml create mode 100644 plugins/resolvers/pipeline/src/assembly/assembly.xml create mode 100644 plugins/resolvers/pipeline/src/main/java/org/apache/hop/resolvers/pipeline/VariableResolverPipeline.java create mode 100644 plugins/resolvers/pipeline/src/main/resources/org/apache/hop/resolvers/pipeline/messages/messages_en_US.properties create mode 100644 plugins/resolvers/pipeline/src/main/resources/version.xml create mode 100644 plugins/resolvers/pom.xml create mode 100644 ui/src/main/java/org/apache/hop/ui/core/variables/resolver/VariableResolverEditor.java create mode 100644 ui/src/main/resources/org/apache/hop/ui/core/variables/resolver/messages/messages_en_US.properties diff --git a/assemblies/plugins/pom.xml b/assemblies/plugins/pom.xml index 21a43b63396..e2b26041e6e 100644 --- a/assemblies/plugins/pom.xml +++ b/assemblies/plugins/pom.xml @@ -714,6 +714,13 @@ ${project.version} zip + + org.apache.hop + hop-resolvers-pipeline + 2.12.0-SNAPSHOT + zip + + org.apache.hop hop-tech-avro diff --git a/core/src/main/java/org/apache/hop/core/HopClientEnvironment.java b/core/src/main/java/org/apache/hop/core/HopClientEnvironment.java index 5ae89a9403a..9d7b149c605 100644 --- a/core/src/main/java/org/apache/hop/core/HopClientEnvironment.java +++ b/core/src/main/java/org/apache/hop/core/HopClientEnvironment.java @@ -39,6 +39,7 @@ import org.apache.hop.core.plugins.PluginRegistry; import org.apache.hop.core.row.value.ValueMetaPluginType; import org.apache.hop.core.util.EnvUtil; +import org.apache.hop.core.variables.resolver.VariableResolverPluginType; import org.apache.hop.core.vfs.HopVfs; import org.apache.hop.core.vfs.plugin.VfsPluginType; @@ -80,6 +81,7 @@ public static synchronized void init() throws HopException { DatabasePluginType.getInstance(), ExtensionPointPluginType.getInstance(), TwoWayPasswordEncoderPluginType.getInstance(), + VariableResolverPluginType.getInstance(), VfsPluginType.getInstance())); } diff --git a/core/src/main/java/org/apache/hop/core/util/StringUtil.java b/core/src/main/java/org/apache/hop/core/util/StringUtil.java index 49d352c11fb..9950e57a21a 100644 --- a/core/src/main/java/org/apache/hop/core/util/StringUtil.java +++ b/core/src/main/java/org/apache/hop/core/util/StringUtil.java @@ -51,6 +51,10 @@ public class StringUtil { public static final String FIELD_CLOSE = "}"; + public static final String RESOLVER_OPEN = "#{"; + + public static final String RESOLVER_CLOSE = "}"; + public static final String CRLF = "\r\n"; public static final String INDENTCHARS = " "; diff --git a/core/src/main/java/org/apache/hop/core/variables/Variables.java b/core/src/main/java/org/apache/hop/core/variables/Variables.java index ea7d99fe9b3..73b735081dc 100644 --- a/core/src/main/java/org/apache/hop/core/variables/Variables.java +++ b/core/src/main/java/org/apache/hop/core/variables/Variables.java @@ -25,11 +25,16 @@ import java.util.Set; import org.apache.hop.core.Const; import org.apache.hop.core.config.HopConfig; +import org.apache.hop.core.exception.HopException; import org.apache.hop.core.exception.HopValueException; import org.apache.hop.core.row.IRowMeta; import org.apache.hop.core.row.value.ValueMetaBase; import org.apache.hop.core.util.StringUtil; import org.apache.hop.core.util.Utils; +import org.apache.hop.core.variables.resolver.VariableResolver; +import org.apache.hop.metadata.api.IHopMetadataSerializer; +import org.apache.hop.metadata.serializer.multi.MultiMetadataProvider; +import org.apache.hop.metadata.util.HopMetadataInstance; /** This class is an implementation of IVariables */ public class Variables implements IVariables { @@ -139,11 +144,84 @@ public synchronized void setVariable(String variableName, String variableValue) @Override public synchronized String resolve(String aString) { - if (aString == null || aString.length() == 0) { + if (aString == null || aString.isEmpty()) { return aString; } - return StringUtil.environmentSubstitute(aString, properties); + String resolved = StringUtil.environmentSubstitute(aString, properties); + + resolved = substituteVariableResolvers(resolved); + + return resolved; + } + + private String substituteVariableResolvers(String input) { + String resolved = input; + int startIndex = 0; + while (startIndex < resolved.length()) { + int resolverIndex = resolved.indexOf(StringUtil.RESOLVER_OPEN); + if (resolverIndex < 0) { + // There's nothing more to do here. + // + return resolved; + } + + // Is there a close token? + // + int closeIndex = resolved.indexOf(StringUtil.RESOLVER_CLOSE, resolverIndex); + if (closeIndex < 0) { + // False positive on the open token + // + return resolved; + } + + // The variable resolver String [resolverIndex, closeIndex$] + // is in the format: #{name:argument} + // + int colonIndex = resolved.indexOf(':', resolverIndex); + String name; + String argument; + if (colonIndex >= 0) { + name = resolved.substring(resolverIndex + StringUtil.RESOLVER_OPEN.length(), colonIndex); + argument = resolved.substring(colonIndex + 1, closeIndex); + } else { + name = resolved.substring(resolverIndex + StringUtil.RESOLVER_OPEN.length(), closeIndex); + argument = ""; + } + + try { + MultiMetadataProvider provider = HopMetadataInstance.getMetadataProvider(); + IHopMetadataSerializer serializer = + provider.getSerializer(VariableResolver.class); + + // This next loadA() method is relatively slow since it needs to go to disk. + // + VariableResolver resolver = serializer.load(name); + if (resolver == null) { + // return resolved; + // + throw new HopException( + "Variable Resolver '" + name + "' could not be found in the metadata"); + } + String resolvedArgument = resolver.getIResolver().resolve(argument, this); + + // We take the first part of the original resolved string, the resolved argument, and the + // last part + // to continue resolving all expressions in the string. + // + String before = resolved.substring(0, resolverIndex); + String after = resolved.substring(closeIndex + 1); + resolved = before + resolvedArgument + after; + + // Where do we continue? + startIndex = before.length() + resolvedArgument.length(); + + } catch (HopException e) { + throw new RuntimeException( + "Error resolving variable '" + input + "' with variable resolver metadata", e); + } + } + return resolved; } /** diff --git a/core/src/main/java/org/apache/hop/core/variables/resolver/IVariableResolver.java b/core/src/main/java/org/apache/hop/core/variables/resolver/IVariableResolver.java new file mode 100644 index 00000000000..3897cc6dd0d --- /dev/null +++ b/core/src/main/java/org/apache/hop/core/variables/resolver/IVariableResolver.java @@ -0,0 +1,47 @@ +/* + * 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.apache.hop.core.variables.resolver; + +import org.apache.hop.core.exception.HopException; +import org.apache.hop.core.variables.IVariables; +import org.apache.hop.metadata.api.HopMetadataObject; + +/** Indicates that the class can resolve variables in a specific manner. */ +@HopMetadataObject(objectFactory = VariableResolverObjectFactory.class) +public interface IVariableResolver { + void init(); + + /** + * Resolve the variables in the given String. + * + * @param string The String in which we want to replace variables. + * @param variables The variables and values to use as a reference + * @return The input string with expressions resolved. + * @throws HopException In case something goes wrong. + */ + String resolve(String string, IVariables variables) throws HopException; + + void setPluginId(); + + String getPluginId(); + + void setPluginName(String pluginName); + + String getPluginName(); +} diff --git a/core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolver.java b/core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolver.java new file mode 100644 index 00000000000..b796cda748c --- /dev/null +++ b/core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolver.java @@ -0,0 +1,53 @@ +/* + * 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.apache.hop.core.variables.resolver; + +import lombok.Getter; +import lombok.Setter; +import org.apache.hop.metadata.api.HopMetadata; +import org.apache.hop.metadata.api.HopMetadataBase; +import org.apache.hop.metadata.api.HopMetadataProperty; +import org.apache.hop.metadata.api.HopMetadataPropertyType; +import org.apache.hop.metadata.api.IHopMetadata; + +@HopMetadata( + key = "variable-resolver", + name = "i18n::VariableResolver.name", + description = "i18n::VariableResolver.Description", + image = "ui/images/variable.svg", + documentationUrl = "/metadata-types/variable-resolver.html", + hopMetadataPropertyType = HopMetadataPropertyType.RDBMS_CONNECTION) +@Getter +@Setter +public class VariableResolver extends HopMetadataBase implements IHopMetadata { + public static final String GUI_PLUGIN_ELEMENT_PARENT_ID = + "VariableResolver-PluginSpecific-Options"; + + @HopMetadataProperty private String description; + + @HopMetadataProperty(key = "variable-resolver") + private IVariableResolver iResolver; + + public String getPluginName() { + if (iResolver == null) { + return null; + } + return iResolver.getPluginName(); + } +} diff --git a/core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolverObjectFactory.java b/core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolverObjectFactory.java new file mode 100644 index 00000000000..905f9a4ce2f --- /dev/null +++ b/core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolverObjectFactory.java @@ -0,0 +1,43 @@ +/* + * 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.apache.hop.core.variables.resolver; + +import org.apache.hop.core.exception.HopException; +import org.apache.hop.core.plugins.IPlugin; +import org.apache.hop.core.plugins.PluginRegistry; +import org.apache.hop.metadata.api.IHopMetadataObjectFactory; + +public class VariableResolverObjectFactory implements IHopMetadataObjectFactory { + + @Override + public Object createObject(String id, Object parentObject) throws HopException { + PluginRegistry registry = PluginRegistry.getInstance(); + IPlugin plugin = registry.findPluginWithId(VariableResolverPluginType.class, id); + return registry.loadClass(plugin); + } + + @Override + public String getObjectId(Object object) throws HopException { + if (!(object instanceof IVariableResolver)) { + throw new HopException( + "Object is not of class IVariableResolver but of " + object.getClass().getName() + "'"); + } + return ((IVariableResolver) object).getPluginId(); + } +} diff --git a/core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolverPlugin.java b/core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolverPlugin.java new file mode 100644 index 00000000000..a1af28a6972 --- /dev/null +++ b/core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolverPlugin.java @@ -0,0 +1,54 @@ +/* + * 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.apache.hop.core.variables.resolver; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** This annotation indicates that the given plugin is a variable resolver plugin. */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface VariableResolverPlugin { + /** + * @return The ID of the password encoder plugin. You can specify more than one ID in a comma + * separated format: id1,id2,id3 for deprecation purposes. + */ + String id(); + + String name(); + + String description() default ""; + + /** + * @return True if a separate class loader is needed every time this class is instantiated + */ + boolean isSeparateClassLoaderNeeded() default false; + + String documentationUrl() default ""; + + String casesUrl() default ""; + + String forumUrl() default ""; + + String classLoaderGroup() default ""; +} diff --git a/core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolverPluginType.java b/core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolverPluginType.java new file mode 100644 index 00000000000..2fbe97f77bd --- /dev/null +++ b/core/src/main/java/org/apache/hop/core/variables/resolver/VariableResolverPluginType.java @@ -0,0 +1,82 @@ +/* + * 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.apache.hop.core.variables.resolver; + +import org.apache.hop.core.plugins.BasePluginType; +import org.apache.hop.core.plugins.PluginAnnotationType; +import org.apache.hop.core.plugins.PluginMainClassType; + +/** This class represents the value meta plugin type. */ +@PluginMainClassType(IVariableResolver.class) +@PluginAnnotationType(VariableResolverPlugin.class) +public class VariableResolverPluginType extends BasePluginType { + + private static VariableResolverPluginType pluginType; + + private VariableResolverPluginType() { + super(VariableResolverPlugin.class, "VARIABLERESOLVERPLUGIN", "VariableResolver"); + } + + public static VariableResolverPluginType getInstance() { + if (pluginType == null) { + pluginType = new VariableResolverPluginType(); + } + return pluginType; + } + + @Override + protected String extractDesc(VariableResolverPlugin annotation) { + return annotation.description(); + } + + @Override + protected String extractID(VariableResolverPlugin annotation) { + return annotation.id(); + } + + @Override + protected String extractName(VariableResolverPlugin annotation) { + return annotation.name(); + } + + @Override + protected boolean extractSeparateClassLoader(VariableResolverPlugin annotation) { + return annotation.isSeparateClassLoaderNeeded(); + } + + @Override + protected String extractDocumentationUrl(VariableResolverPlugin annotation) { + return annotation.documentationUrl(); + } + + @Override + protected String extractCasesUrl(VariableResolverPlugin annotation) { + return annotation.casesUrl(); + } + + @Override + protected String extractForumUrl(VariableResolverPlugin annotation) { + return annotation.forumUrl(); + } + + @Override + protected String extractClassLoaderGroup(VariableResolverPlugin annotation) { + return annotation.classLoaderGroup(); + } +} diff --git a/core/src/main/java/org/apache/hop/metadata/serializer/multi/MultiMetadataProvider.java b/core/src/main/java/org/apache/hop/metadata/serializer/multi/MultiMetadataProvider.java index 2e91ba0c08d..983b76b7932 100644 --- a/core/src/main/java/org/apache/hop/metadata/serializer/multi/MultiMetadataProvider.java +++ b/core/src/main/java/org/apache/hop/metadata/serializer/multi/MultiMetadataProvider.java @@ -22,6 +22,8 @@ import java.util.List; import java.util.ListIterator; import java.util.Set; +import lombok.Getter; +import lombok.Setter; import org.apache.hop.core.encryption.Encr; import org.apache.hop.core.encryption.ITwoWayPasswordEncoder; import org.apache.hop.core.exception.HopException; @@ -36,10 +38,11 @@ /** * This metadata provider delegates for a standard provider but also reads information from others */ +@Getter public class MultiMetadataProvider implements IHopMetadataProvider { - private ITwoWayPasswordEncoder twoWayPasswordEncoder; - private IVariables variables; - private List providers; + @Setter private ITwoWayPasswordEncoder twoWayPasswordEncoder; + private final IVariables variables; + @Setter private List providers; /** * @param twoWayPasswordEncoder The password encoder to use @@ -167,48 +170,4 @@ public void setDescription(String description) { "The description of the multi metadata provider can't be changed. " + "It's derived from the list of providers it contains."); } - - @Override - public ITwoWayPasswordEncoder getTwoWayPasswordEncoder() { - return twoWayPasswordEncoder; - } - - /** - * @param twoWayPasswordEncoder The twoWayPasswordEncoder to set - */ - public void setTwoWayPasswordEncoder(ITwoWayPasswordEncoder twoWayPasswordEncoder) { - this.twoWayPasswordEncoder = twoWayPasswordEncoder; - } - - /** - * Gets providers - * - * @return value of providers - */ - public List getProviders() { - return providers; - } - - /** - * @param providers The providers to set - */ - public void setProviders(List providers) { - this.providers = providers; - } - - /** - * Gets variables - * - * @return value of variables - */ - public IVariables getVariables() { - return variables; - } - - /** - * @param variables The variables to set - */ - public void setVariables(IVariables variables) { - this.variables = variables; - } } diff --git a/core/src/main/java/org/apache/hop/metadata/util/HopMetadataInstance.java b/core/src/main/java/org/apache/hop/metadata/util/HopMetadataInstance.java new file mode 100644 index 00000000000..3a4e74a4b53 --- /dev/null +++ b/core/src/main/java/org/apache/hop/metadata/util/HopMetadataInstance.java @@ -0,0 +1,28 @@ +package org.apache.hop.metadata.util; + +import org.apache.hop.metadata.serializer.multi.MultiMetadataProvider; + +public class HopMetadataInstance { + private static HopMetadataInstance instance; + + private MultiMetadataProvider metadataProvider; + + private HopMetadataInstance() { + // Nothing to do here. + } + + public static HopMetadataInstance getInstance() { + if (instance == null) { + instance = new HopMetadataInstance(); + } + return instance; + } + + public static void setMetadataProvider(MultiMetadataProvider metadataProvider) { + getInstance().metadataProvider = metadataProvider; + } + + public static MultiMetadataProvider getMetadataProvider() { + return getInstance().metadataProvider; + } +} diff --git a/core/src/main/resources/org/apache/hop/core/variables/resolver/messages/messages_en_US.properties b/core/src/main/resources/org/apache/hop/core/variables/resolver/messages/messages_en_US.properties new file mode 100644 index 00000000000..ec3ddb0cb38 --- /dev/null +++ b/core/src/main/resources/org/apache/hop/core/variables/resolver/messages/messages_en_US.properties @@ -0,0 +1,20 @@ +# +# 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. +# +# + +VariableResolver.name = Variable Resolver +VariableResolver.Description = This allows you to create custom ways of dynamically resolving variables. \ No newline at end of file diff --git a/engine/src/main/java/org/apache/hop/run/HopRun.java b/engine/src/main/java/org/apache/hop/run/HopRun.java index 5b2b60005cf..2fa9eee119a 100644 --- a/engine/src/main/java/org/apache/hop/run/HopRun.java +++ b/engine/src/main/java/org/apache/hop/run/HopRun.java @@ -53,6 +53,7 @@ import org.apache.hop.metadata.api.IHasHopMetadataProvider; import org.apache.hop.metadata.api.IHopMetadataSerializer; import org.apache.hop.metadata.serializer.multi.MultiMetadataProvider; +import org.apache.hop.metadata.util.HopMetadataInstance; import org.apache.hop.metadata.util.HopMetadataUtil; import org.apache.hop.pipeline.PipelineExecutionConfiguration; import org.apache.hop.pipeline.PipelineMeta; @@ -903,6 +904,7 @@ public static void main(String[] args) { // Set up the metadata to use // hopRun.metadataProvider = HopMetadataUtil.getStandardHopMetadataProvider(hopRun.variables); + HopMetadataInstance.setMetadataProvider(hopRun.metadataProvider); // Now add configuration plugins with the RUN category. // The 'projects' plugin for example configures things like the project metadata provider. diff --git a/engine/src/main/java/org/apache/hop/www/HopServer.java b/engine/src/main/java/org/apache/hop/www/HopServer.java index 2e8d38e4e7f..4f64ea3b0e7 100644 --- a/engine/src/main/java/org/apache/hop/www/HopServer.java +++ b/engine/src/main/java/org/apache/hop/www/HopServer.java @@ -58,6 +58,7 @@ import org.apache.hop.metadata.api.IHopMetadataSerializer; import org.apache.hop.metadata.serializer.json.JsonMetadataProvider; import org.apache.hop.metadata.serializer.multi.MultiMetadataProvider; +import org.apache.hop.metadata.util.HopMetadataInstance; import org.apache.hop.metadata.util.HopMetadataUtil; import org.apache.hop.pipeline.transform.TransformStatus; import org.apache.hop.server.HopServerMeta; @@ -525,6 +526,7 @@ public static void main(String[] args) { // hopServer.metadataProvider = HopMetadataUtil.getStandardHopMetadataProvider(hopServer.variables); + HopMetadataInstance.setMetadataProvider(hopServer.metadataProvider); // Now add server configuration plugins... // diff --git a/plugins/misc/projects/src/main/java/org/apache/hop/projects/util/ProjectsUtil.java b/plugins/misc/projects/src/main/java/org/apache/hop/projects/util/ProjectsUtil.java index 095d4bbf20d..5f4e908cfc7 100644 --- a/plugins/misc/projects/src/main/java/org/apache/hop/projects/util/ProjectsUtil.java +++ b/plugins/misc/projects/src/main/java/org/apache/hop/projects/util/ProjectsUtil.java @@ -32,6 +32,7 @@ import org.apache.hop.history.AuditManager; import org.apache.hop.metadata.api.IHasHopMetadataProvider; import org.apache.hop.metadata.serializer.multi.MultiMetadataProvider; +import org.apache.hop.metadata.util.HopMetadataInstance; import org.apache.hop.metadata.util.HopMetadataUtil; import org.apache.hop.projects.config.ProjectsConfig; import org.apache.hop.projects.config.ProjectsConfigSingleton; @@ -89,6 +90,7 @@ public static void enableProject( MultiMetadataProvider metadataProvider = HopMetadataUtil.getStandardHopMetadataProvider(variables); hasHopMetadataProvider.setMetadataProvider(metadataProvider); + HopMetadataInstance.setMetadataProvider(metadataProvider); project.setMetadataProvider(metadataProvider); } diff --git a/plugins/pom.xml b/plugins/pom.xml index f3c917c8f36..9da37f922a6 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -34,6 +34,7 @@ databases engines misc + resolvers tech transforms valuetypes diff --git a/plugins/resolvers/pipeline/pom.xml b/plugins/resolvers/pipeline/pom.xml new file mode 100644 index 00000000000..67d6a1db0f0 --- /dev/null +++ b/plugins/resolvers/pipeline/pom.xml @@ -0,0 +1,32 @@ + + + + 4.0.0 + + + org.apache.hop + hop-plugins-resolvers + 2.12.0-SNAPSHOT + + + hop-resolvers-pipeline + jar + Hop Plugins Variable Resolvers Pipeline + + diff --git a/plugins/resolvers/pipeline/src/assembly/assembly.xml b/plugins/resolvers/pipeline/src/assembly/assembly.xml new file mode 100644 index 00000000000..87db33cc091 --- /dev/null +++ b/plugins/resolvers/pipeline/src/assembly/assembly.xml @@ -0,0 +1,42 @@ + + + + hop-resolvers-pipeline + + zip + + . + + + ${project.basedir}/src/main/resources/version.xml + plugins/resolvers/pipeline + true + + + + + + org.apache.hop:hop-resolvers-pipeline:jar + + plugins/resolvers/pipeline + + + diff --git a/plugins/resolvers/pipeline/src/main/java/org/apache/hop/resolvers/pipeline/VariableResolverPipeline.java b/plugins/resolvers/pipeline/src/main/java/org/apache/hop/resolvers/pipeline/VariableResolverPipeline.java new file mode 100644 index 00000000000..d55283072a9 --- /dev/null +++ b/plugins/resolvers/pipeline/src/main/java/org/apache/hop/resolvers/pipeline/VariableResolverPipeline.java @@ -0,0 +1,154 @@ +/* + * 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.apache.hop.resolvers.pipeline; + +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang.StringUtils; +import org.apache.hop.core.exception.HopException; +import org.apache.hop.core.gui.plugin.GuiElementType; +import org.apache.hop.core.gui.plugin.GuiPlugin; +import org.apache.hop.core.gui.plugin.GuiWidgetElement; +import org.apache.hop.core.logging.LogLevel; +import org.apache.hop.core.variables.IVariables; +import org.apache.hop.core.variables.resolver.IVariableResolver; +import org.apache.hop.core.variables.resolver.VariableResolver; +import org.apache.hop.core.variables.resolver.VariableResolverPlugin; +import org.apache.hop.metadata.api.HopMetadataProperty; +import org.apache.hop.metadata.serializer.multi.MultiMetadataProvider; +import org.apache.hop.metadata.util.HopMetadataInstance; +import org.apache.hop.pipeline.PipelineExecutionConfiguration; +import org.apache.hop.pipeline.PipelineMeta; +import org.apache.hop.pipeline.config.PipelineRunConfiguration; +import org.apache.hop.pipeline.engine.IPipelineEngine; +import org.apache.hop.pipeline.engine.PipelineEngineFactory; +import org.apache.hop.pipeline.engines.remote.PipelineRunConfigurationTypeMetadata; + +@Getter +@Setter +@GuiPlugin +@VariableResolverPlugin( + id = "Variable-Resolver-Pipeline", + name = "Pipeline Variable Resolver", + description = "Use a pipeline to resolve the value of a variable expression", + documentationUrl = "/variables/resolvers/pipeline.html" // TODO: write this documentation + ) +public class VariableResolverPipeline implements IVariableResolver { + + /** The name of the pipeline filename to use to resolve variable expressions */ + @GuiWidgetElement( + id = "filename", + order = "01", + label = "i18n::VariableResolverEditor.label.Filename", + type = GuiElementType.FILENAME, + parentId = VariableResolver.GUI_PLUGIN_ELEMENT_PARENT_ID) + @HopMetadataProperty() + private String filename; + + /** The name of the local Hop pipeline run configuration to use */ + @GuiWidgetElement( + id = "runConfigurationName", + order = "02", + label = "i18n::VariableResolverEditor.label.RunConfigurationName", + type = GuiElementType.METADATA, + typeMetadata = PipelineRunConfigurationTypeMetadata.class, + parentId = VariableResolver.GUI_PLUGIN_ELEMENT_PARENT_ID) + @HopMetadataProperty + private String runConfigurationName; + + /** The name of the variable that will contain the expression in the pipeline. */ + @GuiWidgetElement( + id = "expressionVariableName", + order = "03", + label = "i18n::VariableResolverEditor.label.ExpressionVariableName", + type = GuiElementType.TEXT, + parentId = VariableResolver.GUI_PLUGIN_ELEMENT_PARENT_ID) + @HopMetadataProperty + private String expressionVariableName; + + @Override + public void init() { + // This space was intentionally left blank. + } + + @Override + public void setPluginId() {} + + @Override + public String getPluginId() { + return "Variable-Resolver-Pipeline"; + } + + @Override + public void setPluginName(String pluginName) {} + + @Override + public String getPluginName() { + return "Pipeline Variable Resolver"; + } + + @Override + public String resolve(String expression, IVariables variables) throws HopException { + MultiMetadataProvider metadataProvider = HopMetadataInstance.getMetadataProvider(); + String pipelineFilename = variables.resolve(filename); + if (StringUtils.isEmpty(pipelineFilename)) { + throw new HopException( + "Please provide a pipeline filename to use to resolve the variable expression."); + } + String pipelineRunConfigurationName = variables.resolve(runConfigurationName); + PipelineRunConfiguration defaultRunConfiguration = + PipelineRunConfiguration.findDefault(metadataProvider); + if (defaultRunConfiguration == null && StringUtils.isEmpty(pipelineRunConfigurationName)) { + throw new HopException( + "Please specify a local pipeline run configuration to use to resolve the variable expression."); + } + + String variableName = variables.resolve(expressionVariableName); + if (StringUtils.isEmpty(variableName)) { + throw new HopException( + "Please specify a variable name to use to contain the variable expression."); + } + + PipelineExecutionConfiguration configuration = new PipelineExecutionConfiguration(); + configuration.setLogLevel(LogLevel.BASIC); + configuration.setRunConfiguration(pipelineRunConfigurationName); + + PipelineMeta pipelineMeta = new PipelineMeta(pipelineFilename, metadataProvider, variables); + IPipelineEngine pipeline = + PipelineEngineFactory.createPipelineEngine( + variables, pipelineRunConfigurationName, metadataProvider, pipelineMeta); + pipeline.getPipelineMeta().setInternalHopVariables(pipeline); + pipeline.initializeFrom(null); + pipeline.setVariable(variableName, expression); + pipeline.setLogLevel(configuration.getLogLevel()); + pipeline.setMetadataProvider(metadataProvider); + + // Run the pipeline + // + pipeline.prepareExecution(); + pipeline.startThreads(); + pipeline.waitUntilFinished(); + + // After completion, get the variable value back. + // Use the Set Variables transform to do this. + // + expression = pipeline.getVariable(variableName); + return expression; + } +} diff --git a/plugins/resolvers/pipeline/src/main/resources/org/apache/hop/resolvers/pipeline/messages/messages_en_US.properties b/plugins/resolvers/pipeline/src/main/resources/org/apache/hop/resolvers/pipeline/messages/messages_en_US.properties new file mode 100644 index 00000000000..aab7ae63c61 --- /dev/null +++ b/plugins/resolvers/pipeline/src/main/resources/org/apache/hop/resolvers/pipeline/messages/messages_en_US.properties @@ -0,0 +1,20 @@ +# +# 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. +# +# +VariableResolverEditor.label.Filename = Resolver pipeline filename +VariableResolverEditor.label.RunConfigurationName = The local pipeline run configuration to use +VariableResolverEditor.label.ExpressionVariableName = The variable name to contain the expression diff --git a/plugins/resolvers/pipeline/src/main/resources/version.xml b/plugins/resolvers/pipeline/src/main/resources/version.xml new file mode 100644 index 00000000000..6be576acae9 --- /dev/null +++ b/plugins/resolvers/pipeline/src/main/resources/version.xml @@ -0,0 +1,20 @@ + + + +${project.version} \ No newline at end of file diff --git a/plugins/resolvers/pom.xml b/plugins/resolvers/pom.xml new file mode 100644 index 00000000000..907429fd9c8 --- /dev/null +++ b/plugins/resolvers/pom.xml @@ -0,0 +1,36 @@ + + + + 4.0.0 + + + org.apache.hop + hop-plugins + 2.12.0-SNAPSHOT + + + hop-plugins-resolvers + pom + Hop Plugins Variable Resolvers + + + pipeline + + + diff --git a/rest/src/main/java/org/apache/hop/rest/Hop.java b/rest/src/main/java/org/apache/hop/rest/Hop.java index 5bd2afc0bfe..2f60180837c 100644 --- a/rest/src/main/java/org/apache/hop/rest/Hop.java +++ b/rest/src/main/java/org/apache/hop/rest/Hop.java @@ -43,6 +43,7 @@ import org.apache.hop.core.vfs.HopVfs; import org.apache.hop.metadata.api.IHasHopMetadataProvider; import org.apache.hop.metadata.serializer.multi.MultiMetadataProvider; +import org.apache.hop.metadata.util.HopMetadataInstance; import org.apache.hop.metadata.util.HopMetadataUtil; /** Singleton class serving as the basis for the Web Application */ @@ -127,6 +128,7 @@ private Hop() { // Set up the metadata to use // metadataProvider = HopMetadataUtil.getStandardHopMetadataProvider(variables); + HopMetadataInstance.setMetadataProvider(metadataProvider); // Allow plugins to modify the elements loaded so far, before a pipeline or workflow is even // loaded diff --git a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiCompositeWidgets.java b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiCompositeWidgets.java index af480d8391b..f5fbd8b33e7 100644 --- a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiCompositeWidgets.java +++ b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiCompositeWidgets.java @@ -89,6 +89,10 @@ public void createCompositeWidgets( Composite parent, String parentGuiElementId, Control lastControl) { + if (sourceData == null) { + // Nothing to do here. We can't detect widgets without an object. + return; + } // Find the GUI Elements for the given class... // GuiRegistry registry = GuiRegistry.getInstance(); @@ -668,7 +672,10 @@ private String[] getComboItems(Object sourceObject, String getComboValuesMethod) public void setWidgetsContents( Object sourceData, Composite parentComposite, String parentGuiElementId) { - + if (sourceData == null) { + // We can't determine the widgets without an object. + return; + } GuiRegistry registry = GuiRegistry.getInstance(); GuiElements guiElements = registry.findGuiElements(sourceData.getClass().getName(), parentGuiElementId); diff --git a/ui/src/main/java/org/apache/hop/ui/core/variables/resolver/VariableResolverEditor.java b/ui/src/main/java/org/apache/hop/ui/core/variables/resolver/VariableResolverEditor.java new file mode 100644 index 00000000000..c4f0111976c --- /dev/null +++ b/ui/src/main/java/org/apache/hop/ui/core/variables/resolver/VariableResolverEditor.java @@ -0,0 +1,324 @@ +/* + * 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.apache.hop.ui.core.variables.resolver; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.hop.core.Const; +import org.apache.hop.core.gui.plugin.GuiPlugin; +import org.apache.hop.core.plugins.IPlugin; +import org.apache.hop.core.plugins.PluginRegistry; +import org.apache.hop.core.variables.resolver.IVariableResolver; +import org.apache.hop.core.variables.resolver.VariableResolver; +import org.apache.hop.core.variables.resolver.VariableResolverPluginType; +import org.apache.hop.i18n.BaseMessages; +import org.apache.hop.ui.core.PropsUi; +import org.apache.hop.ui.core.dialog.ErrorDialog; +import org.apache.hop.ui.core.gui.GuiCompositeWidgets; +import org.apache.hop.ui.core.gui.GuiCompositeWidgetsAdapter; +import org.apache.hop.ui.core.metadata.MetadataEditor; +import org.apache.hop.ui.core.metadata.MetadataManager; +import org.apache.hop.ui.hopgui.HopGui; +import org.apache.hop.ui.hopgui.perspective.metadata.MetadataPerspective; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.layout.FormData; +import org.eclipse.swt.layout.FormLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Text; + +@GuiPlugin(description = "This is the editor for variable resolver metadata") + +/** + * The metadata editor for VariableResolver. Don't move this class around as it's synchronized with + * the VariableResolver package to find the dialog. + */ +public class VariableResolverEditor extends MetadataEditor { + private static final Class PKG = VariableResolverEditor.class; + + private Text wName; + private Text wDescription; + private Combo wResolverType; + + private Composite wResolverSpecificComp; + private GuiCompositeWidgets guiCompositeWidgets; + private Map metaMap; + + private final PropsUi props; + + /** + * @param hopGui The hop GUI + * @param manager The metadata + * @param resolver The object to edit + */ + public VariableResolverEditor( + HopGui hopGui, MetadataManager manager, VariableResolver resolver) { + super(hopGui, manager, resolver); + props = PropsUi.getInstance(); + populateMetaMap(); + } + + @Override + public void createControl(Composite parent) { + int middle = props.getMiddlePct(); + int margin = PropsUi.getMargin(); + + Label wIcon = new Label(parent, SWT.RIGHT); + wIcon.setImage(getImage()); + FormData fdlicon = new FormData(); + fdlicon.top = new FormAttachment(0, 0); + fdlicon.right = new FormAttachment(100, 0); + wIcon.setLayoutData(fdlicon); + PropsUi.setLook(wIcon); + + // What's the name + Label wlName = new Label(parent, SWT.RIGHT); + PropsUi.setLook(wlName); + wlName.setText(BaseMessages.getString(PKG, "VariableResolverEditor.label.Name")); + FormData fdlName = new FormData(); + fdlName.top = new FormAttachment(0, 0); + fdlName.left = new FormAttachment(0, 0); + wlName.setLayoutData(fdlName); + wName = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER); + PropsUi.setLook(wName); + FormData fdName = new FormData(); + fdName.top = new FormAttachment(wlName, margin); + fdName.left = new FormAttachment(0, 0); + fdName.right = new FormAttachment(wIcon, -margin); + wName.setLayoutData(fdName); + + // What's the description + Label wlDescription = new Label(parent, SWT.RIGHT); + PropsUi.setLook(wlDescription); + wlDescription.setText(BaseMessages.getString(PKG, "VariableResolverEditor.label.Description")); + FormData fdlDescription = new FormData(); + fdlDescription.top = new FormAttachment(wName, margin); + fdlDescription.left = new FormAttachment(0, 0); + wlDescription.setLayoutData(fdlDescription); + wDescription = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER); + PropsUi.setLook(wDescription); + FormData fdDescription = new FormData(); + fdDescription.top = new FormAttachment(wlDescription, margin); + fdDescription.left = new FormAttachment(0, 0); + fdDescription.right = new FormAttachment(wIcon, -margin); + wDescription.setLayoutData(fdDescription); + + Label spacer = new Label(parent, SWT.HORIZONTAL | SWT.SEPARATOR); + FormData fdSpacer = new FormData(); + fdSpacer.left = new FormAttachment(0, 0); + fdSpacer.top = new FormAttachment(wDescription, 15); + fdSpacer.right = new FormAttachment(100, 0); + spacer.setLayoutData(fdSpacer); + + // What's the type of database access? + // + Label wlResolverType = new Label(parent, SWT.RIGHT); + PropsUi.setLook(wlResolverType); + wlResolverType.setText( + BaseMessages.getString(PKG, "VariableResolverEditor.label.ResolverType")); + FormData fdlConnectionType = new FormData(); + fdlConnectionType.top = new FormAttachment(spacer, margin); + fdlConnectionType.left = new FormAttachment(0, 0); // First one in the left top corner + fdlConnectionType.right = new FormAttachment(middle, -margin); + wlResolverType.setLayoutData(fdlConnectionType); + wResolverType = new Combo(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER); + wResolverType.setItems(getResolverTypes()); + PropsUi.setLook(wResolverType); + FormData fdConnectionType = new FormData(); + fdConnectionType.top = new FormAttachment(wlResolverType, 0, SWT.CENTER); + fdConnectionType.left = new FormAttachment(middle, 0); // To the right of the label + fdConnectionType.right = new FormAttachment(100, 0); + wResolverType.setLayoutData(fdConnectionType); + Control lastControl = wResolverType; + + // Add a composite area + // + wResolverSpecificComp = new Composite(parent, SWT.BACKGROUND); + wResolverSpecificComp.setLayout(new FormLayout()); + FormData fdResolverSpecificComp = new FormData(); + fdResolverSpecificComp.left = new FormAttachment(0, 0); + fdResolverSpecificComp.right = new FormAttachment(100, 0); + fdResolverSpecificComp.top = new FormAttachment(lastControl, margin); + fdResolverSpecificComp.bottom = new FormAttachment(100, 0); + wResolverSpecificComp.setLayoutData(fdResolverSpecificComp); + PropsUi.setLook(wResolverSpecificComp); + // lastControl = wResolverSpecificComp; + + // Now add the database plugin specific widgets + // + guiCompositeWidgets = new GuiCompositeWidgets(manager.getVariables()); + guiCompositeWidgets.createCompositeWidgets( + getMetadata().getIResolver(), + null, + wResolverSpecificComp, + VariableResolver.GUI_PLUGIN_ELEMENT_PARENT_ID, + null); + + // Add listener to detect change + guiCompositeWidgets.setWidgetsListener( + new GuiCompositeWidgetsAdapter() { + @Override + public void widgetModified( + GuiCompositeWidgets compositeWidgets, Control changedWidget, String widgetId) { + setChanged(); + } + }); + + setWidgetsContent(); + + // Some widget set changed + resetChanged(); + + // Add listener to detect change after loading data + Listener modifyListener = + event -> { + setChanged(); + MetadataPerspective.getInstance().updateEditor(this); + }; + wName.addListener(SWT.Modify, modifyListener); + wResolverType.addListener(SWT.Modify, modifyListener); + wResolverType.addListener(SWT.Modify, event -> changeResolverType()); + } + + private AtomicBoolean busyChangingConnectionType = new AtomicBoolean(false); + + private void changeResolverType() { + if (busyChangingConnectionType.get()) { + return; + } + busyChangingConnectionType.set(true); + + // Now change the data type + // + String newTypeName = wResolverType.getText(); + wResolverType.setText(newTypeName); + + IVariableResolver iResolver = metaMap.get(newTypeName); + getMetadata().setIResolver(iResolver); + + // Remove existing children + // + for (Control child : wResolverSpecificComp.getChildren()) { + child.dispose(); + } + + // Re-add the widgets + // + guiCompositeWidgets = new GuiCompositeWidgets(manager.getVariables()); + guiCompositeWidgets.createCompositeWidgets( + iResolver, + null, + wResolverSpecificComp, + VariableResolver.GUI_PLUGIN_ELEMENT_PARENT_ID, + null); + guiCompositeWidgets.setWidgetsListener( + new GuiCompositeWidgetsAdapter() { + @Override + public void widgetModified( + GuiCompositeWidgets compositeWidgets, Control changedWidget, String widgetId) { + setChanged(); + } + }); + + // Put the data back + // + setWidgetsContent(); + + getShell().layout(true, true); + + busyChangingConnectionType.set(false); + } + + private void enableFields() { + // Perhaps later + } + + @Override + public void setWidgetsContent() { + VariableResolver resolver = this.getMetadata(); + wName.setText(Const.NVL(resolver.getName(), "")); + wDescription.setText(Const.NVL(resolver.getDescription(), "")); + wResolverType.setText(Const.NVL(resolver.getPluginName(), "")); + + guiCompositeWidgets.setWidgetsContents( + resolver.getIResolver(), + wResolverSpecificComp, + VariableResolver.GUI_PLUGIN_ELEMENT_PARENT_ID); + + enableFields(); + } + + @Override + public void getWidgetsContent(VariableResolver meta) { + + meta.setName(wName.getText()); + meta.setDescription(wDescription.getText()); + + // TODO meta.setResolverType(wResolverType.getText()); + + // Get the resolver specific information + // + guiCompositeWidgets.getWidgetsContents( + meta.getIResolver(), VariableResolver.GUI_PLUGIN_ELEMENT_PARENT_ID); + } + + private String[] getResolverTypes() { + PluginRegistry registry = PluginRegistry.getInstance(); + List plugins = registry.getPlugins(VariableResolverPluginType.class); + String[] types = new String[plugins.size()]; + for (int i = 0; i < types.length; i++) { + types[i] = plugins.get(i).getName(); + } + Arrays.sort(types, String.CASE_INSENSITIVE_ORDER); + return types; + } + + @Override + public boolean setFocus() { + if (wName == null || wName.isDisposed()) { + return false; + } + return wName.setFocus(); + } + + private Map populateMetaMap() { + metaMap = new HashMap<>(); + List plugins = + PluginRegistry.getInstance().getPlugins(VariableResolverPluginType.class); + for (IPlugin plugin : plugins) { + try { + IVariableResolver resolver = + (IVariableResolver) PluginRegistry.getInstance().loadClass(plugin); + + metaMap.put(plugin.getName(), resolver); + } catch (Exception e) { + new ErrorDialog(getShell(), "Error", "Error instantiating variable resolver metadata", e); + } + } + + return metaMap; + } +} diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java b/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java index 7266580d3d0..b47b95516ae 100644 --- a/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java +++ b/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java @@ -69,6 +69,7 @@ import org.apache.hop.i18n.LanguageChoice; import org.apache.hop.metadata.api.IHasHopMetadataProvider; import org.apache.hop.metadata.serializer.multi.MultiMetadataProvider; +import org.apache.hop.metadata.util.HopMetadataInstance; import org.apache.hop.metadata.util.HopMetadataUtil; import org.apache.hop.partition.PartitionSchema; import org.apache.hop.server.HopServerMeta; @@ -285,6 +286,7 @@ private HopGui(Display display) { // TODO: create metadata plugin system // metadataProvider = HopMetadataUtil.getStandardHopMetadataProvider(variables); + HopMetadataInstance.setMetadataProvider(metadataProvider); eventsHandler = new HopGuiEventsHandler(); diff --git a/ui/src/main/resources/org/apache/hop/ui/core/variables/resolver/messages/messages_en_US.properties b/ui/src/main/resources/org/apache/hop/ui/core/variables/resolver/messages/messages_en_US.properties new file mode 100644 index 00000000000..b6d937948dc --- /dev/null +++ b/ui/src/main/resources/org/apache/hop/ui/core/variables/resolver/messages/messages_en_US.properties @@ -0,0 +1,21 @@ +# +# 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. +# +# + +VariableResolverEditor.label.Name = Name +VariableResolverEditor.label.Description = Description +VariableResolverEditor.label.ResolverType = The type of variable resolver to use: \ No newline at end of file